Skip to content

Commit

Permalink
Merge pull request #770 from refinedmods/feat/GH-612/return-root-outputs
Browse files Browse the repository at this point in the history
Return root outputs immediately
  • Loading branch information
raoulvdberge authored Jan 11, 2025
2 parents 84f504b + e799e84 commit be9709b
Show file tree
Hide file tree
Showing 29 changed files with 549 additions and 272 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

- Autocrafting now handles multiple patterns with the same output correctly by trying to use the pattern with the
highest priority first. If there are missing resources, lower priority patterns are checked.
- The Autocrafter now faces the block you're clicking when placing it, like the other cable blocks (like the Exporter or Importer).

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
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 com.refinedmods.refinedstorage.api.storage.root.RootStorage;
import com.refinedmods.refinedstorage.api.storage.root.RootStorageListener;

import java.util.HashMap;
import java.util.LinkedHashMap;
Expand All @@ -17,20 +19,24 @@
abstract class AbstractTaskPattern {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTaskPattern.class);

protected final boolean root;
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;
this.root = plan.root();
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 boolean step(MutableResourceList internalStorage,
RootStorage rootStorage,
ExternalPatternInputSink externalPatternInputSink);

abstract long interceptInsertion(ResourceKey resource, long amount);
abstract RootStorageListener.InterceptResult interceptInsertion(ResourceKey resource, long amount);

protected final boolean extractAll(final ResourceList inputs,
final MutableResourceList internalStorage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
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 com.refinedmods.refinedstorage.api.storage.root.RootStorage;
import com.refinedmods.refinedstorage.api.storage.root.RootStorageListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -25,7 +27,9 @@ class ExternalTaskPattern extends AbstractTaskPattern {
}

@Override
boolean step(final MutableResourceList internalStorage, final ExternalPatternInputSink externalPatternInputSink) {
boolean step(final MutableResourceList internalStorage,
final RootStorage rootStorage,
final ExternalPatternInputSink externalPatternInputSink) {
if (expectedOutputs.isEmpty()) {
return true;
}
Expand All @@ -41,14 +45,15 @@ boolean step(final MutableResourceList internalStorage, final ExternalPatternInp
}

@Override
long interceptInsertion(final ResourceKey resource, final long amount) {
RootStorageListener.InterceptResult interceptInsertion(final ResourceKey resource, final long amount) {
final long needed = expectedOutputs.get(resource);
if (needed > 0) {
final long available = Math.min(needed, amount);
expectedOutputs.remove(resource, available);
return available;
if (needed == 0) {
return RootStorageListener.InterceptResult.EMPTY;
}
return 0;
final long reserved = Math.min(needed, amount);
expectedOutputs.remove(resource, reserved);
final long intercepted = root ? 0 : reserved;
return new RootStorageListener.InterceptResult(reserved, intercepted);
}

private boolean acceptsIterationInputs(final MutableResourceList internalStorage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

import com.refinedmods.refinedstorage.api.autocrafting.Pattern;
import com.refinedmods.refinedstorage.api.core.Action;
import com.refinedmods.refinedstorage.api.resource.ResourceAmount;
import com.refinedmods.refinedstorage.api.resource.ResourceKey;
import com.refinedmods.refinedstorage.api.resource.list.MutableResourceList;
import com.refinedmods.refinedstorage.api.resource.list.ResourceList;
import com.refinedmods.refinedstorage.api.storage.Actor;
import com.refinedmods.refinedstorage.api.storage.root.RootStorage;
import com.refinedmods.refinedstorage.api.storage.root.RootStorageListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -20,24 +24,40 @@ class InternalTaskPattern extends AbstractTaskPattern {
}

@Override
boolean step(final MutableResourceList internalStorage, final ExternalPatternInputSink externalPatternInputSink) {
boolean step(final MutableResourceList internalStorage,
final RootStorage rootStorage,
final ExternalPatternInputSink externalPatternInputSink) {
final ResourceList iterationInputsSimulated = calculateIterationInputs(Action.SIMULATE);
if (!extractAll(iterationInputsSimulated, internalStorage, Action.SIMULATE)) {
return false;
}
LOGGER.debug("Stepping {}", pattern);
final ResourceList iterationInputs = calculateIterationInputs(Action.EXECUTE);
extractAll(iterationInputs, internalStorage, Action.EXECUTE);
pattern.outputs().forEach(output -> {
pattern.outputs().forEach(output -> returnOutput(internalStorage, rootStorage, output));
return useIteration();
}

private void returnOutput(final MutableResourceList internalStorage,
final RootStorage rootStorage,
final ResourceAmount output) {
if (root) {
LOGGER.debug("Inserting {}x {} into root storage", output.amount(), output.resource());
final long inserted = rootStorage.insert(output.resource(), output.amount(), Action.EXECUTE, Actor.EMPTY);
if (inserted != output.amount()) {
final long remainder = output.amount() - inserted;
LOGGER.debug("Inserting overflow {}x {} into internal storage", remainder, output.resource());
internalStorage.add(output.resource(), remainder);
}
} else {
LOGGER.debug("Inserting {}x {} into internal storage", output.amount(), output.resource());
internalStorage.add(output);
});
return useIteration();
}
}

@Override
long interceptInsertion(final ResourceKey resource, final long amount) {
return 0;
RootStorageListener.InterceptResult interceptInsertion(final ResourceKey resource, final long amount) {
return RootStorageListener.InterceptResult.EMPTY;
}

protected boolean useIteration() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@

class MutablePatternPlan {
private final Pattern pattern;
private final boolean root;
private final Map<Integer, Map<ResourceKey, Long>> ingredients = new HashMap<>();
private long iterations;

MutablePatternPlan(final Pattern pattern) {
MutablePatternPlan(final Pattern pattern, final boolean root) {
this.pattern = pattern;
this.root = root;
}

void addIterations(final long it) {
Expand All @@ -31,7 +33,7 @@ void addUsedIngredient(final int ingredientIndex, final ResourceKey resource, fi
}

MutablePatternPlan copy() {
final MutablePatternPlan copy = new MutablePatternPlan(pattern);
final MutablePatternPlan copy = new MutablePatternPlan(pattern, root);
copy.iterations = iterations;
for (final Map.Entry<Integer, Map<ResourceKey, Long>> entry : ingredients.entrySet()) {
final Map<ResourceKey, Long> resourcesCopy = new LinkedHashMap<>(entry.getValue());
Expand All @@ -41,7 +43,7 @@ MutablePatternPlan copy() {
}

TaskPlan.PatternPlan getPlan() {
return new TaskPlan.PatternPlan(iterations, ingredients.entrySet().stream()
return new TaskPlan.PatternPlan(root, iterations, ingredients.entrySet().stream()
.collect(Collectors.toUnmodifiableMap(
Map.Entry::getKey,
e -> Collections.unmodifiableMap(e.getValue()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

public class MutableTaskPlan {
@Nullable
private final Pattern pattern;
private final Pattern rootPattern;
private final Map<Pattern, MutablePatternPlan> patterns;
private final MutableResourceList initialRequirements;
private boolean missing;
Expand All @@ -25,18 +25,21 @@ public class MutableTaskPlan {
this(null, new LinkedHashMap<>(), MutableResourceListImpl.create(), false);
}

private MutableTaskPlan(@Nullable final Pattern pattern,
private MutableTaskPlan(@Nullable final Pattern rootPattern,
final Map<Pattern, MutablePatternPlan> patterns,
final MutableResourceList initialRequirements,
final boolean missing) {
this.pattern = pattern;
this.rootPattern = rootPattern;
this.patterns = patterns;
this.initialRequirements = initialRequirements;
this.missing = missing;
}

void addOrUpdatePattern(final Pattern usedPattern, final long iterations) {
patterns.computeIfAbsent(usedPattern, MutablePatternPlan::new).addIterations(iterations);
patterns.computeIfAbsent(usedPattern, p -> new MutablePatternPlan(
p,
p.equals(rootPattern)
)).addIterations(iterations);
}

void addToExtract(final ResourceKey resource, final long amount) {
Expand All @@ -57,15 +60,15 @@ MutableTaskPlan copy(final Pattern childPattern) {
patternsCopy.put(entry.getKey(), entry.getValue().copy());
}
return new MutableTaskPlan(
pattern == null ? childPattern : pattern,
rootPattern == null ? childPattern : rootPattern,
patternsCopy,
initialRequirements.copy(),
missing
);
}

Optional<TaskPlan> getPlan() {
if (missing || pattern == null) {
if (missing || rootPattern == null) {
return Optional.empty();
}
final Map<Pattern, TaskPlan.PatternPlan> finalPatterns = Collections.unmodifiableMap(patterns.entrySet()
Expand All @@ -76,7 +79,7 @@ Optional<TaskPlan> getPlan() {
(a, b) -> a,
LinkedHashMap::new
)));
return Optional.of(new TaskPlan(pattern, finalPatterns, initialRequirements.copyState()));
return Optional.of(new TaskPlan(rootPattern, finalPatterns, initialRequirements.copyState()));
}

void setMissing() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public void step(final RootStorage rootStorage, final ExternalPatternInputSink e
switch (state) {
case READY -> startTask(rootStorage);
case EXTRACTING_INITIAL_RESOURCES -> extractInitialResourcesAndTryStartRunningTask(rootStorage);
case RUNNING -> stepPatterns(externalPatternInputSink);
case RUNNING -> stepPatterns(rootStorage, externalPatternInputSink);
case RETURNING_INTERNAL_STORAGE -> returnInternalStorageAndTryCompleteTask(rootStorage);
}
}
Expand All @@ -86,16 +86,20 @@ private void extractInitialResourcesAndTryStartRunningTask(final RootStorage roo
}
}

private void stepPatterns(final ExternalPatternInputSink externalPatternInputSink) {
private void stepPatterns(final RootStorage rootStorage, final ExternalPatternInputSink externalPatternInputSink) {
patterns.entrySet().removeIf(pattern -> {
final boolean completed = pattern.getValue().step(internalStorage, externalPatternInputSink);
final boolean completed = pattern.getValue().step(internalStorage, rootStorage, externalPatternInputSink);
if (completed) {
LOGGER.debug("{} completed", pattern.getKey());
}
return completed;
});
if (patterns.isEmpty()) {
updateState(TaskState.RETURNING_INTERNAL_STORAGE);
if (internalStorage.isEmpty()) {
updateState(TaskState.COMPLETED);
} else {
updateState(TaskState.RETURNING_INTERNAL_STORAGE);
}
}
}

Expand Down Expand Up @@ -146,19 +150,25 @@ private boolean returnInternalStorage(final RootStorage rootStorage) {
}

@Override
public long beforeInsert(final ResourceKey resource, final long amount, final Actor actor) {
long remaining = amount;
public InterceptResult beforeInsert(final ResourceKey resource, final long amount, final Actor actor) {
// TODO: variations in reserved and intercepted are not well tested for a single task
// (try it, tweak the numbers)
// TODO: variants in reserved and intercepted are not well tested across multiple tasks
long reserved = 0;
long intercepted = 0;
for (final AbstractTaskPattern pattern : patterns.values()) {
final long intercepted = pattern.interceptInsertion(resource, remaining);
if (intercepted > 0) {
internalStorage.add(resource, intercepted);
final long remainder = amount - reserved;
final InterceptResult result = pattern.interceptInsertion(resource, remainder);
if (result.intercepted() > 0) {
internalStorage.add(resource, result.intercepted());
}
remaining -= intercepted;
if (remaining == 0) {
return amount;
reserved += result.reserved();
intercepted += result.intercepted();
if (reserved == amount) {
return new InterceptResult(reserved, intercepted);
}
}
return amount - remaining;
return new InterceptResult(reserved, intercepted);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
import org.apiguardian.api.API;

@API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12")
public record TaskPlan(Pattern pattern,
public record TaskPlan(Pattern rootPattern,
Map<Pattern, PatternPlan> patterns,
Collection<ResourceAmount> initialRequirements) {
public PatternPlan getPattern(final Pattern p) {
return patterns.get(p);
public PatternPlan getPattern(final Pattern pattern) {
return patterns.get(pattern);
}

public record PatternPlan(long iterations, Map<Integer, Map<ResourceKey, Long>> ingredients) {
public record PatternPlan(boolean root, long iterations, Map<Integer, Map<ResourceKey, Long>> ingredients) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ public final class PatternFixtures {
.ingredient(1).input(OAK_PLANKS).input(SPRUCE_PLANKS).end()
.output(CRAFTING_TABLE, 1)
.build();
public static final Pattern CRAFTING_TABLE_YIELD_2X_PATTERN = pattern()
.ingredient(1).input(OAK_PLANKS).input(SPRUCE_PLANKS).end()
.ingredient(1).input(OAK_PLANKS).input(SPRUCE_PLANKS).end()
.ingredient(1).input(OAK_PLANKS).input(SPRUCE_PLANKS).end()
.ingredient(1).input(OAK_PLANKS).input(SPRUCE_PLANKS).end()
.output(CRAFTING_TABLE, 2)
.build();
public static final Pattern IRON_INGOT_PATTERN = pattern(PatternType.EXTERNAL)
.ingredient(IRON_ORE, 1)
.output(IRON_INGOT, 1)
Expand Down
Loading

0 comments on commit be9709b

Please sign in to comment.