Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1622,6 +1622,13 @@ public static <T, M> boolean insertIngredientCrafting(IngredientComponent<T, M>
IIngredientMatcher<T, M> matcher = ingredientComponent.getMatcher();
IIngredientComponentStorage<T, M> storage = ingredientComponent.getStorage(capabilityProvider, side);
List<T> instances = ingredients.getInstances(ingredientComponent);
if (simulate) {
// Stack equal instances, as simulated insertions do not take into account each other.
// Otherwise, instances that each fit separately could be accepted while they don't fit together.
Map<IngredientComponent<?, ?>, List<?>> instancesMap = Maps.newIdentityHashMap();
instancesMap.put(ingredientComponent, instances);
instances = compressMixedIngredients(new MixedIngredients(instancesMap)).getInstances(ingredientComponent);
}
List<T> failedInstances = Lists.newArrayList();
boolean ok = true;
for (T instance : instances) {
Expand Down Expand Up @@ -1664,6 +1671,25 @@ public static boolean insertCrafting(Function<IngredientComponent<?, ?>, PartPos
IMixedIngredients ingredients,
INetwork network, int channel,
boolean simulate) {
return insertCrafting(targetGetter, ingredients, getNetworkStorageGetter(network, channel, false), simulate);
}

/**
* Insert the ingredients of all applicable ingredient component types into the target to make it start crafting.
*
* If insertion in non-simulation mode fails,
* ingredients will be inserted into the given fallback storage.
*
* @param targetGetter A function to get the target position.
* @param ingredients The ingredients to insert.
* @param storageFallbackGetter A function to get the storage to insert failed ingredients into.
* @param simulate If insertion should be simulated.
* @return If all instances could be inserted.
*/
public static boolean insertCrafting(Function<IngredientComponent<?, ?>, PartPos> targetGetter,
IMixedIngredients ingredients,
Function<IngredientComponent<?, ?>, IIngredientComponentStorage> storageFallbackGetter,
boolean simulate) {
Map<IngredientComponent<?, ?>, BlockEntity> tileMap = Maps.newIdentityHashMap();

// First, check if we can find valid tiles for all ingredient components
Expand All @@ -1679,10 +1705,10 @@ public static boolean insertCrafting(Function<IngredientComponent<?, ?>, PartPos
// Next, insert the instances into the respective tiles
boolean ok = true;
for (Map.Entry<IngredientComponent<?, ?>, BlockEntity> entry : tileMap.entrySet()) {
IIngredientComponentStorage<?, ?> storageNetwork = simulate ? null : getNetworkStorage(network, channel, entry.getKey(), false);
IIngredientComponentStorage<?, ?> storageFallback = simulate ? null : storageFallbackGetter.apply(entry.getKey());
if (!insertIngredientCrafting((IngredientComponent) entry.getKey(), entry.getValue(),
targetGetter.apply(entry.getKey()).getSide(), ingredients,
storageNetwork, simulate)) {
storageFallback, simulate)) {
ok = false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,28 @@ public void unmarkCraftingJobProcessing(CraftingJob craftingJob) {
}
}

/**
* Undo a single crafting operation that was added via {@link #addCraftingJobProcessingPendingIngredientsEntry}.
* Other operations of the same job, which may still be running in non-blocking mode, are kept.
* If no operations remain, the job is moved back to the pending jobs.
* @param craftingJob The crafting job.
* @param pendingIngredients The exact pending ingredients entry that was added for the operation.
* @return If the operation was still running.
*/
protected boolean removeCraftingJobProcessingPendingIngredientsEntry(CraftingJob craftingJob,
Map<IngredientComponent<?, ?>,
List<IPrototypedIngredient<?, ?>>> pendingIngredients) {
List<Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>>> pendingIngredientsEntries =
this.processingCraftingJobsPendingIngredients.get(craftingJob.getId());
if (pendingIngredientsEntries == null || !pendingIngredientsEntries.removeIf(entry -> entry == pendingIngredients)) {
return false;
}
if (pendingIngredientsEntries.isEmpty()) {
unmarkCraftingJobProcessing(craftingJob);
}
return true;
}

public void addCraftingJobProcessingPendingIngredientsEntry(CraftingJob craftingJob,
Map<IngredientComponent<?, ?>,
List<IPrototypedIngredient<?, ?>>> pendingIngredients) {
Expand Down Expand Up @@ -578,8 +600,9 @@ protected boolean insertCrafting(PartPos target, IMixedIngredients ingredients,
}
}

// Fallback to default crafting insertion
return CraftingHelpers.insertCrafting(targetGetter, ingredients, network, channel, simulate);
// Fallback to default crafting insertion, failed ingredients go back to the job
return CraftingHelpers.insertCrafting(targetGetter, ingredients,
CraftingHelpers.getCraftingJobBufferStorageGetter(craftingJob), simulate);
}

protected void insertLoopNonBlocking(INetwork network, int channel, PartPos targetPos, CraftingJob craftingJob) {
Expand Down Expand Up @@ -609,8 +632,9 @@ protected boolean consumeAndInsertCrafting(boolean blockingMode, INetwork networ
this.pendingCraftingJobs.remove(startingCraftingJob.getId());

// Update state with expected outputs
addCraftingJobProcessingPendingIngredientsEntry(startingCraftingJob,
CraftingHelpers.getRecipeOutputs(startingCraftingJob.getRecipe()));
Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>> pendingIngredients =
CraftingHelpers.getRecipeOutputs(startingCraftingJob.getRecipe());
addCraftingJobProcessingPendingIngredientsEntry(startingCraftingJob, pendingIngredients);

// Register listeners for pending ingredients
for (IngredientComponent<?, ?> component : startingCraftingJob.getRecipe().getOutput().getComponents()) {
Expand All @@ -619,17 +643,19 @@ protected boolean consumeAndInsertCrafting(boolean blockingMode, INetwork networ

// Push the ingredients to the crafting interface
if (!insertCrafting(targetPos, ingredients, recipe, startingCraftingJob, network, channel, false)) {
// Unregister listeners again for pending ingredients
for (IngredientComponent<?, ?> component : startingCraftingJob.getRecipe().getOutput().getComponents()) {
unregisterIngredientObserver(component, network);
}

// If we reach this point, the target does not accept the recipe inputs,
// even though they were acceptable in simulation mode.
// The failed ingredients were already re-inserted into the network at this point,
// so we mark the job as failed, and add it again to the queue.
// The failed ingredients were already put back into the job's buffer at this point.
// Only this operation is undone, as others of this job may still be running in non-blocking mode.
if (removeCraftingJobProcessingPendingIngredientsEntry(startingCraftingJob, pendingIngredients)) {
// Unregister listeners again for pending ingredients
for (IngredientComponent<?, ?> component : startingCraftingJob.getRecipe().getOutput().getComponents()) {
unregisterIngredientObserver(component, network);
}
}

// Mark the job as failed, it will be added again to the queue once no operations are running.
startingCraftingJob.setInvalidInputs(true);
unmarkCraftingJobProcessing(startingCraftingJob);
return false;
} else {
return true;
Expand Down
Loading