9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-23 17:09:19 +00:00

优化熔炉性能,保证线程安全

This commit is contained in:
XiaoMoMi
2025-06-07 19:49:47 +08:00
parent b0e1305427
commit 26edc960a2
16 changed files with 276 additions and 285 deletions

View File

@@ -160,7 +160,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
for (Map.Entry<Integer, List<String>> entry : this.clientBoundTags.entrySet()) { for (Map.Entry<Integer, List<String>> entry : this.clientBoundTags.entrySet()) {
list.add(new TagUtils.TagEntry(entry.getKey(), entry.getValue())); list.add(new TagUtils.TagEntry(entry.getKey(), entry.getValue()));
} }
Object packet = TagUtils.createUpdateTagsPacket(Map.of(MRegistries.instance$Registries$BLOCK, list)); Object packet = TagUtils.createUpdateTagsPacket(Map.of(MRegistries.BLOCK, list));
for (Player player : Bukkit.getOnlinePlayers()) { for (Player player : Bukkit.getOnlinePlayers()) {
this.plugin.networkManager().sendPacket(this.plugin.adapt(player), packet); this.plugin.networkManager().sendPacket(this.plugin.adapt(player), packet);
} }
@@ -762,7 +762,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
private Object createBlockProperties(Key realBlockKey) throws Exception { private Object createBlockProperties(Key realBlockKey) throws Exception {
Object blockProperties = CoreReflections.method$BlockBehaviour$Properties$of.invoke(null); Object blockProperties = CoreReflections.method$BlockBehaviour$Properties$of.invoke(null);
Object realBlockResourceLocation = createResourceLocation(realBlockKey); Object realBlockResourceLocation = createResourceLocation(realBlockKey);
Object realBlockResourceKey = CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.instance$Registries$BLOCK, realBlockResourceLocation); Object realBlockResourceKey = CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.BLOCK, realBlockResourceLocation);
if (CoreReflections.field$BlockBehaviour$Properties$id != null) { if (CoreReflections.field$BlockBehaviour$Properties$id != null) {
CoreReflections.field$BlockBehaviour$Properties$id.set(blockProperties, realBlockResourceKey); CoreReflections.field$BlockBehaviour$Properties$id.set(blockProperties, realBlockResourceKey);
} }

View File

@@ -151,7 +151,7 @@ public class BukkitCustomBlock extends AbstractCustomBlock {
Object holder = BukkitCraftEngine.instance().blockManager().getMinecraftBlockHolder(state.customBlockState().registryId()); Object holder = BukkitCraftEngine.instance().blockManager().getMinecraftBlockHolder(state.customBlockState().registryId());
Set<Object> tags = new HashSet<>(); Set<Object> tags = new HashSet<>();
for (Key tag : settings.tags()) { for (Key tag : settings.tags()) {
tags.add(CoreReflections.method$TagKey$create.invoke(null, MRegistries.instance$Registries$BLOCK, KeyUtils.toResourceLocation(tag))); tags.add(CoreReflections.method$TagKey$create.invoke(null, MRegistries.BLOCK, KeyUtils.toResourceLocation(tag)));
} }
CoreReflections.field$Holder$Reference$tags.set(holder, tags); CoreReflections.field$Holder$Reference$tags.set(holder, tags);
// set burning properties // set burning properties

View File

@@ -77,7 +77,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior {
} }
private void generateTree(Object world, Object blockPos, Object blockState, Object randomSource) throws Exception { private void generateTree(Object world, Object blockPos, Object blockState, Object randomSource) throws Exception {
Object registry = CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.instance$Registries$CONFIGURED_FEATURE); Object registry = CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.CONFIGURED_FEATURE);
if (registry == null) return; if (registry == null) return;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Optional<Object> holder = (Optional<Object>) CoreReflections.method$Registry$getHolder1.invoke(registry, FeatureUtils.createFeatureKey(treeFeature())); Optional<Object> holder = (Optional<Object>) CoreReflections.method$Registry$getHolder1.invoke(registry, FeatureUtils.createFeatureKey(treeFeature()));

View File

@@ -214,7 +214,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
.orElseGet(() -> ((WritableRegistry<Key>) BuiltInRegistries.OPTIMIZED_ITEM_ID) .orElseGet(() -> ((WritableRegistry<Key>) BuiltInRegistries.OPTIMIZED_ITEM_ID)
.register(new ResourceKey<>(BuiltInRegistries.OPTIMIZED_ITEM_ID.key().location(), id), id)); .register(new ResourceKey<>(BuiltInRegistries.OPTIMIZED_ITEM_ID.key().location(), id), id));
Object resourceLocation = KeyUtils.toResourceLocation(id.namespace(), id.value()); Object resourceLocation = KeyUtils.toResourceLocation(id.namespace(), id.value());
Object mcHolder = ((Optional<Object>) CoreReflections.method$Registry$getHolder1.invoke(MBuiltInRegistries.ITEM, CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.instance$Registries$ITEM, resourceLocation))).get(); Object mcHolder = ((Optional<Object>) CoreReflections.method$Registry$getHolder1.invoke(MBuiltInRegistries.ITEM, CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.ITEM, resourceLocation))).get();
Set<Object> tags = (Set<Object>) CoreReflections.field$Holder$Reference$tags.get(mcHolder); Set<Object> tags = (Set<Object>) CoreReflections.field$Holder$Reference$tags.get(mcHolder);
for (Object tag : tags) { for (Object tag : tags) {
Key tagId = Key.of(CoreReflections.field$TagKey$location.get(tag).toString()); Key tagId = Key.of(CoreReflections.field$TagKey$location.get(tag).toString());

View File

@@ -10,6 +10,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; 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.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.bukkit.util.MaterialUtils; import net.momirealms.craftengine.bukkit.util.MaterialUtils;
import net.momirealms.craftengine.bukkit.util.RecipeUtils; import net.momirealms.craftengine.bukkit.util.RecipeUtils;
@@ -45,9 +46,9 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
// 将自定义配方转为“广义”配方,接受更加宽容的输入 // 将自定义配方转为“广义”配方,接受更加宽容的输入
// 部分过程借助bukkit完成部分直接通过nms方法注册 // 部分过程借助bukkit完成部分直接通过nms方法注册
private static final Map<Key, BukkitRecipeConvertor<? extends Recipe<ItemStack>>> MIXED_RECIPE_CONVERTORS = new HashMap<>(); private static final Map<Key, BukkitRecipeConvertor<? extends Recipe<ItemStack>>> MIXED_RECIPE_CONVERTORS = new HashMap<>();
private static Object nmsRecipeManager;
private static final List<Object> injectedIngredients = new ArrayList<>(); private static final List<Object> injectedIngredients = new ArrayList<>();
private static final IdentityHashMap<Recipe<ItemStack>, Object> recipeToMcRecipeHolder = new IdentityHashMap<>(); private static final IdentityHashMap<Recipe<ItemStack>, Object> CE_RECIPE_2_NMS_HOLDER = new IdentityHashMap<>();
private static Object nmsRecipeManager;
private static void registerNMSSmithingRecipe(Object recipe) { private static void registerNMSSmithingRecipe(Object recipe) {
try { try {
@@ -265,7 +266,8 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
} }
public Object nmsRecipeHolderByRecipe(Recipe<ItemStack> recipe) { public Object nmsRecipeHolderByRecipe(Recipe<ItemStack> recipe) {
return recipeToMcRecipeHolder.get(recipe); if (super.isReloading) return null;
return CE_RECIPE_2_NMS_HOLDER.get(recipe);
} }
public static Object nmsRecipeManager() { public static Object nmsRecipeManager() {
@@ -287,6 +289,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
@Override @Override
public void load() { public void load() {
if (!Config.enableRecipeSystem()) return; if (!Config.enableRecipeSystem()) return;
super.isReloading = true;
if (VersionHelper.isOrAbove1_21_2()) { if (VersionHelper.isOrAbove1_21_2()) {
try { try {
this.stolenFeatureFlagSet = CoreReflections.field$RecipeManager$featureflagset.get(nmsRecipeManager); this.stolenFeatureFlagSet = CoreReflections.field$RecipeManager$featureflagset.get(nmsRecipeManager);
@@ -308,7 +311,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to unregister recipes", e); this.plugin.logger().warn("Failed to unregister recipes", e);
} }
recipeToMcRecipeHolder.clear();
} }
@Override @Override
@@ -320,6 +322,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
@Override @Override
public void disable() { public void disable() {
unload(); unload();
CE_RECIPE_2_NMS_HOLDER.clear();
HandlerList.unregisterAll(this.recipeEventListener); HandlerList.unregisterAll(this.recipeEventListener);
if (this.crafterEventListener != null) { if (this.crafterEventListener != null) {
HandlerList.unregisterAll(this.crafterEventListener); HandlerList.unregisterAll(this.crafterEventListener);
@@ -473,6 +476,15 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
// clear cache // clear cache
injectedIngredients.clear(); injectedIngredients.clear();
CE_RECIPE_2_NMS_HOLDER.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));
}
super.isReloading = false;
} catch (Exception e) { } catch (Exception e) {
this.plugin.logger().warn("Failed to run delayed recipe tasks", e); this.plugin.logger().warn("Failed to run delayed recipe tasks", e);
} }
@@ -691,7 +703,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
return optionalItem.map(itemStackCustomItem -> MaterialUtils.getMaterial(itemStackCustomItem.material())).orElse(null); return optionalItem.map(itemStackCustomItem -> MaterialUtils.getMaterial(itemStackCustomItem.material())).orElse(null);
} }
private static List<Object> getIngredientLooks(List<Holder<Key>> holders) throws ReflectiveOperationException { private static List<Object> getIngredientLooks(List<Holder<Key>> holders) {
List<Object> itemStacks = new ArrayList<>(); List<Object> itemStacks = new ArrayList<>();
for (Holder<Key> holder : holders) { for (Holder<Key> holder : holders) {
ItemStack itemStack = BukkitItemManager.instance().getBuildableItem(holder.value()).get().buildItemStack(ItemBuildContext.EMPTY, 1); ItemStack itemStack = BukkitItemManager.instance().getBuildableItem(holder.value()).get().buildItemStack(ItemBuildContext.EMPTY, 1);
@@ -710,8 +722,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
.map(Optional::get) .map(Optional::get)
.toList(); .toList();
Object shapedRecipe = getNMSRecipe(id); Object shapedRecipe = getOptionalNMSRecipe(id).get();
recipeToMcRecipeHolder.put(recipe, shapedRecipe);
if (VersionHelper.isOrAbove1_20_2()) { if (VersionHelper.isOrAbove1_20_2()) {
shapedRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapedRecipe); shapedRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapedRecipe);
} }
@@ -731,8 +742,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
try { try {
List<Ingredient<ItemStack>> actualIngredients = recipe.ingredientsInUse(); List<Ingredient<ItemStack>> actualIngredients = recipe.ingredientsInUse();
Object shapelessRecipe = getNMSRecipe(id); Object shapelessRecipe = getOptionalNMSRecipe(id).get();
recipeToMcRecipeHolder.put(recipe, shapelessRecipe);
if (VersionHelper.isOrAbove1_20_2()) { if (VersionHelper.isOrAbove1_20_2()) {
shapelessRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapelessRecipe); shapelessRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapelessRecipe);
} }
@@ -751,8 +761,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
private static void injectCookingRecipe(Key id, CustomCookingRecipe<ItemStack> recipe) { private static void injectCookingRecipe(Key id, CustomCookingRecipe<ItemStack> recipe) {
try { try {
Ingredient<ItemStack> actualIngredient = recipe.ingredient(); Ingredient<ItemStack> actualIngredient = recipe.ingredient();
Object smeltingRecipe = getNMSRecipe(id); Object smeltingRecipe = getOptionalNMSRecipe(id).get();
recipeToMcRecipeHolder.put(recipe, smeltingRecipe);
if (VersionHelper.isOrAbove1_20_2()) { if (VersionHelper.isOrAbove1_20_2()) {
smeltingRecipe = CoreReflections.field$RecipeHolder$recipe.get(smeltingRecipe); smeltingRecipe = CoreReflections.field$RecipeHolder$recipe.get(smeltingRecipe);
} }
@@ -771,23 +780,17 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
// 获取nms配方请注意1.20.1获取配方本身而1.20.2+获取的是配方的holder // 获取nms配方请注意1.20.1获取配方本身而1.20.2+获取的是配方的holder
// recipe on 1.20.1 and holder on 1.20.2+ // recipe on 1.20.1 and holder on 1.20.2+
private static Object getNMSRecipe(Key id) throws ReflectiveOperationException { private static Optional<Object> getOptionalNMSRecipe(Key id) throws ReflectiveOperationException {
if (VersionHelper.isOrAbove1_21_2()) { if (VersionHelper.isOrAbove1_21_2()) {
Object resourceKey = CraftBukkitReflections.method$CraftRecipe$toMinecraft.invoke(null, new NamespacedKey(id.namespace(), id.value())); Object resourceKey = FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(id));
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Optional<Object> optional = (Optional<Object>) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceKey); Optional<Object> optional = (Optional<Object>) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceKey);
if (optional.isEmpty()) { return optional;
throw new IllegalArgumentException("Recipe " + id + " not found");
}
return optional.get();
} else { } else {
Object resourceLocation = KeyUtils.toResourceLocation(id); Object resourceLocation = KeyUtils.toResourceLocation(id);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Optional<Object> optional = (Optional<Object>) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceLocation); Optional<Object> optional = (Optional<Object>) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceLocation);
if (optional.isEmpty()) { return optional;
throw new IllegalArgumentException("Recipe " + id + " not found");
}
return optional.get();
} }
} }

View File

@@ -843,7 +843,7 @@ public class RecipeEventListener implements Listener {
try { try {
player = (Player) CraftBukkitReflections.method$InventoryView$getPlayer.invoke(event.getView()); player = (Player) CraftBukkitReflections.method$InventoryView$getPlayer.invoke(event.getView());
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to get inventory viewer", e); this.plugin.logger().warn("Failed to get inventory viewer", e);
return; return;
} }
@@ -854,14 +854,18 @@ public class RecipeEventListener implements Listener {
if (ceRecipe != null) { if (ceRecipe != null) {
inventory.setResult(ceRecipe.result(new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); inventory.setResult(ceRecipe.result(new ItemBuildContext(serverPlayer, ContextHolder.EMPTY)));
serverPlayer.setLastUsedRecipe(ceRecipe.id()); serverPlayer.setLastUsedRecipe(ceRecipe.id());
correctCraftingRecipeUsed(inventory, ceRecipe); if (!ceRecipe.id().equals(recipeId)) {
correctCraftingRecipeUsed(inventory, ceRecipe);
}
return; return;
} }
ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPED, input, lastRecipe); ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPED, input, lastRecipe);
if (ceRecipe != null) { if (ceRecipe != null) {
inventory.setResult(ceRecipe.result(new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); inventory.setResult(ceRecipe.result(new ItemBuildContext(serverPlayer, ContextHolder.EMPTY)));
serverPlayer.setLastUsedRecipe(ceRecipe.id()); serverPlayer.setLastUsedRecipe(ceRecipe.id());
correctCraftingRecipeUsed(inventory, ceRecipe); if (!ceRecipe.id().equals(recipeId)) {
correctCraftingRecipeUsed(inventory, ceRecipe);
}
return; return;
} }
// clear result if not met // clear result if not met
@@ -869,9 +873,8 @@ public class RecipeEventListener implements Listener {
} }
private void correctCraftingRecipeUsed(CraftingInventory inventory, Recipe<ItemStack> recipe) { private void correctCraftingRecipeUsed(CraftingInventory inventory, Recipe<ItemStack> recipe) {
Object holderOrRecipe = recipeManager.nmsRecipeHolderByRecipe(recipe); Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe);
if (holderOrRecipe == null) { if (holderOrRecipe == null) {
// it's a vanilla recipe but not injected
return; return;
} }
try { try {
@@ -922,20 +925,21 @@ public class RecipeEventListener implements Listener {
CustomSmithingTransformRecipe<ItemStack> transformRecipe = (CustomSmithingTransformRecipe<ItemStack>) ceRecipe; CustomSmithingTransformRecipe<ItemStack> transformRecipe = (CustomSmithingTransformRecipe<ItemStack>) ceRecipe;
ItemStack processed = transformRecipe.assemble(new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY), this.itemManager.wrap(base)); ItemStack processed = transformRecipe.assemble(new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY), this.itemManager.wrap(base));
event.setResult(processed); event.setResult(processed);
correctSmithingRecipeUsed(inventory, ceRecipe); if (!ceRecipe.id().equals(recipeId)) {
correctSmithingRecipeUsed(inventory, ceRecipe);
}
} }
private void correctSmithingRecipeUsed(SmithingInventory inventory, Recipe<ItemStack> recipe) { private void correctSmithingRecipeUsed(SmithingInventory inventory, Recipe<ItemStack> recipe) {
Object holderOrRecipe = recipeManager.nmsRecipeHolderByRecipe(recipe); Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe);
if (holderOrRecipe == null) { if (holderOrRecipe == null) {
// it's a vanilla recipe but not injected
return; return;
} }
try { try {
Object resultInventory = CraftBukkitReflections.field$CraftResultInventory$resultInventory.get(inventory); Object resultInventory = CraftBukkitReflections.field$CraftResultInventory$resultInventory.get(inventory);
CoreReflections.field$ResultContainer$recipeUsed.set(resultInventory, holderOrRecipe); CoreReflections.field$ResultContainer$recipeUsed.set(resultInventory, holderOrRecipe);
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to correct used recipe", e); this.plugin.logger().warn("Failed to correct used recipe", e);
} }
} }

View File

@@ -8,6 +8,10 @@ public interface InjectedCacheCheck {
void recipeType(Object recipeType); void recipeType(Object recipeType);
Key customRecipeType();
void customRecipeType(Key customRecipeType);
Object lastRecipe(); Object lastRecipe();
void lastRecipe(Object lastRecipe); void lastRecipe(Object lastRecipe);

View File

@@ -16,6 +16,8 @@ import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; 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.MRecipeTypes;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.recipe.CustomCookingRecipe; import net.momirealms.craftengine.core.item.recipe.CustomCookingRecipe;
import net.momirealms.craftengine.core.item.recipe.OptimizedIDItem; import net.momirealms.craftengine.core.item.recipe.OptimizedIDItem;
@@ -28,7 +30,6 @@ import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.Optional; import java.util.Optional;
public class RecipeInjector { public class RecipeInjector {
@@ -41,17 +42,23 @@ public class RecipeInjector {
.name("net.momirealms.craftengine.bukkit.entity.InjectedCacheChecker") .name("net.momirealms.craftengine.bukkit.entity.InjectedCacheChecker")
.implement(CoreReflections.clazz$RecipeManager$CachedCheck) .implement(CoreReflections.clazz$RecipeManager$CachedCheck)
.implement(InjectedCacheCheck.class) .implement(InjectedCacheCheck.class)
.defineField("recipeType", Object.class, Visibility.PUBLIC) .defineField("recipeType", Object.class, Visibility.PUBLIC)
.method(ElementMatchers.named("recipeType")) .method(ElementMatchers.named("recipeType"))
.intercept(FieldAccessor.ofField("recipeType")) .intercept(FieldAccessor.ofField("recipeType"))
.defineField("customRecipeType", Key.class, Visibility.PUBLIC)
.method(ElementMatchers.named("customRecipeType"))
.intercept(FieldAccessor.ofField("customRecipeType"))
.defineField("lastRecipe", Object.class, Visibility.PUBLIC) .defineField("lastRecipe", Object.class, Visibility.PUBLIC)
.method(ElementMatchers.named("lastRecipe")) .method(ElementMatchers.named("lastRecipe"))
.intercept(FieldAccessor.ofField("lastRecipe")) .intercept(FieldAccessor.ofField("lastRecipe"))
.method(ElementMatchers.named("setLastRecipe"))
.intercept(FieldAccessor.ofField("lastRecipe"))
.defineField("lastCustomRecipe", Key.class, Visibility.PUBLIC) .defineField("lastCustomRecipe", Key.class, Visibility.PUBLIC)
.method(ElementMatchers.named("lastCustomRecipe")) .method(ElementMatchers.named("lastCustomRecipe"))
.intercept(FieldAccessor.ofField("lastCustomRecipe")) .intercept(FieldAccessor.ofField("lastCustomRecipe"))
.method(ElementMatchers.named("getRecipeFor").or(ElementMatchers.named("a"))) .method(ElementMatchers.named("getRecipeFor").or(ElementMatchers.named("a")))
.intercept(MethodDelegation.to( .intercept(MethodDelegation.to(
VersionHelper.isOrAbove1_21_2() ? VersionHelper.isOrAbove1_21_2() ?
@@ -73,269 +80,227 @@ public class RecipeInjector {
if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected
Object recipeType = FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$recipeType(entity); Object recipeType = FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$recipeType(entity);
InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker); InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker);
injectedChecker.recipeType(recipeType); if (recipeType == MRecipeTypes.SMELTING) {
injectedChecker.customRecipeType(RecipeTypes.SMELTING);
injectedChecker.recipeType(MRecipeTypes.SMELTING);
} else if (recipeType == MRecipeTypes.BLASTING) {
injectedChecker.customRecipeType(RecipeTypes.BLASTING);
injectedChecker.recipeType(MRecipeTypes.BLASTING);
} else if (recipeType == MRecipeTypes.SMOKING) {
injectedChecker.customRecipeType(RecipeTypes.SMOKING);
injectedChecker.recipeType(MRecipeTypes.SMOKING);
} else {
throw new IllegalStateException("RecipeType " + recipeType + " not supported");
}
CoreReflections.field$AbstractFurnaceBlockEntity$quickCheck.set(entity, injectedChecker); CoreReflections.field$AbstractFurnaceBlockEntity$quickCheck.set(entity, injectedChecker);
} else if (!VersionHelper.isOrAbove1_21_2() && CoreReflections.clazz$CampfireBlockEntity.isInstance(entity)) { } else if (!VersionHelper.isOrAbove1_21_2() && CoreReflections.clazz$CampfireBlockEntity.isInstance(entity)) {
Object quickCheck = CoreReflections.field$CampfireBlockEntity$quickCheck.get(entity); Object quickCheck = CoreReflections.field$CampfireBlockEntity$quickCheck.get(entity);
if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected
InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker); InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker);
injectedChecker.customRecipeType(RecipeTypes.CAMPFIRE_COOKING);
injectedChecker.recipeType(MRecipeTypes.CAMPFIRE_COOKING); injectedChecker.recipeType(MRecipeTypes.CAMPFIRE_COOKING);
CoreReflections.field$CampfireBlockEntity$quickCheck.set(entity, injectedChecker); CoreReflections.field$CampfireBlockEntity$quickCheck.set(entity, injectedChecker);
} }
} }
@SuppressWarnings("DuplicatedCode")
public static class GetRecipeForMethodInterceptor1_20 { public static class GetRecipeForMethodInterceptor1_20 {
public static final GetRecipeForMethodInterceptor1_20 INSTANCE = new GetRecipeForMethodInterceptor1_20(); public static final GetRecipeForMethodInterceptor1_20 INSTANCE = new GetRecipeForMethodInterceptor1_20();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@RuntimeType @RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { public Object intercept(@This Object thisObj, @AllArguments Object[] args) {
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
Object type = injectedCacheCheck.recipeType(); Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe();
Object lastRecipe = 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(mcRecipeManager, type, args[0], args[1], lastRecipe); if (optionalRecipe.isEmpty()) {
if (optionalRecipe.isPresent()) {
Pair<Object, Object> pair = optionalRecipe.get();
Object resourceLocation = pair.getFirst();
Key recipeId = Key.of(resourceLocation.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
ItemStack itemStack;
List<Object> items;
if (type == MRecipeTypes.CAMPFIRE_COOKING) {
items = (List<Object>) CoreReflections.field$SimpleContainer$items.get(args[0]);
} else {
items = (List<Object>) CoreReflections.field$AbstractFurnaceBlockEntity$items.get(args[0]);
}
itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(items.get(0));
// it's a recipe from other plugins
boolean isCustom = recipeManager.isCustomRecipe(recipeId);
if (!isCustom) {
injectedCacheCheck.lastRecipe(resourceLocation);
return Optional.of(pair.getSecond());
}
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.CAMPFIRE_COOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe);
} else {
return Optional.empty();
}
if (ceRecipe == null) {
return Optional.empty();
}
// Cache recipes, it might be incorrect on reloading
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
// It doesn't matter at all
injectedCacheCheck.lastRecipe(resourceLocation);
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(pair.getSecond()));
} else {
return Optional.empty(); return Optional.empty();
} }
Pair<Object, Object> resourceLocationAndRecipe = optionalRecipe.get();
Object rawRecipeResourceLocation = resourceLocationAndRecipe.getFirst();
Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey);
if (!isCustom) {
injectedCacheCheck.lastRecipe(rawRecipeResourceLocation);
return Optional.of(resourceLocationAndRecipe.getSecond());
}
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(
injectedCacheCheck.recipeType() == MRecipeTypes.CAMPFIRE_COOKING ?
FastNMS.INSTANCE.field$SimpleContainer$items(args[0]).getFirst() :
FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$getItem(args[0], 0)
);
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe());
if (ceRecipe == null) {
return Optional.empty();
}
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
if (!ceRecipe.id().equals(rawRecipeKey)) {
injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id()));
}
return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(resourceLocationAndRecipe.getSecond());
} }
} }
@SuppressWarnings("DuplicatedCode")
public static class GetRecipeForMethodInterceptor1_20_5 { public static class GetRecipeForMethodInterceptor1_20_5 {
public static final GetRecipeForMethodInterceptor1_20_5 INSTANCE = new GetRecipeForMethodInterceptor1_20_5(); public static final GetRecipeForMethodInterceptor1_20_5 INSTANCE = new GetRecipeForMethodInterceptor1_20_5();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@RuntimeType @RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { public Object intercept(@This Object thisObj, @AllArguments Object[] args) {
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
Object type = injectedCacheCheck.recipeType(); Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe();
Object lastRecipe = 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(mcRecipeManager, type, args[0], args[1], lastRecipe); if (optionalRecipe.isEmpty()) {
if (optionalRecipe.isPresent()) {
Object holder = optionalRecipe.get();
Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder);
Key recipeId = Key.of(id.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
ItemStack itemStack;
List<Object> items;
if (type == MRecipeTypes.CAMPFIRE_COOKING) {
items = (List<Object>) CoreReflections.field$SimpleContainer$items.get(args[0]);
} else {
items = (List<Object>) CoreReflections.field$AbstractFurnaceBlockEntity$items.get(args[0]);
}
itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(items.get(0));
// it's a recipe from other plugins
boolean isCustom = recipeManager.isCustomRecipe(recipeId);
if (!isCustom) {
injectedCacheCheck.lastRecipe(id);
return optionalRecipe;
}
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.CAMPFIRE_COOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe);
} else {
return Optional.empty();
}
if (ceRecipe == null) {
return Optional.empty();
}
// Cache recipes, it might be incorrect on reloading
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
// It doesn't matter at all
injectedCacheCheck.lastRecipe(id);
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder));
} else {
return Optional.empty(); return Optional.empty();
} }
Object rawRecipeHolder = optionalRecipe.get();
Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$RecipeHolder$id(rawRecipeHolder);
Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(
injectedCacheCheck.recipeType() == MRecipeTypes.CAMPFIRE_COOKING ?
FastNMS.INSTANCE.field$SimpleContainer$items(args[0]).getFirst() :
FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$getItem(args[0], 0)
);
boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey);
if (!isCustom) {
injectedCacheCheck.lastRecipe(rawRecipeResourceLocation);
return optionalRecipe;
}
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe());
if (ceRecipe == null) {
return Optional.empty();
}
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
if (!ceRecipe.id().equals(rawRecipeKey)) {
injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id()));
}
return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe));
} }
} }
@SuppressWarnings("DuplicatedCode")
public static class GetRecipeForMethodInterceptor1_21 { public static class GetRecipeForMethodInterceptor1_21 {
public static final GetRecipeForMethodInterceptor1_21 INSTANCE = new GetRecipeForMethodInterceptor1_21(); public static final GetRecipeForMethodInterceptor1_21 INSTANCE = new GetRecipeForMethodInterceptor1_21();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@RuntimeType @RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { public Object intercept(@This Object thisObj, @AllArguments Object[] args) {
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
Object type = injectedCacheCheck.recipeType(); Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe();
Object lastRecipe = 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(mcRecipeManager, type, args[0], args[1], lastRecipe); if (optionalRecipe.isEmpty()) {
if (optionalRecipe.isPresent()) {
Object holder = optionalRecipe.get();
Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder);
Key recipeId = Key.of(id.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(CoreReflections.field$SingleRecipeInput$item.get(args[0]));
// it's a recipe from other plugins
boolean isCustom = recipeManager.isCustomRecipe(recipeId);
if (!isCustom) {
injectedCacheCheck.lastRecipe(id);
return optionalRecipe;
}
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.CAMPFIRE_COOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe);
} else {
return Optional.empty();
}
if (ceRecipe == null) {
return Optional.empty();
}
// Cache recipes, it might be incorrect on reloading
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
// It doesn't matter at all
injectedCacheCheck.lastRecipe(id);
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder));
} else {
return Optional.empty(); return Optional.empty();
} }
Object rawRecipeHolder = optionalRecipe.get();
Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$RecipeHolder$id(rawRecipeHolder);
Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey);
if (!isCustom) {
injectedCacheCheck.lastRecipe(rawRecipeResourceLocation);
return optionalRecipe;
}
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.field$SingleRecipeInput$item(args[0]));
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe());
if (ceRecipe == null) {
return Optional.empty();
}
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
if (!ceRecipe.id().equals(rawRecipeKey)) {
injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id()));
}
return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe));
} }
} }
@SuppressWarnings("DuplicatedCode")
public static class GetRecipeForMethodInterceptor1_21_2 { public static class GetRecipeForMethodInterceptor1_21_2 {
public static final GetRecipeForMethodInterceptor1_21_2 INSTANCE = new GetRecipeForMethodInterceptor1_21_2(); public static final GetRecipeForMethodInterceptor1_21_2 INSTANCE = new GetRecipeForMethodInterceptor1_21_2();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@RuntimeType @RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { public Object intercept(@This Object thisObj, @AllArguments Object[] args) {
Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager();
InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj;
Object type = injectedCacheCheck.recipeType(); Object lastRecipeResourceKey = injectedCacheCheck.lastRecipe();
Object lastRecipe = 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(mcRecipeManager, type, args[0], args[1], lastRecipe); if (optionalRecipe.isEmpty()) {
if (optionalRecipe.isPresent()) {
Object holder = optionalRecipe.get();
Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder);
Object resourceLocation = FastNMS.INSTANCE.field$ResourceKey$location(id);
Key recipeId = Key.of(resourceLocation.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(CoreReflections.field$SingleRecipeInput$item.get(args[0]));
// it's a recipe from other plugins
boolean isCustom = recipeManager.isCustomRecipe(recipeId);
if (!isCustom) {
injectedCacheCheck.lastRecipe(id);
return optionalRecipe;
}
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe();
if (type == MRecipeTypes.SMELTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.BLASTING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == MRecipeTypes.SMOKING) {
ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else {
return Optional.empty();
}
if (ceRecipe == null) {
return Optional.empty();
}
// Cache recipes, it might be incorrect on reloading
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
// It doesn't matter at all
injectedCacheCheck.lastRecipe(id);
return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder));
} else {
return Optional.empty(); return Optional.empty();
} }
// 获取配方的基础信息
Object recipeHolder = optionalRecipe.get();
Object rawRecipeResourceKey = FastNMS.INSTANCE.field$RecipeHolder$id(recipeHolder);
Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$ResourceKey$location(rawRecipeResourceKey);
Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString());
BukkitRecipeManager recipeManager = BukkitRecipeManager.instance();
// 来自其他插件注册的自定义配方
boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey);
if (!isCustom) {
injectedCacheCheck.lastRecipe(rawRecipeResourceKey);
return optionalRecipe;
}
// 获取唯一内存地址id
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.field$SingleRecipeInput$item(args[0]));
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
if (idHolder.isEmpty()) {
return Optional.empty();
}
SingleItemInput<ItemStack> input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
CustomCookingRecipe<ItemStack> ceRecipe = (CustomCookingRecipe<ItemStack>) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe());
// 这个ce配方并不存在那么应该返回空
if (ceRecipe == null) {
return Optional.empty();
}
// 记录上一次使用的配方(ce)
injectedCacheCheck.lastCustomRecipe(ceRecipe.id());
// 更新上一次使用的配方(nms)
if (!ceRecipe.id().equals(rawRecipeKey)) {
injectedCacheCheck.lastRecipe(FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(ceRecipe.id())));
}
return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe));
} }
} }
} }

View File

@@ -10,21 +10,23 @@ import java.lang.reflect.Type;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
public final class MRegistries { public final class MRegistries {
public static final Object instance$Registries$BLOCK; public static final Object BLOCK;
public static final Object instance$Registries$ITEM; public static final Object ITEM;
public static final Object instance$Registries$ATTRIBUTE; public static final Object ATTRIBUTE;
public static final Object instance$Registries$BIOME; public static final Object BIOME;
public static final Object instance$Registries$MOB_EFFECT; public static final Object MOB_EFFECT;
public static final Object instance$Registries$SOUND_EVENT; public static final Object SOUND_EVENT;
public static final Object instance$Registries$PARTICLE_TYPE; public static final Object PARTICLE_TYPE;
public static final Object instance$Registries$ENTITY_TYPE; public static final Object ENTITY_TYPE;
public static final Object instance$Registries$FLUID; public static final Object FLUID;
public static final Object instance$Registries$RECIPE_TYPE; public static final Object RECIPE_TYPE;
public static final Object instance$Registries$DIMENSION_TYPE; public static final Object DIMENSION_TYPE;
public static final Object instance$Registries$CONFIGURED_FEATURE; public static final Object CONFIGURED_FEATURE;
public static final Object instance$Registries$PLACED_FEATURE; public static final Object PLACED_FEATURE;
@Nullable // 1.21+ @Nullable // 1.21+
public static final Object instance$Registries$JUKEBOX_SONG; public static final Object JUKEBOX_SONG;
@Nullable // 1.21+
public static final Object RECIPE;
static { static {
Field[] fields = CoreReflections.clazz$Registries.getDeclaredFields(); Field[] fields = CoreReflections.clazz$Registries.getDeclaredFields();
@@ -43,6 +45,7 @@ public final class MRegistries {
Object registries$ConfiguredFeature = null; Object registries$ConfiguredFeature = null;
Object registries$PlacedFeature = null; Object registries$PlacedFeature = null;
Object registries$JukeboxSong = null; Object registries$JukeboxSong = null;
Object registries$Recipe = null;
for (Field field : fields) { for (Field field : fields) {
Type fieldType = field.getGenericType(); Type fieldType = field.getGenericType();
if (fieldType instanceof ParameterizedType paramType) { if (fieldType instanceof ParameterizedType paramType) {
@@ -60,6 +63,8 @@ public final class MRegistries {
registries$RecipeType = field.get(null); registries$RecipeType = field.get(null);
} else if (rawType == CoreReflections.clazz$ConfiguredFeature) { } else if (rawType == CoreReflections.clazz$ConfiguredFeature) {
registries$ConfiguredFeature = field.get(null); registries$ConfiguredFeature = field.get(null);
} else if (rawType == CoreReflections.clazz$Recipe) {
registries$Recipe = field.get(null);
} }
} else { } else {
if (type == CoreReflections.clazz$Block) { if (type == CoreReflections.clazz$Block) {
@@ -88,20 +93,21 @@ public final class MRegistries {
} }
} }
} }
instance$Registries$BLOCK = requireNonNull(registries$Block); BLOCK = requireNonNull(registries$Block);
instance$Registries$ITEM = requireNonNull(registries$Item); ITEM = requireNonNull(registries$Item);
instance$Registries$ATTRIBUTE = requireNonNull(registries$Attribute); ATTRIBUTE = requireNonNull(registries$Attribute);
instance$Registries$BIOME = requireNonNull(registries$Biome); BIOME = requireNonNull(registries$Biome);
instance$Registries$MOB_EFFECT = requireNonNull(registries$MobEffect); MOB_EFFECT = requireNonNull(registries$MobEffect);
instance$Registries$SOUND_EVENT = requireNonNull(registries$SoundEvent); SOUND_EVENT = requireNonNull(registries$SoundEvent);
instance$Registries$DIMENSION_TYPE = requireNonNull(registries$DimensionType); DIMENSION_TYPE = requireNonNull(registries$DimensionType);
instance$Registries$PARTICLE_TYPE = requireNonNull(registries$ParticleType); PARTICLE_TYPE = requireNonNull(registries$ParticleType);
instance$Registries$ENTITY_TYPE = requireNonNull(registries$EntityType); ENTITY_TYPE = requireNonNull(registries$EntityType);
instance$Registries$FLUID = requireNonNull(registries$Fluid); FLUID = requireNonNull(registries$Fluid);
instance$Registries$RECIPE_TYPE = requireNonNull(registries$RecipeType); RECIPE_TYPE = requireNonNull(registries$RecipeType);
instance$Registries$CONFIGURED_FEATURE = requireNonNull(registries$ConfiguredFeature); CONFIGURED_FEATURE = requireNonNull(registries$ConfiguredFeature);
instance$Registries$PLACED_FEATURE = requireNonNull(registries$PlacedFeature); PLACED_FEATURE = requireNonNull(registries$PlacedFeature);
instance$Registries$JUKEBOX_SONG = registries$JukeboxSong; JUKEBOX_SONG = registries$JukeboxSong;
RECIPE = registries$Recipe;
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -26,7 +26,7 @@ public class BukkitSoundManager extends AbstractSoundManager {
protected void registerSongs(Map<Key, JukeboxSong> songs) { protected void registerSongs(Map<Key, JukeboxSong> songs) {
if (songs.isEmpty()) return; if (songs.isEmpty()) return;
try { try {
Object registry = CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.instance$Registries$JUKEBOX_SONG);; Object registry = CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.JUKEBOX_SONG);;
unfreezeRegistry(registry); unfreezeRegistry(registry);
for (Map.Entry<Key, JukeboxSong> entry : songs.entrySet()) { for (Map.Entry<Key, JukeboxSong> entry : songs.entrySet()) {
Key id = entry.getKey(); Key id = entry.getKey();

View File

@@ -16,7 +16,7 @@ public class BlockTags {
Object value = CACHE.get(key); Object value = CACHE.get(key);
if (value == null) { if (value == null) {
try { try {
value = CoreReflections.method$TagKey$create.invoke(null, MRegistries.instance$Registries$BLOCK, KeyUtils.toResourceLocation(key)); value = CoreReflections.method$TagKey$create.invoke(null, MRegistries.BLOCK, KeyUtils.toResourceLocation(key));
CACHE.put(key, value); CACHE.put(key, value);
return value; return value;
} catch (Exception e) { } catch (Exception e) {

View File

@@ -10,7 +10,7 @@ public class FeatureUtils {
public static Object createFeatureKey(Key id) { public static Object createFeatureKey(Key id) {
try { try {
return CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.instance$Registries$CONFIGURED_FEATURE, KeyUtils.toResourceLocation(id)); return CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.CONFIGURED_FEATURE, KeyUtils.toResourceLocation(id));
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -19,7 +19,7 @@ public class ItemTags {
Object value = CACHE.get(key); Object value = CACHE.get(key);
if (value == null) { if (value == null) {
try { try {
value = CoreReflections.method$TagKey$create.invoke(null, MRegistries.instance$Registries$ITEM, KeyUtils.toResourceLocation(key)); value = CoreReflections.method$TagKey$create.invoke(null, MRegistries.ITEM, KeyUtils.toResourceLocation(key));
CACHE.put(key, value); CACHE.put(key, value);
return value; return value;
} catch (Exception e) { } catch (Exception e) {

View File

@@ -19,7 +19,7 @@ public class RegistryUtils {
public static int currentBiomeRegistrySize() { public static int currentBiomeRegistrySize() {
try { try {
Object idMap = CoreReflections.method$Registry$asHolderIdMap.invoke(CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.instance$Registries$BIOME)); Object idMap = CoreReflections.method$Registry$asHolderIdMap.invoke(CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.BIOME));
return (int) CoreReflections.method$IdMap$size.invoke(idMap); return (int) CoreReflections.method$IdMap$size.invoke(idMap);
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);

View File

@@ -28,6 +28,7 @@ public abstract class AbstractRecipeManager<T> implements RecipeManager<T> {
protected final Set<Key> dataPackRecipes = new HashSet<>(); protected final Set<Key> dataPackRecipes = new HashSet<>();
protected final Set<Key> customRecipes = new HashSet<>(); protected final Set<Key> customRecipes = new HashSet<>();
private final RecipeParser recipeParser; private final RecipeParser recipeParser;
protected boolean isReloading;
public AbstractRecipeManager() { public AbstractRecipeManager() {
this.recipeReader = initVanillaRecipeReader(); this.recipeReader = initVanillaRecipeReader();
@@ -72,37 +73,44 @@ public abstract class AbstractRecipeManager<T> implements RecipeManager<T> {
@Override @Override
public boolean isDataPackRecipe(Key key) { public boolean isDataPackRecipe(Key key) {
if (this.isReloading) return false;
return this.dataPackRecipes.contains(key); return this.dataPackRecipes.contains(key);
} }
@Override @Override
public boolean isCustomRecipe(Key key) { public boolean isCustomRecipe(Key key) {
if (this.isReloading) return false;
return this.byId.containsKey(key); return this.byId.containsKey(key);
} }
@Override @Override
public Optional<Recipe<T>> recipeById(Key key) { public Optional<Recipe<T>> recipeById(Key key) {
if (this.isReloading) return Optional.empty();
return Optional.ofNullable(this.byId.get(key)); return Optional.ofNullable(this.byId.get(key));
} }
@Override @Override
public List<Recipe<T>> recipesByType(Key type) { public List<Recipe<T>> recipesByType(Key type) {
if (this.isReloading) return List.of();
return this.byType.getOrDefault(type, List.of()); return this.byType.getOrDefault(type, List.of());
} }
@Override @Override
public List<Recipe<T>> recipeByResult(Key result) { public List<Recipe<T>> recipeByResult(Key result) {
if (this.isReloading) return List.of();
return this.byResult.getOrDefault(result, List.of()); return this.byResult.getOrDefault(result, List.of());
} }
@Override @Override
public List<Recipe<T>> recipeByIngredient(Key ingredient) { public List<Recipe<T>> recipeByIngredient(Key ingredient) {
if (this.isReloading) return List.of();
return this.byIngredient.getOrDefault(ingredient, List.of()); return this.byIngredient.getOrDefault(ingredient, List.of());
} }
@Nullable @Nullable
@Override @Override
public Recipe<T> recipeByInput(Key type, RecipeInput input) { public Recipe<T> recipeByInput(Key type, RecipeInput input) {
if (this.isReloading) return null;
List<Recipe<T>> recipes = this.byType.get(type); List<Recipe<T>> recipes = this.byType.get(type);
if (recipes == null) return null; if (recipes == null) return null;
for (Recipe<T> recipe : recipes) { for (Recipe<T> recipe : recipes) {
@@ -116,8 +124,9 @@ public abstract class AbstractRecipeManager<T> implements RecipeManager<T> {
@Nullable @Nullable
@Override @Override
public Recipe<T> recipeByInput(Key type, RecipeInput input, Key lastRecipe) { public Recipe<T> recipeByInput(Key type, RecipeInput input, Key lastRecipe) {
if (this.isReloading) return null;
if (lastRecipe != null) { if (lastRecipe != null) {
Recipe<T> last = byId.get(lastRecipe); Recipe<T> last = this.byId.get(lastRecipe);
if (last != null && last.matches(input)) { if (last != null && last.matches(input)) {
return last; return last;
} }

View File

@@ -51,7 +51,7 @@ byte_buddy_version=1.17.5
ahocorasick_version=0.6.3 ahocorasick_version=0.6.3
snake_yaml_version=2.4 snake_yaml_version=2.4
anti_grief_version=0.17 anti_grief_version=0.17
nms_helper_version=0.66.12 nms_helper_version=0.66.15
evalex_version=3.5.0 evalex_version=3.5.0
reactive_streams_version=1.0.4 reactive_streams_version=1.0.4
amazon_awssdk_version=2.31.23 amazon_awssdk_version=2.31.23