9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2026-01-06 15:52:03 +00:00

Optimize Recipes

This commit is contained in:
XiaoMoMi
2025-02-15 04:35:02 +08:00
parent a20a008f65
commit d657a481f5
19 changed files with 108 additions and 83 deletions

View File

@@ -24,7 +24,7 @@ import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.util.HexaFunction;
import net.momirealms.craftengine.core.util.HeptaFunction;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.PentaFunction;
import net.momirealms.craftengine.core.util.VersionHelper;
@@ -190,9 +190,6 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
private final VanillaRecipeReader recipeReader;
private final List<NamespacedKey> injectedDataPackRecipes;
private final List<NamespacedKey> registeredCustomRecipes;
// [internal:xxx] + [custom:custom]
// includes injected vanilla recipes and custom recipes
private final Set<Key> customRecipes;
// data pack recipe resource locations [minecraft:xxx]
private final Set<Key> dataPackRecipes;
@@ -206,7 +203,6 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
this.injectedDataPackRecipes = new ArrayList<>();
this.registeredCustomRecipes = new ArrayList<>();
this.dataPackRecipes = new HashSet<>();
this.customRecipes = new HashSet<>();
this.recipeEventListener = new RecipeEventListener(plugin, this, plugin.itemManager());
if (VersionHelper.isVersionNewerThan1_21()) {
this.crafterEventListener = new CrafterEventListener(plugin, this, plugin.itemManager());
@@ -239,7 +235,7 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
@Override
public boolean isCustomRecipe(Key key) {
return this.customRecipes.contains(key);
return this.byId.containsKey(key);
}
@Override
@@ -279,7 +275,6 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
this.recipes.clear();
this.byId.clear();
this.dataPackRecipes.clear();
this.customRecipes.clear();
try {
// do not unregister them
@@ -314,17 +309,17 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
@Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
if (!ConfigManager.enableRecipeSystem()) return;
if (this.customRecipes.contains(id)) {
if (this.byId.containsKey(id)) {
this.plugin.logger().warn(path, "Duplicated recipe " + id);
return;
}
Recipe<ItemStack> recipe = RecipeTypes.fromMap(section);
Recipe<ItemStack> recipe = RecipeTypes.fromMap(id, section);
NamespacedKey key = NamespacedKey.fromString(id.toString());
BUKKIT_RECIPE_REGISTER.get(recipe.type()).accept(key, recipe);
try {
this.registeredCustomRecipes.add(key);
this.customRecipes.add(id);
this.recipes.computeIfAbsent(recipe.type(), k -> new ArrayList<>()).add(recipe);
this.byId.put(id, recipe);
} catch (Exception e) {
plugin.logger().warn("Failed to add custom recipe " + id, e);
}
@@ -337,8 +332,8 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
// example: stone button
public void addVanillaInternalRecipe(Key id, Recipe<ItemStack> recipe) {
this.customRecipes.add(id);
this.recipes.computeIfAbsent(recipe.type(), k -> new ArrayList<>()).add(recipe);
this.byId.put(id, recipe);
}
@Nullable
@@ -357,20 +352,13 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
@Nullable
@Override
public Recipe<ItemStack> getRecipe(Key type, RecipeInput input, Key lastRecipe) {
List<Recipe<ItemStack>> recipes = this.recipes.get(type);
if (recipes == null) return null;
if (lastRecipe != null) {
Recipe<ItemStack> last = byId.get(lastRecipe);
if (last != null && last.matches(input)) {
return last;
}
for (Recipe<ItemStack> recipe : recipes) {
if (recipe.matches(input)) {
return recipe;
}
}
}
return null;
return getRecipe(type, input);
}
@Override
@@ -524,6 +512,7 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
}
CustomShapelessRecipe<ItemStack> ceRecipe = new CustomShapelessRecipe<>(
id,
recipe.category(),
recipe.group(),
ingredientList,
@@ -581,6 +570,7 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
}
CustomShapedRecipe<ItemStack> ceRecipe = new CustomShapedRecipe<>(
id,
recipe.category(),
recipe.group(),
new CustomShapedRecipe.Pattern<>(recipe.pattern(), ingredients),
@@ -604,7 +594,7 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
private void handleDataPackCookingRecipe(Key id,
VanillaCookingRecipe recipe,
PentaFunction<NamespacedKey, ItemStack, RecipeChoice, Float, Integer, org.bukkit.inventory.CookingRecipe<?>> constructor1,
HexaFunction<CookingRecipeCategory, String, Ingredient<ItemStack>, Integer, Float, CustomRecipeResult<ItemStack>, CookingRecipe<ItemStack>> constructor2,
HeptaFunction<Key, CookingRecipeCategory, String, Ingredient<ItemStack>, Integer, Float, CustomRecipeResult<ItemStack>, CookingRecipe<ItemStack>> constructor2,
Method fromBukkitRecipeMethod,
Consumer<Runnable> callback) {
NamespacedKey key = new NamespacedKey(id.namespace(), id.value());
@@ -638,6 +628,7 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
}
CookingRecipe<ItemStack> ceRecipe = constructor2.apply(
id,
recipe.category(),
recipe.group(),
Ingredient.of(holders),

View File

@@ -158,27 +158,19 @@ public class RecipeEventListener implements Listener {
}
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
// though it might be inaccurate after reloading
// but it's worthy to cache
Recipe<ItemStack> lastRecipe = serverPlayer.lastUsedRecipe();
if (lastRecipe != null && (lastRecipe.type() == RecipeTypes.SHAPELESS || lastRecipe.type() == RecipeTypes.SHAPED )) {
if (lastRecipe.matches(input)) {
inventory.setResult(lastRecipe.getResult(serverPlayer));
return;
}
}
Key lastRecipe = serverPlayer.lastUsedRecipe();
Recipe<ItemStack> ceRecipe = this.recipeManager.getRecipe(RecipeTypes.SHAPELESS, input);
Recipe<ItemStack> ceRecipe = this.recipeManager.getRecipe(RecipeTypes.SHAPELESS, input, lastRecipe);
if (ceRecipe != null) {
inventory.setResult(ceRecipe.getResult(serverPlayer));
serverPlayer.setLastUsedRecipe(ceRecipe);
serverPlayer.setLastUsedRecipe(ceRecipe.id());
correctCraftingRecipeUsed(inventory, ceRecipe);
return;
}
ceRecipe = this.recipeManager.getRecipe(RecipeTypes.SHAPED, input);
ceRecipe = this.recipeManager.getRecipe(RecipeTypes.SHAPED, input, lastRecipe);
if (ceRecipe != null) {
inventory.setResult(ceRecipe.getResult(serverPlayer));
serverPlayer.setLastUsedRecipe(ceRecipe);
serverPlayer.setLastUsedRecipe(ceRecipe.id());
correctCraftingRecipeUsed(inventory, ceRecipe);
return;
}

View File

@@ -79,6 +79,7 @@ public class BukkitInjector {
private static Class<?> clazz$InjectedCacheChecker;
private static Field field$InjectedCacheChecker$recipeType;
private static Field field$InjectedCacheChecker$lastRecipe;
private static Field field$InjectedCacheChecker$lastCustomRecipe;
public static void init() {
try {
@@ -87,6 +88,7 @@ public class BukkitInjector {
.implement(Reflections.clazz$RecipeManager$CachedCheck)
.defineField("recipeType", Reflections.clazz$RecipeType, Visibility.PUBLIC)
.defineField("lastRecipe", Object.class, Visibility.PUBLIC)
.defineField("lastCustomRecipe", Key.class, Visibility.PUBLIC)
.method(ElementMatchers.named("getRecipeFor").or(ElementMatchers.named("a")))
.intercept(MethodDelegation.to(
VersionHelper.isVersionNewerThan1_21_2() ?
@@ -102,6 +104,7 @@ public class BukkitInjector {
.getLoaded();
field$InjectedCacheChecker$recipeType = clazz$InjectedCacheChecker.getDeclaredField("recipeType");
field$InjectedCacheChecker$lastRecipe = clazz$InjectedCacheChecker.getDeclaredField("lastRecipe");
field$InjectedCacheChecker$lastCustomRecipe = clazz$InjectedCacheChecker.getDeclaredField("lastCustomRecipe");
// Paletted Container
clazz$InjectedPalettedContainer = byteBuddy
@@ -336,20 +339,22 @@ public class BukkitInjector {
CookingInput<ItemStack> input = new CookingInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = (Key) field$InjectedCacheChecker$lastCustomRecipe.get(thisObj);
if (type == Reflections.instance$RecipeType$SMELTING) {
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMELTING, input);
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == Reflections.instance$RecipeType$BLASTING) {
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.BLASTING, input);
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == Reflections.instance$RecipeType$SMOKING) {
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMOKING, input);
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else {
return Optional.empty();
}
if (ceRecipe == null) {
return Optional.empty();
}
// Cache recipes, it might be incorrect on reloading
field$InjectedCacheChecker$lastCustomRecipe.set(thisObj, ceRecipe.id());
// It doesn't matter at all
field$InjectedCacheChecker$lastRecipe.set(thisObj, resourceLocation);
return Optional.of(Optional.ofNullable(recipeManager.getRecipeHolderByRecipe(ceRecipe)).orElse(pair.getSecond()));
@@ -392,20 +397,22 @@ public class BukkitInjector {
CookingInput<ItemStack> input = new CookingInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = (Key) field$InjectedCacheChecker$lastCustomRecipe.get(thisObj);
if (type == Reflections.instance$RecipeType$SMELTING) {
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMELTING, input);
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == Reflections.instance$RecipeType$BLASTING) {
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.BLASTING, input);
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == Reflections.instance$RecipeType$SMOKING) {
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMOKING, input);
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else {
return Optional.empty();
}
if (ceRecipe == null) {
return Optional.empty();
}
// Cache recipes, it might be incorrect on reloading
field$InjectedCacheChecker$lastCustomRecipe.set(thisObj, ceRecipe.id());
// It doesn't matter at all
field$InjectedCacheChecker$lastRecipe.set(thisObj, id);
return Optional.of(Optional.ofNullable(recipeManager.getRecipeHolderByRecipe(ceRecipe)).orElse(holder));
@@ -447,20 +454,22 @@ public class BukkitInjector {
CookingInput<ItemStack> input = new CookingInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = (Key) field$InjectedCacheChecker$lastCustomRecipe.get(thisObj);
if (type == Reflections.instance$RecipeType$SMELTING) {
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMELTING, input);
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == Reflections.instance$RecipeType$BLASTING) {
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.BLASTING, input);
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == Reflections.instance$RecipeType$SMOKING) {
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMOKING, input);
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else {
return Optional.empty();
}
if (ceRecipe == null) {
return Optional.empty();
}
// Cache recipes, it might be incorrect on reloading
field$InjectedCacheChecker$lastCustomRecipe.set(thisObj, ceRecipe.id());
// It doesn't matter at all
field$InjectedCacheChecker$lastRecipe.set(thisObj, id);
return Optional.of(Optional.ofNullable(recipeManager.getRecipeHolderByRecipe(ceRecipe)).orElse(holder));
@@ -503,20 +512,22 @@ public class BukkitInjector {
CookingInput<ItemStack> input = new CookingInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack));
net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack> ceRecipe;
Key lastCustomRecipe = (Key) field$InjectedCacheChecker$lastCustomRecipe.get(thisObj);
if (type == Reflections.instance$RecipeType$SMELTING) {
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMELTING, input);
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMELTING, input, lastCustomRecipe);
} else if (type == Reflections.instance$RecipeType$BLASTING) {
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.BLASTING, input);
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.BLASTING, input, lastCustomRecipe);
} else if (type == Reflections.instance$RecipeType$SMOKING) {
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMOKING, input);
ceRecipe = (net.momirealms.craftengine.core.item.recipe.CookingRecipe<ItemStack>) recipeManager.getRecipe(RecipeTypes.SMOKING, input, lastCustomRecipe);
} else {
return Optional.empty();
}
if (ceRecipe == null) {
return Optional.empty();
}
// Cache recipes, it might be incorrect on reloading
field$InjectedCacheChecker$lastCustomRecipe.set(thisObj, ceRecipe.id());
// It doesn't matter at all
field$InjectedCacheChecker$lastRecipe.set(thisObj, id);
return Optional.of(Optional.ofNullable(recipeManager.getRecipeHolderByRecipe(ceRecipe)).orElse(holder));

View File

@@ -10,7 +10,6 @@ import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.recipe.Recipe;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
import net.momirealms.craftengine.core.util.Direction;
@@ -61,7 +60,7 @@ public class BukkitServerPlayer extends Player {
private int resentSoundTick;
private int resentSwingTick;
private Recipe<ItemStack> lastUsedRecipe = null;
private Key lastUsedRecipe = null;
public BukkitServerPlayer(BukkitCraftEngine plugin, Channel channel) {
this.channel = channel;
@@ -536,11 +535,11 @@ public class BukkitServerPlayer extends Player {
return resentSwingTick == gameTicks();
}
public Recipe<ItemStack> lastUsedRecipe() {
public Key lastUsedRecipe() {
return lastUsedRecipe;
}
public void setLastUsedRecipe(Recipe<ItemStack> lastUsedRecipe) {
public void setLastUsedRecipe(Key lastUsedRecipe) {
this.lastUsedRecipe = lastUsedRecipe;
}
}