From e85cda1b0a5d2da9619ce177533286808865dd4e Mon Sep 17 00:00:00 2001 From: tomKPZ Date: Mon, 23 Apr 2018 22:34:56 -0700 Subject: [PATCH] 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 --- CHANGELOG.md | 3 + .../api/autocrafting/ICraftingManager.java | 24 ++++++ .../ICraftingPatternContainer.java | 34 ++++++-- .../apiimpl/autocrafting/CraftingManager.java | 58 ++++++++++++- .../autocrafting/task/CraftingStep.java | 4 +- .../task/CraftingStepProcess.java | 6 +- .../autocrafting/task/CraftingTask.java | 4 +- .../apiimpl/network/node/NetworkNode.java | 10 ++- .../network/node/NetworkNodeCrafter.java | 84 +++++++++++++++---- 9 files changed, 195 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efbd354bb..167b798f4 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/src/main/java/com/raoulvdberge/refinedstorage/api/autocrafting/ICraftingManager.java b/src/main/java/com/raoulvdberge/refinedstorage/api/autocrafting/ICraftingManager.java index 36127e811..2f46c70d1 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/api/autocrafting/ICraftingManager.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/api/autocrafting/ICraftingManager.java @@ -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> 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. * diff --git a/src/main/java/com/raoulvdberge/refinedstorage/api/autocrafting/ICraftingPatternContainer.java b/src/main/java/com/raoulvdberge/refinedstorage/api/autocrafting/ICraftingPatternContainer.java index 30899c5c8..701d49533 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/api/autocrafting/ICraftingPatternContainer.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/api/autocrafting/ICraftingPatternContainer.java @@ -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(); } diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/CraftingManager.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/CraftingManager.java index 345769489..f8cdcc61a 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/CraftingManager.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/CraftingManager.java @@ -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> containerInventories = new LinkedHashMap<>(); private CraftingPatternChainList patterns = new CraftingPatternChainList(); + // A map of blockers to blockees. + private Map blockingContainers = new HashMap<>(); + // A set of blockees. + private Set blockedContainers = new HashSet<>(); + private List craftingTasks = new ArrayList<>(); private List craftingTasksToAdd = new ArrayList<>(); private List 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 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; } diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/task/CraftingStep.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/task/CraftingStep.java index 9c92059e1..f86adf8c3 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/task/CraftingStep.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/task/CraftingStep.java @@ -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; diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/task/CraftingStepProcess.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/task/CraftingStepProcess.java index 12c2401bb..af1b4c5e3 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/task/CraftingStepProcess.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/task/CraftingStepProcess.java @@ -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); diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/task/CraftingTask.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/task/CraftingTask.java index 52f13df29..5bc3044ec 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/task/CraftingTask.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/autocrafting/task/CraftingTask.java @@ -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"); diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/NetworkNode.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/NetworkNode.java index 7a397aef6..36161b739 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/NetworkNode.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/NetworkNode.java @@ -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 diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/NetworkNodeCrafter.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/NetworkNodeCrafter.java index 9c6a2881c..04a6ba8f4 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/NetworkNodeCrafter.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/NetworkNodeCrafter.java @@ -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()); + } - markDirty(); + @Override + public UUID getUuid() { + if (uuid == null) { + uuid = UUID.randomUUID(); + markDirty(); + } + return uuid; } }