Skip to content

Commit

Permalink
Merge pull request #797 from refinedmods/feat/GH-612/relay-tasks
Browse files Browse the repository at this point in the history
feat: relay support for autocrafting tasks
  • Loading branch information
raoulvdberge authored Feb 1, 2025
2 parents 10927d3 + 8f8ad0c commit 2e30c47
Show file tree
Hide file tree
Showing 11 changed files with 699 additions and 88 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Added

- Colored variants are now moved to a separate creative mode tab.
- You can now start autocrafting tasks in the Relay's output network, using patterns and autocrafters from the input network.

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ public interface ExternalPatternSink {
* If the sink is locked, it must return {@link Result#LOCKED}.
* If the resources are not applicable for this sink, it must return {@link Result#SKIPPED}.
*
* @param pattern the pattern
* @param resources the resources
* @param action the action
* @return the result
*/
Result accept(Collection<ResourceAmount> resources, Action action);
Result accept(Pattern pattern, Collection<ResourceAmount> resources, Action action);

/**
* @return the key for this sink
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ private boolean acceptsIterationInputs(final MutableResourceList internalStorage
// across the sink and the internal storage.
// The end result is that we lie, do as if the insertion was successful,
// and potentially void the extracted resources from the internal storage.
if (sink.accept(iterationInputs.copyState(), Action.EXECUTE) != ExternalPatternSink.Result.ACCEPTED) {
if (sink.accept(pattern, iterationInputs.copyState(), Action.EXECUTE) != ExternalPatternSink.Result.ACCEPTED) {
LOGGER.warn("Sink {} did not accept all inputs for pattern {}", sink, pattern);
}
return true;
Expand All @@ -209,6 +209,7 @@ private ExternalPatternSink getSinkThatIsAcceptingResources(final List<ExternalP
while (currentSinkIndex < sinks.size()) {
final ExternalPatternSink sink = sinks.get(currentSinkIndex);
final ExternalPatternSink.Result simulatedResult = sink.accept(
pattern,
iterationInputsSimulated.copyState(),
Action.SIMULATE
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ public ExternalPatternSinkKey getKey() {
}

@Override
public Result accept(final Collection<ResourceAmount> resources, final Action action) {
public Result accept(final Pattern p,
final Collection<ResourceAmount> resources,
final Action action) {
if (fixedResult != null) {
return fixedResult;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ void shouldNotCompleteTaskWithExternalPatternIfSinkDoesNotAcceptResourcesOnlyWhe
);
final PatternRepository patterns = patterns(IRON_INGOT_PATTERN, IRON_PICKAXE_PATTERN);
final ExternalPatternSinkProviderImpl sinkProvider = new ExternalPatternSinkProviderImpl();
sinkProvider.put(IRON_INGOT_PATTERN, (resources, action) -> action == Action.EXECUTE
sinkProvider.put(IRON_INGOT_PATTERN, (pattern, resources, action) -> action == Action.EXECUTE
? ExternalPatternSink.Result.REJECTED
: ExternalPatternSink.Result.ACCEPTED);
final Task task = getRunningTask(storage, patterns, sinkProvider, IRON_PICKAXE, 1);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package com.refinedmods.refinedstorage.api.network.impl.autocrafting;

import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus;
import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternSinkProvider;
import com.refinedmods.refinedstorage.api.autocrafting.task.StepBehavior;
import com.refinedmods.refinedstorage.api.autocrafting.task.Task;
import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId;
import com.refinedmods.refinedstorage.api.autocrafting.task.TaskListener;
import com.refinedmods.refinedstorage.api.autocrafting.task.TaskState;
import com.refinedmods.refinedstorage.api.network.Network;
import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent;
import com.refinedmods.refinedstorage.api.network.autocrafting.ParentContainer;
import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProvider;
import com.refinedmods.refinedstorage.api.network.storage.StorageNetworkComponent;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Nullable;

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

public class TaskContainer {
private static final Logger LOGGER = LoggerFactory.getLogger(TaskContainer.class);

private final PatternProvider patternProvider;
private final List<Task> tasks = new CopyOnWriteArrayList<>();
private final List<Task> tasksView = Collections.unmodifiableList(tasks);
private final Set<ParentContainer> parents = new HashSet<>();

public TaskContainer(final PatternProvider patternProvider) {
this.patternProvider = patternProvider;
}

public List<Task> getAll() {
return tasksView;
}

public List<TaskStatus> getStatuses() {
return tasks.stream().map(Task::getStatus).toList();
}

public void onRemovedFromContainer(final ParentContainer parent) {
tasks.forEach(parent::taskRemoved);
parents.remove(parent);
}

public void onAddedIntoContainer(final ParentContainer parent) {
tasks.forEach(task -> parent.taskAdded(patternProvider, task));
parents.add(parent);
}

public void add(final Task task, @Nullable final Network network) {
tasks.add(task);
if (network != null) {
attach(task, network.getComponent(StorageNetworkComponent.class));
}
}

public void cancel(final TaskId id) {
for (final Task task : tasks) {
if (task.getId().equals(id)) {
task.cancel();
return;
}
}
throw new IllegalArgumentException("Task %s not found".formatted(id));
}

public void attachAll(final Network network) {
final StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class);
tasks.forEach(task -> attach(task, storage));
}

public void detachAll(final Network network) {
final StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class);
tasks.forEach(task -> detach(task, storage));
}

private void attach(final Task task, final StorageNetworkComponent storage) {
storage.addListener(task);
}

private void detach(final Task task, final StorageNetworkComponent storage) {
storage.removeListener(task);
}

public void step(final Network network, final StepBehavior stepBehavior, final TaskListener listener) {
final StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class);
final ExternalPatternSinkProvider sinkProvider = network.getComponent(AutocraftingNetworkComponent.class);
tasks.removeIf(task -> step(task, storage, sinkProvider, stepBehavior, listener));
}

private boolean step(final Task task,
final StorageNetworkComponent storage,
final ExternalPatternSinkProvider sinkProvider,
final StepBehavior stepBehavior,
final TaskListener listener) {
boolean changed;
boolean completed;
try {
changed = task.step(storage, sinkProvider, stepBehavior, listener);
completed = task.getState() == TaskState.COMPLETED;
} catch (final Exception e) {
LOGGER.error("Exception while stepping task {} {}, removing task", task.getResource(), task.getAmount(), e);
changed = false;
completed = true;
}
if (completed) {
detach(task, storage);
parents.forEach(parent -> parent.taskCompleted(task));
} else if (changed) {
parents.forEach(parent -> parent.taskChanged(task));
}
return completed;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,30 @@
import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus;
import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternSink;
import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternSinkKey;
import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternSinkProvider;
import com.refinedmods.refinedstorage.api.autocrafting.task.StepBehavior;
import com.refinedmods.refinedstorage.api.autocrafting.task.Task;
import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId;
import com.refinedmods.refinedstorage.api.autocrafting.task.TaskListener;
import com.refinedmods.refinedstorage.api.autocrafting.task.TaskState;
import com.refinedmods.refinedstorage.api.core.Action;
import com.refinedmods.refinedstorage.api.network.Network;
import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent;
import com.refinedmods.refinedstorage.api.network.autocrafting.ParentContainer;
import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProvider;
import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProviderExternalPatternSink;
import com.refinedmods.refinedstorage.api.network.impl.autocrafting.TaskContainer;
import com.refinedmods.refinedstorage.api.network.impl.node.SimpleNetworkNode;
import com.refinedmods.refinedstorage.api.network.storage.StorageNetworkComponent;
import com.refinedmods.refinedstorage.api.resource.ResourceAmount;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Nullable;

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

public class PatternProviderNetworkNode extends SimpleNetworkNode implements PatternProvider, TaskListener {
private static final Logger LOGGER = LoggerFactory.getLogger(PatternProviderNetworkNode.class);

private final Pattern[] patterns;
private final Set<ParentContainer> parents = new HashSet<>();
private final List<Task> tasks = new CopyOnWriteArrayList<>();
private final TaskContainer tasks = new TaskContainer(this);
private int priority;
@Nullable
private PatternProviderExternalPatternSink sink;
Expand Down Expand Up @@ -68,17 +60,11 @@ public void setPattern(final int index, @Nullable final Pattern pattern) {
@Override
public void setNetwork(@Nullable final Network network) {
if (this.network != null) {
final StorageNetworkComponent storage = this.network.getComponent(StorageNetworkComponent.class);
for (final Task task : tasks) {
cleanupTask(task, storage);
}
tasks.detachAll(this.network);
}
super.setNetwork(network);
if (network != null) {
final StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class);
for (final Task task : tasks) {
setupTask(task, storage);
}
tasks.attachAll(network);
}
}

Expand All @@ -103,7 +89,7 @@ protected void onActiveChanged(final boolean newActive) {
@Override
public void onAddedIntoContainer(final ParentContainer parentContainer) {
parents.add(parentContainer);
tasks.forEach(task -> parentContainer.taskAdded(this, task));
tasks.onAddedIntoContainer(parentContainer);
for (final Pattern pattern : patterns) {
if (pattern != null) {
parentContainer.add(this, pattern, priority);
Expand All @@ -113,7 +99,7 @@ public void onAddedIntoContainer(final ParentContainer parentContainer) {

@Override
public void onRemovedFromContainer(final ParentContainer parentContainer) {
tasks.forEach(parentContainer::taskRemoved);
tasks.onRemovedFromContainer(parentContainer);
parents.remove(parentContainer);
for (final Pattern pattern : patterns) {
if (pattern != null) {
Expand All @@ -124,27 +110,18 @@ public void onRemovedFromContainer(final ParentContainer parentContainer) {

@Override
public void addTask(final Task task) {
tasks.add(task);
if (network != null) {
setupTask(task, network.getComponent(StorageNetworkComponent.class));
}
tasks.add(task, network);
parents.forEach(parent -> parent.taskAdded(this, task));
}

@Override
public void cancelTask(final TaskId taskId) {
for (final Task task : tasks) {
if (task.getId().equals(taskId)) {
task.cancel();
return;
}
}
throw new IllegalArgumentException("Task %s not found".formatted(taskId));
tasks.cancel(taskId);
}

@Override
public List<TaskStatus> getTaskStatuses() {
return tasks.stream().map(Task::getStatus).toList();
return tasks.getStatuses();
}

@Override
Expand All @@ -167,24 +144,18 @@ public void receivedExternalIteration(final Pattern pattern) {
provider.receivedExternalIteration();
}

private void setupTask(final Task task, final StorageNetworkComponent storage) {
storage.addListener(task);
}

private void cleanupTask(final Task task, final StorageNetworkComponent storage) {
storage.removeListener(task);
}

@Override
public ExternalPatternSink.Result accept(final Collection<ResourceAmount> resources, final Action action) {
public ExternalPatternSink.Result accept(final Pattern pattern,
final Collection<ResourceAmount> resources,
final Action action) {
if (sink == null) {
return ExternalPatternSink.Result.SKIPPED;
}
return sink.accept(resources, action);
}

public List<Task> getTasks() {
return tasks;
return tasks.getAll();
}

@Override
Expand All @@ -193,31 +164,7 @@ public void doWork() {
if (network == null || !isActive()) {
return;
}
final StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class);
final ExternalPatternSinkProvider sinkProvider = network.getComponent(AutocraftingNetworkComponent.class);
tasks.removeIf(task -> stepTask(task, storage, sinkProvider));
}

private boolean stepTask(final Task task,
final StorageNetworkComponent storage,
final ExternalPatternSinkProvider sinkProvider) {
boolean changed;
boolean completed;
try {
changed = task.step(storage, sinkProvider, stepBehavior, this);
completed = task.getState() == TaskState.COMPLETED;
} catch (final Exception e) {
LOGGER.error("Exception while stepping task {} {}, removing task", task.getResource(), task.getAmount(), e);
changed = false;
completed = true;
}
if (completed) {
cleanupTask(task, storage);
parents.forEach(parent -> parent.taskCompleted(task));
} else if (changed) {
parents.forEach(parent -> parent.taskChanged(task));
}
return completed;
tasks.step(network, stepBehavior, this);
}

@Nullable
Expand Down
Loading

0 comments on commit 2e30c47

Please sign in to comment.