Skip to content

Commit

Permalink
Merge pull request #33 from sweetrpg/bugfix/processing-loop
Browse files Browse the repository at this point in the history
Fix recipe cost calculation
  • Loading branch information
paulyhedral authored Dec 1, 2024
2 parents 57c8623 + 69ee9e8 commit 4035448
Show file tree
Hide file tree
Showing 4 changed files with 290 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -200,16 +200,16 @@ private static void handleToggleShoppingList() {
}

private static void handleAddToQueue() {
CraftTracker.LOGGER.debug("#handleToggleCraftList");
CraftTracker.LOGGER.debug("#handleAddToQueue");

CTPlugin.jeiRuntime.getIngredientListOverlay().getIngredientUnderMouse()
.ifPresent(ingredient -> {
CraftTracker.LOGGER.debug("AddToQueuePacket#handle: type {}", ingredient.getType());
CraftTracker.LOGGER.debug("AddToQueuePacket#handle: ingredient {}", ingredient.getIngredient());
CraftTracker.LOGGER.debug("#handleAddToQueue: type {}", ingredient.getType());
CraftTracker.LOGGER.debug("#handleAddToQueue: ingredient {}", ingredient.getIngredient());

if(ingredient.getIngredient() instanceof ItemStack itemStack) {
ResourceLocation res = itemStack.getItem().getRegistryName();
CraftTracker.LOGGER.debug("AddToQueuePacket#handle: res {}", res);
CraftTracker.LOGGER.debug("#handleAddToQueue: res {}", res);

var player = Minecraft.getInstance().player;
CraftingQueueManager.INSTANCE.addProduct(player, res, 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
import com.sweetrpg.crafttracker.CraftTracker;
import com.sweetrpg.crafttracker.common.model.CraftingQueueProduct;
import com.sweetrpg.crafttracker.common.storage.CraftingQueueStorage;
import com.sweetrpg.crafttracker.common.util.InventoryUtil;
import com.sweetrpg.crafttracker.common.util.RecipeUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraftforge.fml.loading.FMLPaths;
import net.minecraftforge.registries.ForgeRegistries;
import org.apache.commons.lang3.ObjectUtils;

import java.io.IOException;
import java.io.InputStream;
Expand All @@ -29,6 +30,8 @@ public class CraftingQueueManager {

public static CraftingQueueManager INSTANCE = new CraftingQueueManager();

private static final int MAX_PROCESSING_LEVEL = 2;

public static final Path STORAGE_DIR = FMLPaths.GAMEDIR.get().resolve("craft_tracker");

private Map<ResourceLocation, CraftingQueueProduct> endProducts = new HashMap<>();
Expand Down Expand Up @@ -214,25 +217,31 @@ public void removeAll() {
public void computeAll() {
CraftTracker.LOGGER.debug("CraftingQueueManager#computeAll");

this.intermediateProducts.clear();
this.rawMaterials.clear();
this.fuel.clear();
// this.intermediateProducts.clear();
// this.rawMaterials.clear();
// this.fuel.clear();

ProcessingContext ctx = new ProcessingContext();

this.endProducts.forEach((k, v) -> {
this.computeProduct(v);
this.computeProduct(ctx, v);
});

this.intermediateProducts = ctx.intermediateProducts;
this.rawMaterials = ctx.rawMaterials;
this.fuel = ctx.fuel;
}

public void computeProduct(CraftingQueueProduct product) {
void computeProduct(ProcessingContext ctx, CraftingQueueProduct product) {
CraftTracker.LOGGER.debug("CraftingQueueManager#computeProduct: {}", product);

var index = Math.min(product.getIndex(), product.getRecipes().size());
var recipe = product.getRecipes().get(index);

this.computeRecipe(recipe, product.getQuantity());
this.computeRecipe(ctx, recipe, product.getQuantity());
}

public void computeRecipe(Recipe recipe, int recipeQuantity) {
void computeRecipe(ProcessingContext ctx, Recipe<?> recipe, int recipeQuantity) {
CraftTracker.LOGGER.debug("CraftingQueueManager#computeRecipe: {}", recipe);

var ingredients = recipe.getIngredients();
Expand All @@ -244,85 +253,189 @@ public void computeRecipe(Recipe recipe, int recipeQuantity) {
CraftTracker.LOGGER.debug("id: {}", id);
if(this.intermediateProducts.containsKey(id)) {
CraftTracker.LOGGER.debug("intermediates has this ingredient already: {}", id);
var quantity = this.intermediateProducts.remove(id);
var item = ForgeRegistries.ITEMS.getValue(id);
this.intermediateProducts.remove(id);
this.updateRawMaterials(id, new ItemStack(item, quantity), recipeQuantity);
var quantity = ctx.intermediateProducts.remove(id);
// var item = ForgeRegistries.ITEMS.getValue(id);
ctx.intermediateProducts.remove(id);
this.updateRawMaterials(ctx, id, quantity * recipeQuantity);

return;
}
}

ingredients.stream()
.filter((i) -> i instanceof Ingredient)
.map((i) -> Ingredient.class.cast(i))
.forEach((i) -> {
CraftTracker.LOGGER.debug("i: {}", i);

if(i instanceof Ingredient ingredient) {
CraftTracker.LOGGER.debug("ingredient: {}", ingredient);

Arrays.stream(ingredient.getItems())
.findFirst()
.ifPresent(item -> {
CraftTracker.LOGGER.debug("item: {}", item);
var id = item.getItem().getRegistryName();
CraftTracker.LOGGER.debug("id: {}", id);
var subRecipes = RecipeUtil.getRecipesFor(id);
CraftTracker.LOGGER.debug("subRecipes: {}", subRecipes);

if(subRecipes.isEmpty()) {
CraftTracker.LOGGER.debug("subRecipes is empty; raw material");
// no recipes for this ingredient, so it's a raw material
this.updateRawMaterials(id, item, recipeQuantity);
}
else {
CraftTracker.LOGGER.debug("subRecipes has {} items; intermediate", subRecipes.size());

// check if player already has the item
var inventory = Minecraft.getInstance().player.getInventory();
if(inventory.contains(item)) {
inventory.items.stream()
.filter(inv -> inv.getItem().getRegistryName().equals(id))
.map(inv -> inv.getCount())
.findFirst()
.ifPresent(count -> {
this.intermediateProducts.compute(id, (itemId, quantity) -> {
Integer finalCount = (quantity == null ? 0 : quantity) + item.getCount() - count;
if(finalCount < 1) {
return null;
}

return finalCount;
});
});
}
else {
this.intermediateProducts.compute(id, (itemId, quantity) -> {
if(quantity == null) {
return item.getCount();
}

return quantity + item.getCount();
});
}

int subIndex = RecipeUtil.chooseLeastExpensiveOf(subRecipes);
this.computeRecipe(subRecipes.get(subIndex), recipeQuantity);
}
});
}
});
}
// assemble ingredients
// Map<ResourceLocation, Tuple<Ingredient, Integer>> ingredientCounts = new HashMap<>();
// for(Ingredient ingredient : ingredients) {
// CraftTracker.LOGGER.debug("ingredient: {}", ingredient);
//
// if(ingredient.isEmpty()) {
// CraftTracker.LOGGER.debug("no ingredients for: {}", ingredient);
// continue;
// }
//
//// var info = new IngredientInfo();
//// info.ingredient = ingredient;
////
//// var ingredientRecipes = Arrays.stream(ingredient.getItems())
//// .map(itemStack -> itemStack.getItem())
//// .flatMap(item -> RecipeUtil.getRecipesFor(item.getRegistryName()).stream())
//// .toList();
//// CraftTracker.LOGGER.debug("ingredientRecipes: {}", ingredientRecipes);
//// info.recipeIndex = RecipeUtil.chooseLeastExpensiveOf(ingredientRecipes);
//// info.recipe = ingredientRecipes.get(info.recipeIndex);
//// info.amount = recipe.getResultItem().getCount();
//
// Arrays.stream(ingredient.getItems())
// .findFirst()
// .map(stack -> stack.getItem())
// .ifPresent(item -> {
// ingredientCounts.compute(item.getRegistryName(), (itemId, tuple) -> {
// if(tuple == null) {
// return new Tuple<>(ingredient, 1);
// }
//
// return new Tuple<>(ingredient, tuple.getB() + 1);
// });
// });
//
//// ingredientInfos.add(info);
// }
// CraftTracker.LOGGER.debug("ingredientCounts: {}", ingredientCounts);

// process ingredients
for(Ingredient ingredient : ingredients) {
// ingredientCounts.forEach((ingredientId, tuple) -> {
// Ingredient ingredient = tuple.getA(); // info.ingredient;
// int amountRequired = tuple.getB(); // info.amount;

if(ingredient.isEmpty() || ingredient.getItems().length == 0) {
continue;
}

private void updateRawMaterials(ResourceLocation id, ItemStack item, int recipeQuantity) {
this.rawMaterials.compute(id, (itemId, quantity) -> {
if(quantity == null) {
return item.getCount() * recipeQuantity;
int itemIndex = RecipeUtil.chooseLeastExpensiveOf(ingredient.getItems());
Item item = ingredient.getItems()[itemIndex].getItem();
int amountRequired = 1; // TODO?

// ctx.handledItems.add(info.recipe.getResultItem().getItem().getRegistryName());
ctx.handledItems.add(item.getRegistryName());

// Arrays.stream(ingredient.getItems())
// .findFirst()
// .ifPresent(item -> {
CraftTracker.LOGGER.debug("item: {}", item);
var id = item.getRegistryName();
CraftTracker.LOGGER.debug("id: {}", id);
var subRecipes = RecipeUtil.getRecipesFor(id);
CraftTracker.LOGGER.debug("subRecipes: {}", subRecipes);

// check if player already has the item
var player = Minecraft.getInstance().player;
var hasInInventory = InventoryUtil.getQuantityOf(player, item.getRegistryName());
var needsQty = (amountRequired * recipeQuantity) - hasInInventory;

if(needsQty < 1) {
CraftTracker.LOGGER.debug("player already has enough of item {} ({} >= {})", id, hasInInventory, amountRequired);
return;
}

return quantity + (item.getCount() * recipeQuantity);
});
if(subRecipes.isEmpty() || ctx.processingLevel >= MAX_PROCESSING_LEVEL) {
CraftTracker.LOGGER.debug("subRecipes is empty; raw material");
// no recipes for this ingredient, so it's a raw material
this.updateRawMaterials(ctx, id, needsQty);
}
else {
CraftTracker.LOGGER.debug("subRecipes has {} items; intermediate", subRecipes.size());

int subIndex = RecipeUtil.chooseLeastExpensiveOf(subRecipes);
CraftTracker.LOGGER.debug("least expensive item index: {}", subIndex);
var chosenSubRecipe = subRecipes.get(subIndex);

// var amountProduced = chosenSubRecipe.getResultItem().getCount();

ctx.intermediateProducts.compute(id,
(itemId, quantity) ->
ObjectUtils.defaultIfNull(quantity, 0) + needsQty);

ctx.processingLevel++;
this.computeRecipe(ctx, chosenSubRecipe, recipeQuantity);
ctx.processingLevel--;
}
// });
}

// for(Ingredient ingredient : ingredients) {
// CraftTracker.LOGGER.debug("ingredient: {}", ingredient);
//
// Arrays.stream(ingredient.getItems())
// .findFirst()
// .ifPresent(item -> {
// CraftTracker.LOGGER.debug("item: {}", item);
// var id = item.getItem().getRegistryName();
// CraftTracker.LOGGER.debug("id: {}", id);
// var subRecipes = RecipeUtil.getRecipesFor(id);
// CraftTracker.LOGGER.debug("subRecipes: {}", subRecipes);
//
// if(subRecipes.isEmpty()) {
// CraftTracker.LOGGER.debug("subRecipes is empty; raw material");
// // no recipes for this ingredient, so it's a raw material
// this.updateRawMaterials(id, item, recipeQuantity);
// }
// else {
// CraftTracker.LOGGER.debug("subRecipes has {} items; intermediate", subRecipes.size());
//
// // check if player already has the item
// var player = Minecraft.getInstance().player;
// var hasInInventory = InventoryUtil.getQuantityOf(player, item.getItem().getRegistryName());
// var needsToMake = item.getCount() - hasInInventory;
//// var inventory = .getInventory();
//// if(inventory.contains(item)) {
//// CraftTracker.LOGGER.debug("player has item in inventory: {}", item);
//// inventory.items.stream()
//// .filter(inv -> inv.getItem().getRegistryName().equals(id))
//// .map(inv -> inv.getCount())
//// .findFirst()
//// .ifPresent(count -> {
//// CraftTracker.LOGGER.debug("adjusting count of item {} to: {}", id, count);
////
//// this.intermediateProducts.compute(id, (itemId, quantity) -> {
//// Integer finalCount = (quantity == null ? 0 : quantity) + item.getCount() - count;
//// if(finalCount < 1) {
//// CraftTracker.LOGGER.debug("final count less than 1, removing: {}", id);
//// return null;
//// }
////
//// processedItems.add(itemId);
////
//// CraftTracker.LOGGER.debug("adjusting count of intermediate item {} to: {}", itemId, finalCount);
//// return finalCount;
//// });
//// });
//// }
//// else {
//// CraftTracker.LOGGER.debug("player DOES NOT have item in inventory: {}", item);
//
// this.intermediateProducts.compute(id, (itemId, quantity) -> {
// if(quantity == null) {
// return needsToMake;
// }
//
// processedItems.add(itemId);
//
// return quantity + needsToMake;
// });
//// }
//
// int subIndex = RecipeUtil.chooseLeastExpensiveOf(subRecipes);
// CraftTracker.LOGGER.debug("least expensive item index: {}", subIndex);
// this.computeRecipe(ctx, subRecipes.get(subIndex), recipeQuantity, processedItems);
// }
// });
// }
}

private void updateRawMaterials(ProcessingContext ctx, ResourceLocation id, int amountNeeded) {
ctx.rawMaterials.compute(id,
(itemId, quantity) ->
ObjectUtils.defaultIfNull(quantity, 0) + amountNeeded);
}

public static class ProductItem {
Expand Down Expand Up @@ -374,4 +487,19 @@ public void setQuantity(int quantity) {
this.quantity = quantity;
}
}

class ProcessingContext {
Map<ResourceLocation, Integer> intermediateProducts = new HashMap<>();
Map<ResourceLocation, Integer> rawMaterials = new HashMap<>();
Map<ResourceLocation, Integer> fuel = new HashMap<>();
Set<ResourceLocation> handledItems = new HashSet<>();
int processingLevel;
}

class IngredientInfo {
Ingredient ingredient;
Recipe<?> recipe;
int recipeIndex;
int amount;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.sweetrpg.crafttracker.common.model;

import net.minecraft.resources.ResourceLocation;

import java.util.Map;

public class ProductRequirement {
public ResourceLocation productId;
public ResourceLocation recipeId;
public Map<ResourceLocation, Integer> intermediateRecipes;
public Map<ResourceLocation, Integer> materials;

public ProductRequirement(ResourceLocation productId, ResourceLocation recipeId, Map<ResourceLocation, Integer> intermediateRecipes, Map<ResourceLocation, Integer> materials) {
this.productId = productId;
this.recipeId = recipeId;
this.intermediateRecipes = intermediateRecipes;
this.materials = materials;
}

}
Loading

0 comments on commit 4035448

Please sign in to comment.