Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autocrafting tasks #765

Merged
merged 6 commits into from
Jan 10, 2025
Merged
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 @@ -5,6 +5,9 @@

import java.util.List;

import org.apiguardian.api.API;

@API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12")
public record Ingredient(long amount, List<ResourceKey> inputs) {
public Ingredient(final long amount, final List<ResourceKey> inputs) {
CoreValidations.validateLargerThanZero(amount, "Amount must be larger than zero");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,23 @@
import com.refinedmods.refinedstorage.api.resource.ResourceAmount;

import java.util.List;
import java.util.UUID;

import org.apiguardian.api.API;

@API(status = API.Status.STABLE, since = "2.0.0-milestone.4.6")
public record Pattern(List<Ingredient> ingredients, List<ResourceAmount> outputs) {
public Pattern(final List<Ingredient> ingredients, final List<ResourceAmount> outputs) {
public record Pattern(UUID id, List<Ingredient> ingredients, List<ResourceAmount> outputs, PatternType type) {
public Pattern(final UUID id,
final List<Ingredient> ingredients,
final List<ResourceAmount> outputs,
final PatternType type) {
CoreValidations.validateNotNull(id, "ID cannot be null");
CoreValidations.validateNotEmpty(ingredients, "Ingredients cannot be empty");
CoreValidations.validateNotEmpty(outputs, "Outputs cannot be empty");
CoreValidations.validateNotNull(type, "Type cannot be null");
this.id = id;
this.ingredients = List.copyOf(ingredients);
this.outputs = List.copyOf(outputs);
this.type = type;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,36 @@

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import org.apiguardian.api.API;

@API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12")
public class PatternBuilder {
private final PatternType type;
private final UUID id;
private final List<Ingredient> ingredients = new ArrayList<>();
private final List<ResourceAmount> outputs = new ArrayList<>();

private PatternBuilder() {
private PatternBuilder(final UUID id, final PatternType type) {
this.id = id;
this.type = type;
}

public static PatternBuilder pattern() {
return new PatternBuilder();
return pattern(PatternType.INTERNAL);
}

public IngredientBuilder ingredient(final long amount) {
return new IngredientBuilder(amount);
public static PatternBuilder pattern(final PatternType type) {
return pattern(UUID.randomUUID(), type);
}

public PatternBuilder ingredient(final Ingredient ingredient) {
ingredients.add(ingredient);
return this;
public static PatternBuilder pattern(final UUID id, final PatternType type) {
return new PatternBuilder(id, type);
}

public IngredientBuilder ingredient(final long amount) {
return new IngredientBuilder(amount);
}

public PatternBuilder ingredient(final ResourceKey input, final long amount) {
Expand All @@ -37,7 +48,7 @@ public PatternBuilder output(final ResourceKey output, final long amount) {
}

public Pattern build() {
return new Pattern(ingredients, outputs);
return new Pattern(id, ingredients, outputs, type);
}

public class IngredientBuilder {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.refinedmods.refinedstorage.api.autocrafting;

import org.apiguardian.api.API;

@API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12")
public enum PatternType {
INTERNAL,
EXTERNAL
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package com.refinedmods.refinedstorage.api.autocrafting.calculation;

import com.refinedmods.refinedstorage.api.autocrafting.Pattern;
import com.refinedmods.refinedstorage.api.core.CoreValidations;
import com.refinedmods.refinedstorage.api.resource.ResourceAmount;
import com.refinedmods.refinedstorage.api.resource.ResourceKey;

record Amount(long iterations, long amountPerIteration) {
public record Amount(long iterations, long amountPerIteration) {
public Amount {
CoreValidations.validateLargerThanZero(iterations, "Iterations");
CoreValidations.validateLargerThanZero(amountPerIteration, "Amount per iteration");
}

public long getTotal() {
return iterations * amountPerIteration;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ public <T> void calculate(final ResourceKey resource,
throw new NumberOverflowDuringCalculationException();
}
final CraftingCalculatorListener<T> childListener = listener.childCalculationStarted(
pattern,
resource,
patternAmount.getTotal()
patternAmount
);
final CraftingTree<T> tree = root(pattern, rootStorage, patternAmount, patternRepository, childListener);
final CraftingTree.CalculationResult calculationResult = tree.calculate();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package com.refinedmods.refinedstorage.api.autocrafting.calculation;

import com.refinedmods.refinedstorage.api.autocrafting.Pattern;
import com.refinedmods.refinedstorage.api.resource.ResourceKey;

import org.apiguardian.api.API;

@API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12")
public interface CraftingCalculatorListener<T> {
CraftingCalculatorListener<T> childCalculationStarted(ResourceKey resource, long amount);
CraftingCalculatorListener<T> childCalculationStarted(Pattern childPattern, ResourceKey resource, Amount amount);

void childCalculationCompleted(CraftingCalculatorListener<T> childListener);

void ingredientsExhausted(ResourceKey resource, long amount);

void ingredientUsed(Pattern ingredientPattern, int ingredientIndex, ResourceKey resource, long amount);

void ingredientExtractedFromStorage(ResourceKey resource, long amount);

T getData();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ static <T> CraftingTree<T> child(final Pattern pattern,
final CraftingCalculatorListener<T> listener,
final Set<Pattern> activePatterns) {
final CraftingCalculatorListener<T> childListener = listener.childCalculationStarted(
pattern,
resource,
amount.getTotal()
amount
);
final CraftingState childState = parentState.copy();
return new CraftingTree<>(pattern, childState, amount, patternRepository, childListener, activePatterns);
Expand All @@ -64,9 +65,10 @@ CalculationResult calculate() {
throw new PatternCycleDetectedException(pattern);
}
CalculationResult result = CalculationResult.SUCCESS;
for (final Ingredient ingredient : pattern.ingredients()) {
for (int ingredientIndex = 0; ingredientIndex < pattern.ingredients().size(); ++ingredientIndex) {
final Ingredient ingredient = pattern.ingredients().get(ingredientIndex);
final IngredientState ingredientState = new IngredientState(ingredient, craftingState);
final CalculationResult ingredientResult = calculateIngredient(ingredientState);
final CalculationResult ingredientResult = calculateIngredient(ingredientIndex, ingredientState);
if (ingredientResult == CalculationResult.MISSING_RESOURCES) {
result = CalculationResult.MISSING_RESOURCES;
}
Expand All @@ -76,7 +78,7 @@ CalculationResult calculate() {
return result;
}

private CalculationResult calculateIngredient(final IngredientState ingredientState) {
private CalculationResult calculateIngredient(final int ingredientIndex, final IngredientState ingredientState) {
CraftingState.ResourceState resourceState = craftingState.getResource(ingredientState.get());
long remaining = ingredientState.amount() * amount.iterations();
if (remaining < 0) {
Expand All @@ -86,12 +88,14 @@ private CalculationResult calculateIngredient(final IngredientState ingredientSt
if (resourceState.isInInternalStorage()) {
final long toTake = Math.min(remaining, resourceState.inInternalStorage());
craftingState.extractFromInternalStorage(resourceState.resource(), toTake);
listener.ingredientUsed(pattern, ingredientIndex, resourceState.resource(), toTake);
remaining -= toTake;
}
if (remaining > 0 && resourceState.isInStorage()) {
final long toTake = Math.min(remaining, resourceState.inStorage());
craftingState.extractFromStorage(resourceState.resource(), toTake);
listener.ingredientExtractedFromStorage(resourceState.resource(), toTake);
listener.ingredientUsed(pattern, ingredientIndex, resourceState.resource(), toTake);
remaining -= toTake;
}
if (remaining > 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.refinedmods.refinedstorage.api.autocrafting.calculation;

import com.refinedmods.refinedstorage.api.autocrafting.Pattern;
import com.refinedmods.refinedstorage.api.resource.ResourceKey;

import static java.util.Objects.requireNonNull;
Expand All @@ -19,8 +20,9 @@ boolean isMissingResources() {
}

@Override
public CraftingCalculatorListener<Boolean> childCalculationStarted(final ResourceKey resource,
final long amount) {
public CraftingCalculatorListener<Boolean> childCalculationStarted(final Pattern childPattern,
final ResourceKey resource,
final Amount amount) {
return new MissingResourcesCraftingCalculatorListener(missingResources);
}

Expand All @@ -34,6 +36,14 @@ public void ingredientsExhausted(final ResourceKey resource, final long amount)
missingResources = true;
}

@Override
public void ingredientUsed(final Pattern ingredientPattern,
final int ingredientIndex,
final ResourceKey resource,
final long amount) {
// no op
}

@Override
public void ingredientExtractedFromStorage(final ResourceKey resource, final long amount) {
// no op
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.refinedmods.refinedstorage.api.autocrafting.preview;

import com.refinedmods.refinedstorage.api.autocrafting.Pattern;
import com.refinedmods.refinedstorage.api.autocrafting.calculation.Amount;
import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculator;
import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculatorListener;
import com.refinedmods.refinedstorage.api.autocrafting.calculation.NumberOverflowDuringCalculationException;
Expand Down Expand Up @@ -40,11 +42,12 @@ public static Preview calculatePreview(final CraftingCalculator calculator,
}

@Override
public CraftingCalculatorListener<PreviewBuilder> childCalculationStarted(final ResourceKey resource,
final long amount) {
public CraftingCalculatorListener<PreviewBuilder> childCalculationStarted(final Pattern childPattern,
final ResourceKey resource,
final Amount amount) {
LOGGER.debug("{} - Child calculation starting for {}x {}", listenerId, amount, resource);
final PreviewBuilder copy = builder.copy();
copy.addToCraft(resource, amount);
copy.addToCraft(resource, amount.getTotal());
return new PreviewCraftingCalculatorListener(copy);
}

Expand All @@ -60,6 +63,14 @@ public void ingredientsExhausted(final ResourceKey resource, final long amount)
builder.addMissing(resource, amount);
}

@Override
public void ingredientUsed(final Pattern ingredientPattern,
final int ingredientIndex,
final ResourceKey resource,
final long amount) {
// no op
}

@Override
public void ingredientExtractedFromStorage(final ResourceKey resource, final long amount) {
LOGGER.debug("{} - Extracted from storage: {} - {}", listenerId, resource, amount);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ public interface PreviewProvider {

CompletableFuture<Long> getMaxAmount(ResourceKey resource);

boolean startTask(ResourceKey resource, long amount, Actor actor, boolean notify);
CompletableFuture<Boolean> startTask(ResourceKey resource, long amount, Actor actor, boolean notify);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.refinedmods.refinedstorage.api.autocrafting.status;

import com.refinedmods.refinedstorage.api.autocrafting.TaskId;
import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId;
import com.refinedmods.refinedstorage.api.resource.ResourceKey;

import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.refinedmods.refinedstorage.api.autocrafting.status;

import com.refinedmods.refinedstorage.api.autocrafting.TaskId;
import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId;

import org.apiguardian.api.API;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.refinedmods.refinedstorage.api.autocrafting.status;

import com.refinedmods.refinedstorage.api.autocrafting.TaskId;
import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId;

import java.util.List;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.refinedmods.refinedstorage.api.autocrafting.task;

import com.refinedmods.refinedstorage.api.autocrafting.Pattern;
import com.refinedmods.refinedstorage.api.core.Action;
import com.refinedmods.refinedstorage.api.resource.ResourceKey;
import com.refinedmods.refinedstorage.api.resource.list.MutableResourceList;
import com.refinedmods.refinedstorage.api.resource.list.MutableResourceListImpl;
import com.refinedmods.refinedstorage.api.resource.list.ResourceList;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

abstract class AbstractTaskPattern {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTaskPattern.class);

protected final Pattern pattern;
protected final Map<Integer, Map<ResourceKey, Long>> ingredients = new HashMap<>();

protected AbstractTaskPattern(final Pattern pattern, final TaskPlan.PatternPlan plan) {
this.pattern = pattern;
for (final Map.Entry<Integer, Map<ResourceKey, Long>> entry : plan.ingredients().entrySet()) {
final Map<ResourceKey, Long> possibilitiesCopy = new LinkedHashMap<>(entry.getValue());
ingredients.put(entry.getKey(), possibilitiesCopy);
}
}

abstract boolean step(MutableResourceList internalStorage, ExternalPatternInputSink externalPatternInputSink);

abstract long interceptInsertion(ResourceKey resource, long amount);

protected final boolean extractAll(final ResourceList inputs,
final MutableResourceList internalStorage,
final Action action) {
for (final ResourceKey inputResource : inputs.getAll()) {
final long inputAmount = inputs.get(inputResource);
final long inInternalStorage = internalStorage.get(inputResource);
if (inInternalStorage < inputAmount) {
return false;
}
if (action == Action.EXECUTE) {
internalStorage.remove(inputResource, inputAmount);
LOGGER.debug("Extracted {}x {} from internal storage", inputAmount, inputResource);
}
}
return true;
}

protected final ResourceList calculateIterationInputs(final Action action) {
final MutableResourceList iterationInputs = MutableResourceListImpl.create();
for (final Map.Entry<Integer, Map<ResourceKey, Long>> ingredient : ingredients.entrySet()) {
final int ingredientIndex = ingredient.getKey();
if (!calculateIterationInputs(ingredient, ingredientIndex, iterationInputs, action)) {
throw new IllegalStateException();
}
}
return iterationInputs;
}

private boolean calculateIterationInputs(final Map.Entry<Integer, Map<ResourceKey, Long>> ingredient,
final int ingredientIndex,
final MutableResourceList iterationInputs,
final Action action) {
long needed = pattern.ingredients().get(ingredientIndex).amount();
for (final Map.Entry<ResourceKey, Long> possibility : ingredient.getValue().entrySet()) {
final long available = Math.min(needed, possibility.getValue());
if (available == 0) {
continue;
}
iterationInputs.add(possibility.getKey(), available);
if (action == Action.EXECUTE) {
possibility.setValue(possibility.getValue() - available);
}
needed -= available;
if (needed == 0) {
break;
}
}
return needed == 0;
}
}
Loading
Loading