Allow crafters to be daisy-chained (#1740)

* Allow crafters to be daisy-chained

In crafting situations that require more than the 9 blocking patterns
that one crafter allows, the only solution is to duplicate the setup
to allow a second crafter.  This CL allows crafters to be
daisy-chained to provide a solution to this issue.

Also, since unlimited crafters can now be placed on a single
inventory, this eliminates the need to have multiple intermediate
inventories which then pipe into the desired inventory.  This also
improves the crafting manager UI as now those patterns can all be
listed under one "Furnace" instead of (potentially multiple) "Chest"s.

* Update crafting manager on crafter rotate

* Revert .gitignore change and fix crash

* Refactor

* Remove Set import

* Address comments
This commit is contained in:
tomKPZ
2018-04-23 22:34:56 -07:00
committed by Raoul
parent 2aea6a761e
commit e85cda1b0a
9 changed files with 195 additions and 32 deletions

View File

@@ -1,5 +1,8 @@
# Refined Storage Changelog
### 1.5.34
- Allow crafters to be daisy-chained (tomKPZ)
### 1.5.33
- Added Crafter Manager (raoulvdberge)
- Patterns in the Crafter slots now automatically render the output without pressing shift (raoulvdberge)

View File

@@ -12,6 +12,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* The crafting manager handles the storing, updating, adding and deleting of crafting tasks in a network.
@@ -32,6 +33,29 @@ public interface ICraftingManager {
*/
Map<String, List<IItemHandlerModifiable>> getNamedContainers();
/**
* @param blocker the container that is blocking another container
* @param blockee the container being blocked, may be the same as blocker
*/
void addContainerBlock(UUID blocker, UUID blockee);
/**
* @param blocker the container that is blocking another container
*/
void removeContainerBlock(UUID blocker);
/**
* @param blockee the container to check
* @return whether the container is blocked
*/
boolean isContainerBlocked(UUID blockee);
/**
* @param container the container to set the blocking state for
* @param blocked whether the container should be blocked
*/
void setContainerBlocked(ICraftingPatternContainer container, boolean blocked);
/**
* Adds a crafting task.
*

View File

@@ -1,12 +1,15 @@
package com.raoulvdberge.refinedstorage.api.autocrafting;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.IItemHandlerModifiable;
import javax.annotation.Nullable;
import java.util.List;
import java.util.UUID;
/**
* Represents a network node that contains crafting patterns.
@@ -18,15 +21,25 @@ public interface ICraftingPatternContainer {
int getSpeedUpdateCount();
/**
* @return the inventory that this container is facing
* @return the inventory that this container is connected to
*/
IItemHandler getFacingInventory();
IItemHandler getConnectedInventory();
/**
* @return the tile that this container is connected to
*/
TileEntity getConnectedTile();
/**
* @return the tile that this container is facing
*/
TileEntity getFacingTile();
/**
* @return the direction to the facing tile
*/
EnumFacing getDirection();
/**
* @return the patterns stored in this container
*/
@@ -53,12 +66,23 @@ public interface ICraftingPatternContainer {
BlockPos getPosition();
/**
* @return true if this container is blocked, false otherwise
* Containers may be daisy-chained together. If this container points to
* another one, gets the root container in the chain. If containers are
* not daisy-chained, returns this container. If there was a container
* loop, returns null.
*
* @return the root pattern container
*/
@Nullable
ICraftingPatternContainer getRootContainer();
/**
* @return true if this container or its proxy is blocked, false otherwise
*/
boolean isBlocked();
/**
* @param blocked whether the container should be blocked
* @return the UUID of this container
*/
void setBlocked(boolean blocked);
UUID getUuid();
}

View File

@@ -27,10 +27,14 @@ import net.minecraftforge.items.ItemHandlerHelper;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public class CraftingManager implements ICraftingManager {
private static final String NBT_CRAFTING_TASKS = "CraftingTasks";
private static final String NBT_BLOCKED_CONTAINERS = "BlockedContainers";
private static final String NBT_BLOCKER_UUID = "BlockerUuid";
private static final String NBT_BLOCKEE_UUID = "BlockeeUuid";
private TileController network;
@@ -38,6 +42,11 @@ public class CraftingManager implements ICraftingManager {
private Map<String, List<IItemHandlerModifiable>> containerInventories = new LinkedHashMap<>();
private CraftingPatternChainList patterns = new CraftingPatternChainList();
// A map of blockers to blockees.
private Map<UUID, UUID> blockingContainers = new HashMap<>();
// A set of blockees.
private Set<UUID> blockedContainers = new HashSet<>();
private List<ICraftingTask> craftingTasks = new ArrayList<>();
private List<ICraftingTask> craftingTasksToAdd = new ArrayList<>();
private List<ICraftingTask> craftingTasksToCancel = new ArrayList<>();
@@ -67,6 +76,35 @@ public class CraftingManager implements ICraftingManager {
return containerInventories;
}
@Override
public void addContainerBlock(UUID blocker, UUID blockee) {
blockedContainers.add(blockee);
blockingContainers.put(blocker, blockee);
}
@Override
public void removeContainerBlock(UUID blocker) {
blockedContainers.remove(blockingContainers.get(blocker));
blockingContainers.remove(blocker);
}
@Override
public boolean isContainerBlocked(UUID blockee) {
return blockedContainers.contains(blockee);
}
@Override
public void setContainerBlocked(ICraftingPatternContainer container, boolean blocked) {
if (blocked) {
ICraftingPatternContainer proxy = container.getRootContainer();
if (proxy != null) {
addContainerBlock(container.getUuid(), proxy.getUuid());
}
} else {
removeContainerBlock(container.getUuid());
}
}
@Override
public void add(@Nonnull ICraftingTask task) {
craftingTasksToAdd.add(task);
@@ -230,23 +268,37 @@ public class CraftingManager implements ICraftingManager {
public void readFromNBT(NBTTagCompound tag) {
if (tag.hasKey(NBT_CRAFTING_TASKS)) {
NBTTagList taskList = tag.getTagList(NBT_CRAFTING_TASKS, Constants.NBT.TAG_COMPOUND);
for (int i = 0; i < taskList.tagCount(); ++i) {
craftingTasksToRead.add(taskList.getCompoundTagAt(i));
}
}
if (tag.hasKey(NBT_BLOCKED_CONTAINERS)) {
NBTTagList containerList = tag.getTagList(NBT_BLOCKED_CONTAINERS, Constants.NBT.TAG_COMPOUND);
for (int i = 0; i < containerList.tagCount(); ++i) {
NBTTagCompound compound = containerList.getCompoundTagAt(i);
addContainerBlock(compound.getUniqueId(NBT_BLOCKER_UUID), compound.getUniqueId(NBT_BLOCKEE_UUID));
}
}
}
@Override
public NBTTagCompound writeToNBT(NBTTagCompound tag) {
NBTTagList craftingTaskList = new NBTTagList();
for (ICraftingTask task : craftingTasks) {
craftingTaskList.appendTag(task.writeToNBT(new NBTTagCompound()));
}
tag.setTag(NBT_CRAFTING_TASKS, craftingTaskList);
NBTTagList blockingContainersList = new NBTTagList();
for (Entry<UUID, UUID> pair : blockingContainers.entrySet()) {
NBTTagCompound compound = new NBTTagCompound();
compound.setUniqueId(NBT_BLOCKER_UUID, pair.getKey());
compound.setUniqueId(NBT_BLOCKEE_UUID, pair.getValue());
blockingContainersList.appendTag(compound);
}
tag.setTag(NBT_BLOCKED_CONTAINERS, blockingContainersList);
return tag;
}

View File

@@ -122,7 +122,7 @@ public abstract class CraftingStep implements ICraftingStep {
@Override
public void setStartedProcessing() {
if (getPattern().isBlocking()) {
getPattern().getContainer().setBlocked(true);
network.getCraftingManager().setContainerBlocked(getPattern().getContainer(), true);
}
startedProcessing = true;
@@ -144,7 +144,7 @@ public abstract class CraftingStep implements ICraftingStep {
}
if (getPattern().isBlocking()) {
getPattern().getContainer().setBlocked(false);
network.getCraftingManager().setContainerBlocked(getPattern().getContainer(), false);
}
return true;

View File

@@ -35,7 +35,7 @@ public class CraftingStepProcess extends CraftingStep {
return false;
}
IItemHandler inventory = getPattern().getContainer().getFacingInventory();
IItemHandler inventory = getPattern().getContainer().getConnectedInventory();
int compare = CraftingTask.DEFAULT_COMPARE | (pattern.isOredict() ? IComparer.COMPARE_OREDICT : 0);
@@ -80,7 +80,7 @@ public class CraftingStepProcess extends CraftingStep {
return false;
}
IItemHandler inventory = getPattern().getContainer().getFacingInventory();
IItemHandler inventory = getPattern().getContainer().getConnectedInventory();
return inventory != null && insertItems(inventory, new LinkedList<>(getInputs()), true);
}
@@ -92,7 +92,7 @@ public class CraftingStepProcess extends CraftingStep {
int compare = CraftingTask.DEFAULT_COMPARE | (getPattern().isOredict() ? IComparer.COMPARE_OREDICT : 0);
if (extractItems(extracted, compare, toInsertItems)) {
IItemHandler inventory = getPattern().getContainer().getFacingInventory();
IItemHandler inventory = getPattern().getContainer().getConnectedInventory();
if (insertItems(inventory, new ArrayDeque<>(extracted), true)) {
insertItems(inventory, extracted, false);

View File

@@ -404,7 +404,7 @@ public class CraftingTask implements ICraftingTask {
for (ICraftingStep step : getSteps()) {
if (step.getPattern().isBlocking()) {
step.getPattern().getContainer().setBlocked(false);
network.getCraftingManager().setContainerBlocked(step.getPattern().getContainer(), false);
}
}
@@ -670,7 +670,7 @@ public class CraftingTask implements ICraftingTask {
32
);
if (step.getPattern().getContainer().getFacingTile() == null) {
if (step.getPattern().getContainer().getConnectedTile() == null) {
element = new CraftingMonitorElementError(element, "gui.refinedstorage:crafting_monitor.machine_none");
} else if (!step.hasStartedProcessing() && !step.canStartProcessing()) {
element = new CraftingMonitorElementError(element, "gui.refinedstorage:crafting_monitor.machine_in_use");

View File

@@ -215,7 +215,15 @@ public abstract class NetworkNode implements INetworkNode, INetworkNodeVisitor,
// @todo: Move this data to the network node.
public void resetDirection() {
this.direction = ((TileBase) (IntegrationMCMP.isLoaded() ? RSMCMPAddon.unwrapTile(world, pos) : world.getTileEntity(pos))).getDirection();
EnumFacing direction = ((TileBase) (IntegrationMCMP.isLoaded() ? RSMCMPAddon.unwrapTile(world, pos) : world.getTileEntity(pos))).getDirection();
if (!direction.equals(this.direction)) {
this.direction = direction;
onDirectionChanged();
}
}
protected void onDirectionChanged() {
// NO OP
}
@Nullable

View File

@@ -5,6 +5,8 @@ import com.raoulvdberge.refinedstorage.api.autocrafting.ICraftingPattern;
import com.raoulvdberge.refinedstorage.api.autocrafting.ICraftingPatternContainer;
import com.raoulvdberge.refinedstorage.api.autocrafting.ICraftingPatternProvider;
import com.raoulvdberge.refinedstorage.api.network.INetwork;
import com.raoulvdberge.refinedstorage.api.network.node.INetworkNode;
import com.raoulvdberge.refinedstorage.apiimpl.API;
import com.raoulvdberge.refinedstorage.inventory.ItemHandlerBase;
import com.raoulvdberge.refinedstorage.inventory.ItemHandlerListenerNetworkNode;
import com.raoulvdberge.refinedstorage.inventory.ItemHandlerUpgrade;
@@ -24,14 +26,15 @@ import net.minecraftforge.items.wrapper.CombinedInvWrapper;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class NetworkNodeCrafter extends NetworkNode implements ICraftingPatternContainer {
public static final String ID = "crafter";
public static final String DEFAULT_NAME = "gui.refinedstorage:crafter";
private static final String NBT_BLOCKED = "Blocked";
private static final String NBT_DISPLAY_NAME = "DisplayName";
private static final String NBT_UUID = "CrafterUuid";
private ItemHandlerBase patterns = new ItemHandlerBase(9, new ItemHandlerListenerNetworkNode(this), s -> isValidPatternInSlot(world, s)) {
@Override
@@ -61,11 +64,16 @@ public class NetworkNodeCrafter extends NetworkNode implements ICraftingPatternC
private ItemHandlerUpgrade upgrades = new ItemHandlerUpgrade(4, new ItemHandlerListenerNetworkNode(this), ItemUpgrade.TYPE_SPEED);
private boolean blocked = false;
// Used to prevent infinite recursion on getRootContainer() when
// there's eg. two crafters facing each other.
private boolean visited = false;
@Nullable
private String displayName;
@Nullable
private UUID uuid = null;
public NetworkNodeCrafter(World world, BlockPos pos) {
super(world, pos);
}
@@ -108,11 +116,19 @@ public class NetworkNodeCrafter extends NetworkNode implements ICraftingPatternC
network.getCraftingManager().getTasks().stream()
.filter(task -> task.getPattern().getContainer().getPosition().equals(pos))
.forEach(task -> network.getCraftingManager().cancel(task));
network.getCraftingManager().setContainerBlocked(this, false);
}
network.getCraftingManager().rebuild();
}
@Override
protected void onDirectionChanged() {
if (network != null) {
network.getCraftingManager().rebuild();
}
}
@Override
public void read(NBTTagCompound tag) {
super.read(tag);
@@ -120,13 +136,13 @@ public class NetworkNodeCrafter extends NetworkNode implements ICraftingPatternC
StackUtils.readItems(patterns, 0, tag);
StackUtils.readItems(upgrades, 1, tag);
if (tag.hasKey(NBT_BLOCKED)) {
blocked = tag.getBoolean(NBT_BLOCKED);
}
if (tag.hasKey(NBT_DISPLAY_NAME)) {
displayName = tag.getString(NBT_DISPLAY_NAME);
}
if (tag.hasUniqueId(NBT_UUID)) {
uuid = tag.getUniqueId(NBT_UUID);
}
}
@Override
@@ -141,12 +157,14 @@ public class NetworkNodeCrafter extends NetworkNode implements ICraftingPatternC
StackUtils.writeItems(patterns, 0, tag);
StackUtils.writeItems(upgrades, 1, tag);
tag.setBoolean(NBT_BLOCKED, blocked);
if (displayName != null) {
tag.setString(NBT_DISPLAY_NAME, displayName);
}
if (uuid != null) {
tag.setUniqueId(NBT_UUID, uuid);
}
return tag;
}
@@ -156,8 +174,21 @@ public class NetworkNodeCrafter extends NetworkNode implements ICraftingPatternC
}
@Override
public IItemHandler getFacingInventory() {
return WorldUtils.getItemHandler(getFacingTile(), getDirection().getOpposite());
public IItemHandler getConnectedInventory() {
ICraftingPatternContainer proxy = getRootContainer();
if (proxy == null) {
return null;
}
return WorldUtils.getItemHandler(proxy.getFacingTile(), proxy.getDirection().getOpposite());
}
@Override
public TileEntity getConnectedTile() {
ICraftingPatternContainer proxy = getRootContainer();
if (proxy == null) {
return null;
}
return proxy.getFacingTile();
}
@Override
@@ -177,7 +208,7 @@ public class NetworkNodeCrafter extends NetworkNode implements ICraftingPatternC
return displayName;
}
TileEntity facing = getFacingTile();
TileEntity facing = getConnectedTile();
if (facing instanceof IWorldNameable) {
return ((IWorldNameable) facing).getName();
@@ -223,14 +254,35 @@ public class NetworkNodeCrafter extends NetworkNode implements ICraftingPatternC
}
@Override
public boolean isBlocked() {
return blocked;
@Nullable
public ICraftingPatternContainer getRootContainer() {
if (visited) {
return null;
}
INetworkNode facing = API.instance().getNetworkNodeManager(world).getNode(pos.offset(getDirection()));
if (!(facing instanceof ICraftingPatternContainer) || facing.getNetwork() != network) {
return this;
}
visited = true;
ICraftingPatternContainer facingContainer = ((ICraftingPatternContainer)facing).getRootContainer();
visited = false;
return facingContainer;
}
@Override
public void setBlocked(boolean blocked) {
this.blocked = blocked;
public boolean isBlocked() {
ICraftingPatternContainer proxy = getRootContainer();
return proxy != null && network != null && network.getCraftingManager().isContainerBlocked(proxy.getUuid());
}
@Override
public UUID getUuid() {
if (uuid == null) {
uuid = UUID.randomUUID();
markDirty();
}
return uuid;
}
}