pass active crafts when started

This commit is contained in:
Ell 2024-11-28 11:55:55 +01:00
parent 805393ba6e
commit 0eb8740fde
7 changed files with 151 additions and 129 deletions

View file

@ -2,6 +2,7 @@ package de.ellpeck.prettypipes.items;
import de.ellpeck.prettypipes.misc.DirectionSelector;
import de.ellpeck.prettypipes.misc.ItemFilter;
import de.ellpeck.prettypipes.network.ActiveCraft;
import de.ellpeck.prettypipes.pipe.PipeBlockEntity;
import de.ellpeck.prettypipes.pipe.containers.AbstractPipeContainer;
import net.minecraft.core.BlockPos;
@ -10,7 +11,9 @@ import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.neoforged.neoforge.items.IItemHandler;
import org.apache.commons.lang3.tuple.Pair;
import java.util.Collection;
import java.util.List;
import java.util.Stack;
import java.util.function.Consumer;
@ -41,7 +44,7 @@ public interface IModule {
int getCraftableAmount(ItemStack module, PipeBlockEntity tile, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain);
ItemStack craft(ItemStack module, PipeBlockEntity tile, BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain);
Pair<ItemStack, Collection<ActiveCraft>> craft(ItemStack module, PipeBlockEntity tile, BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain);
Integer getCustomNextNode(ItemStack module, PipeBlockEntity tile, List<BlockPos> nodes, int index);

View file

@ -3,6 +3,7 @@ package de.ellpeck.prettypipes.items;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.misc.DirectionSelector;
import de.ellpeck.prettypipes.misc.ItemFilter;
import de.ellpeck.prettypipes.network.ActiveCraft;
import de.ellpeck.prettypipes.pipe.PipeBlockEntity;
import de.ellpeck.prettypipes.pipe.containers.AbstractPipeContainer;
import net.minecraft.core.BlockPos;
@ -16,7 +17,9 @@ import net.minecraft.world.item.TooltipFlag;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import net.neoforged.neoforge.items.IItemHandler;
import org.apache.commons.lang3.tuple.Pair;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
@ -89,8 +92,8 @@ public abstract class ModuleItem extends Item implements IModule {
}
@Override
public ItemStack craft(ItemStack module, PipeBlockEntity tile, BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain) {
return stack;
public Pair<ItemStack, Collection<ActiveCraft>> craft(ItemStack module, PipeBlockEntity tile, BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain) {
return Pair.of(stack, List.of());
}
@Override

View file

@ -0,0 +1,77 @@
package de.ellpeck.prettypipes.network;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.misc.ItemEquality;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.item.ItemStack;
import net.neoforged.neoforge.common.util.INBTSerializable;
import org.jetbrains.annotations.UnknownNullability;
import java.util.ArrayList;
import java.util.List;
public class ActiveCraft implements INBTSerializable<CompoundTag> {
public List<ItemStack> travelingIngredients = new ArrayList<>();
public List<NetworkLock> ingredientsToRequest;
public BlockPos resultDestPipe;
public ItemStack resultStackRemain;
public boolean inProgress;
// we only remove canceled requests from the queue once their items are fully delivered to the crafting location, so that unfinished recipes don't get stuck in crafters etc.
public boolean canceled;
public ActiveCraft(List<NetworkLock> ingredientsToRequest, BlockPos resultDestPipe, ItemStack resultStackRemain) {
this.ingredientsToRequest = ingredientsToRequest;
this.resultDestPipe = resultDestPipe;
this.resultStackRemain = resultStackRemain;
}
public ActiveCraft(HolderLookup.Provider provider, CompoundTag tag) {
this.deserializeNBT(provider, tag);
}
@Override
public @UnknownNullability CompoundTag serializeNBT(HolderLookup.Provider provider) {
var ret = new CompoundTag();
ret.put("ingredients_to_request", Utility.serializeAll(this.ingredientsToRequest, n -> n.serializeNBT(provider)));
ret.put("traveling_ingredients", Utility.serializeAll(this.travelingIngredients, s -> (CompoundTag) s.save(provider, new CompoundTag())));
ret.putLong("result_dest_pipe", this.resultDestPipe.asLong());
ret.put("result_stack_remain", this.resultStackRemain.saveOptional(provider));
ret.putBoolean("in_progress", this.inProgress);
ret.putBoolean("canceled", this.canceled);
return ret;
}
@Override
public void deserializeNBT(HolderLookup.Provider provider, CompoundTag nbt) {
this.ingredientsToRequest = Utility.deserializeAll(nbt.getList("ingredients_to_request", Tag.TAG_COMPOUND), t -> new NetworkLock(provider, t));
this.travelingIngredients = Utility.deserializeAll(nbt.getList("traveling_ingredients", Tag.TAG_COMPOUND), t -> ItemStack.parse(provider, t).orElseThrow());
this.resultDestPipe = BlockPos.of(nbt.getLong("result_dest_pipe"));
this.resultStackRemain = ItemStack.parseOptional(provider, nbt.getCompound("result_stack_remain"));
this.inProgress = nbt.getBoolean("in_progress");
this.canceled = nbt.getBoolean("canceled");
}
public ItemStack getTravelingIngredient(ItemStack stack, ItemEquality... equalityTypes) {
for (var traveling : this.travelingIngredients) {
if (ItemEquality.compareItems(stack, traveling, equalityTypes))
return traveling;
}
return ItemStack.EMPTY;
}
public boolean markCanceledOrResolve(PipeNetwork network) {
if (this.inProgress) {
this.canceled = true;
return false;
} else {
for (var lock : this.ingredientsToRequest)
network.resolveNetworkLock(lock);
return true;
}
}
}

View file

@ -26,6 +26,7 @@ import net.minecraft.world.level.saveddata.SavedData;
import net.neoforged.neoforge.items.IItemHandler;
import net.neoforged.neoforge.network.PacketDistributor;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.jgrapht.ListenableGraph;
import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
import org.jgrapht.event.GraphEdgeChangeEvent;
@ -213,21 +214,58 @@ public class PipeNetwork extends SavedData implements GraphListener<BlockPos, Ne
return remain;
}
// check craftable items
return this.requestCraftedItem(destPipe, null, remain, new Stack<>(), equalityTypes);
return this.requestCraftedItem(destPipe, null, remain, new Stack<>(), equalityTypes).getLeft();
}
public ItemStack requestCraftedItem(BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain, ItemEquality... equalityTypes) {
public Triple<List<NetworkLock>, ItemStack, Collection<ActiveCraft>> requestLocksAndCrafts(BlockPos destPipe, Collection<NetworkLocation> locations, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain, ItemEquality... equalityTypes) {
List<NetworkLock> requests = new ArrayList<>();
var remain = stack.copy();
// check for existing items
for (var location : locations) {
var amount = location.getItemAmount(this.level, stack, equalityTypes);
if (amount <= 0)
continue;
amount -= this.getLockedAmount(location.getPos(), stack, null, equalityTypes);
if (amount > 0) {
if (remain.getCount() < amount)
amount = remain.getCount();
remain.shrink(amount);
while (amount > 0) {
var copy = stack.copy();
copy.setCount(Math.min(stack.getMaxStackSize(), amount));
var lock = new NetworkLock(location, copy);
this.createNetworkLock(lock);
requests.add(lock);
amount -= copy.getCount();
}
if (remain.isEmpty())
break;
}
}
if (!remain.isEmpty()) {
// check for craftable items
var started = this.requestCraftedItem(destPipe, unavailableConsumer, remain, dependencyChain, equalityTypes);
return Triple.of(requests, started.getLeft(), started.getRight());
} else {
return Triple.of(requests, remain, List.of());
}
}
public Pair<ItemStack, Collection<ActiveCraft>> requestCraftedItem(BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain, ItemEquality... equalityTypes) {
var crafts = new ArrayList<ActiveCraft>();
for (var craftable : this.getAllCraftables(destPipe)) {
if (!ItemEquality.compareItems(stack, craftable.getRight(), equalityTypes))
continue;
var pipe = this.getPipe(craftable.getLeft());
if (pipe == null)
continue;
stack = pipe.craft(destPipe, unavailableConsumer, stack, dependencyChain);
var started = pipe.craft(destPipe, unavailableConsumer, stack, dependencyChain);
stack = started.getLeft();
crafts.addAll(started.getRight());
if (stack.isEmpty())
break;
}
return stack;
return Pair.of(stack, crafts);
}
public ItemStack requestExistingItem(NetworkLocation location, BlockPos destPipe, BlockPos destInventory, NetworkLock ignoredLock, ItemStack stack, ItemEquality... equalityTypes) {

View file

@ -5,10 +5,9 @@ import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.items.IModule;
import de.ellpeck.prettypipes.misc.ItemFilter;
import de.ellpeck.prettypipes.network.NetworkLock;
import de.ellpeck.prettypipes.network.ActiveCraft;
import de.ellpeck.prettypipes.network.PipeNetwork;
import de.ellpeck.prettypipes.pipe.containers.MainPipeContainer;
import de.ellpeck.prettypipes.pipe.modules.craft.CraftingModuleItem;
import de.ellpeck.prettypipes.pressurizer.PressurizerBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@ -42,7 +41,6 @@ import net.neoforged.neoforge.items.IItemHandler;
import net.neoforged.neoforge.items.ItemHandlerHelper;
import net.neoforged.neoforge.items.ItemStackHandler;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -72,7 +70,7 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC
PipeBlockEntity.this.setChanged();
}
};
public final List<Pair<Integer, CraftingModuleItem.ActiveCraft>> activeCrafts = new ArrayList<>();
public final List<Pair<Integer, ActiveCraft>> activeCrafts = new ArrayList<>();
public PressurizerBlockEntity pressurizer;
public BlockState cover;
public int moduleDropCheck;
@ -120,7 +118,7 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC
var crafts = compound.getList("active_crafts", Tag.TAG_COMPOUND);
for (var i = 0; i < crafts.size(); i++) {
var tag = crafts.getCompound(i);
this.activeCrafts.add(Pair.of(tag.getInt("module_slot"), new CraftingModuleItem.ActiveCraft(provider, tag.getCompound("data"))));
this.activeCrafts.add(Pair.of(tag.getInt("module_slot"), new ActiveCraft(provider, tag.getCompound("data"))));
}
super.loadAdditional(compound, provider);
}
@ -296,15 +294,18 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC
return total;
}
public ItemStack craft(BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain) {
public Pair<ItemStack, Collection<ActiveCraft>> craft(BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain) {
var crafts = new ArrayList<ActiveCraft>();
var modules = this.streamModules().iterator();
while (modules.hasNext()) {
var module = modules.next();
stack = module.getRight().craft(module.getLeft(), this, destPipe, unavailableConsumer, stack, dependencyChain);
var started = module.getRight().craft(module.getLeft(), this, destPipe, unavailableConsumer, stack, dependencyChain);
stack = started.getLeft();
crafts.addAll(started.getRight());
if (stack.isEmpty())
break;
}
return stack;
return Pair.of(stack, crafts);
}
public IItemHandler getItemHandler(Direction dir) {

View file

@ -7,9 +7,9 @@ import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.items.IModule;
import de.ellpeck.prettypipes.items.ModuleItem;
import de.ellpeck.prettypipes.items.ModuleTier;
import de.ellpeck.prettypipes.misc.EquatableItemStack;
import de.ellpeck.prettypipes.misc.ItemEquality;
import de.ellpeck.prettypipes.misc.ItemFilter;
import de.ellpeck.prettypipes.network.ActiveCraft;
import de.ellpeck.prettypipes.network.NetworkLock;
import de.ellpeck.prettypipes.network.PipeNetwork;
import de.ellpeck.prettypipes.pipe.PipeBlockEntity;
@ -18,21 +18,15 @@ import de.ellpeck.prettypipes.terminal.CraftingTerminalBlockEntity;
import de.ellpeck.prettypipes.terminal.ItemTerminalBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.neoforged.neoforge.common.util.INBTSerializable;
import net.neoforged.neoforge.items.IItemHandler;
import net.neoforged.neoforge.items.ItemHandlerHelper;
import net.neoforged.neoforge.items.ItemStackHandler;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.jetbrains.annotations.UnknownNullability;
import java.util.*;
import java.util.function.Consumer;
@ -174,11 +168,11 @@ public class CraftingModuleItem extends ModuleItem {
}
@Override
public ItemStack craft(ItemStack module, PipeBlockEntity tile, BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain) {
public Pair<ItemStack, Collection<ActiveCraft>> craft(ItemStack module, PipeBlockEntity tile, BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain) {
// check if we can craft the required amount of items
var craftableAmount = this.getCraftableAmount(module, tile, unavailableConsumer, stack, dependencyChain);
if (craftableAmount <= 0)
return stack;
return Pair.of(stack, List.of());
var slot = tile.getModuleSlot(module);
var network = PipeNetwork.get(tile.getLevel());
@ -192,6 +186,7 @@ public class CraftingModuleItem extends ModuleItem {
var toCraft = Math.min(craftableCrafts, requiredCrafts);
var locks = new ArrayList<NetworkLock>();
var crafts = new ArrayList<ActiveCraft>();
var contents = module.get(Contents.TYPE);
// if we're ensuring item order, all items for a single recipe should be sent in order first before starting on the next one!
for (var c = contents.ensureItemOrder ? toCraft : 1; c > 0; c--) {
@ -202,8 +197,9 @@ public class CraftingModuleItem extends ModuleItem {
var copy = in.copy();
if (!contents.ensureItemOrder)
copy.setCount(in.getCount() * toCraft);
var ret = ItemTerminalBlockEntity.requestItemLater(tile.getLevel(), tile.getBlockPos(), items, unavailableConsumer, copy, CraftingModuleItem.addDependency(dependencyChain, module), equalityTypes);
var ret = network.requestLocksAndCrafts(tile.getBlockPos(), items, unavailableConsumer, copy, CraftingModuleItem.addDependency(dependencyChain, module), equalityTypes);
locks.addAll(ret.getLeft());
crafts.addAll(ret.getRight());
}
}
@ -212,10 +208,11 @@ public class CraftingModuleItem extends ModuleItem {
var result = stack.copy();
result.shrink(remain.getCount());
var activeCraft = new ActiveCraft(locks, new ArrayList<>(), destPipe, result);
var activeCraft = new ActiveCraft(locks, destPipe, result);
tile.activeCrafts.add(Pair.of(slot, activeCraft));
crafts.add(activeCraft);
return remain;
return Pair.of(remain, crafts);
}
@Override
@ -277,68 +274,4 @@ public class CraftingModuleItem extends ModuleItem {
}
public static class ActiveCraft implements INBTSerializable<CompoundTag> {
public List<NetworkLock> ingredientsToRequest;
public List<ItemStack> travelingIngredients;
public BlockPos resultDestPipe;
public ItemStack resultStackRemain;
public boolean inProgress;
// we only remove canceled requests from the queue once their items are fully delivered to the crafting location, so that unfinished recipes don't get stuck in crafters etc.
public boolean canceled;
public ActiveCraft(List<NetworkLock> ingredientsToRequest, List<ItemStack> travelingIngredients, BlockPos resultDestPipe, ItemStack resultStackRemain) {
this.ingredientsToRequest = ingredientsToRequest;
this.travelingIngredients = travelingIngredients;
this.resultDestPipe = resultDestPipe;
this.resultStackRemain = resultStackRemain;
}
public ActiveCraft(HolderLookup.Provider provider, CompoundTag tag) {
this.deserializeNBT(provider, tag);
}
@Override
public @UnknownNullability CompoundTag serializeNBT(HolderLookup.Provider provider) {
var ret = new CompoundTag();
ret.put("ingredients_to_request", Utility.serializeAll(this.ingredientsToRequest, n -> n.serializeNBT(provider)));
ret.put("traveling_ingredients", Utility.serializeAll(this.travelingIngredients, s -> (CompoundTag) s.save(provider, new CompoundTag())));
ret.putLong("result_dest_pipe", this.resultDestPipe.asLong());
ret.put("result_stack_remain", this.resultStackRemain.saveOptional(provider));
ret.putBoolean("in_progress", this.inProgress);
ret.putBoolean("canceled", this.canceled);
return ret;
}
@Override
public void deserializeNBT(HolderLookup.Provider provider, CompoundTag nbt) {
this.ingredientsToRequest = Utility.deserializeAll(nbt.getList("ingredients_to_request", Tag.TAG_COMPOUND), t -> new NetworkLock(provider, t));
this.travelingIngredients = Utility.deserializeAll(nbt.getList("traveling_ingredients", Tag.TAG_COMPOUND), t -> ItemStack.parse(provider, t).orElseThrow());
this.resultDestPipe = BlockPos.of(nbt.getLong("result_dest_pipe"));
this.resultStackRemain = ItemStack.parseOptional(provider, nbt.getCompound("result_stack_remain"));
this.inProgress = nbt.getBoolean("in_progress");
this.canceled = nbt.getBoolean("canceled");
}
public ItemStack getTravelingIngredient(ItemStack stack, ItemEquality... equalityTypes) {
for (var traveling : this.travelingIngredients) {
if (ItemEquality.compareItems(stack, traveling, equalityTypes))
return traveling;
}
return ItemStack.EMPTY;
}
public boolean markCanceledOrResolve(PipeNetwork network) {
if (this.inProgress) {
this.canceled = true;
return false;
} else {
for (var lock : this.ingredientsToRequest)
network.resolveNetworkLock(lock);
return true;
}
}
}
}

View file

@ -5,10 +5,7 @@ import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.misc.EquatableItemStack;
import de.ellpeck.prettypipes.misc.ItemEquality;
import de.ellpeck.prettypipes.network.NetworkItem;
import de.ellpeck.prettypipes.network.NetworkLocation;
import de.ellpeck.prettypipes.network.NetworkLock;
import de.ellpeck.prettypipes.network.PipeNetwork;
import de.ellpeck.prettypipes.network.*;
import de.ellpeck.prettypipes.packets.PacketNetworkItems;
import de.ellpeck.prettypipes.pipe.ConnectionType;
import de.ellpeck.prettypipes.pipe.IPipeConnectable;
@ -35,6 +32,7 @@ import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.neoforge.items.ItemHandlerHelper;
import net.neoforged.neoforge.items.ItemStackHandler;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -172,9 +170,10 @@ public class ItemTerminalBlockEntity extends BlockEntity implements IPipeConnect
public int requestItemImpl(ItemStack stack, Consumer<ItemStack> unavailableConsumer) {
var item = this.networkItems.get(new EquatableItemStack(stack, ItemEquality.NBT));
Collection<NetworkLocation> locations = item == null ? Collections.emptyList() : item.getLocations();
var ret = ItemTerminalBlockEntity.requestItemLater(this.level, this.getConnectedPipe().getBlockPos(), locations, unavailableConsumer, stack, new Stack<>(), ItemEquality.NBT);
var network = PipeNetwork.get(this.level);
var ret = network.requestLocksAndCrafts(this.getConnectedPipe().getBlockPos(), locations, unavailableConsumer, stack, new Stack<>(), ItemEquality.NBT);
this.existingRequests.addAll(ret.getLeft());
return stack.getCount() - ret.getRight().getCount();
return stack.getCount() - ret.getMiddle().getCount();
}
public Player[] getLookingPlayers() {
@ -269,38 +268,6 @@ public class ItemTerminalBlockEntity extends BlockEntity implements IPipeConnect
return true;
}
public static Pair<List<NetworkLock>, ItemStack> requestItemLater(Level world, BlockPos destPipe, Collection<NetworkLocation> locations, Consumer<ItemStack> unavailableConsumer, ItemStack stack, Stack<ItemStack> dependencyChain, ItemEquality... equalityTypes) {
List<NetworkLock> requests = new ArrayList<>();
var remain = stack.copy();
var network = PipeNetwork.get(world);
// check for existing items
for (var location : locations) {
var amount = location.getItemAmount(world, stack, equalityTypes);
if (amount <= 0)
continue;
amount -= network.getLockedAmount(location.getPos(), stack, null, equalityTypes);
if (amount > 0) {
if (remain.getCount() < amount)
amount = remain.getCount();
remain.shrink(amount);
while (amount > 0) {
var copy = stack.copy();
copy.setCount(Math.min(stack.getMaxStackSize(), amount));
var lock = new NetworkLock(location, copy);
network.createNetworkLock(lock);
requests.add(lock);
amount -= copy.getCount();
}
if (remain.isEmpty())
break;
}
}
// check for craftable items
if (!remain.isEmpty())
remain = network.requestCraftedItem(destPipe, unavailableConsumer, remain, dependencyChain, equalityTypes);
return Pair.of(requests, remain);
}
public static Consumer<ItemStack> onItemUnavailable(Player player, boolean ignore) {
return s -> {
if (ignore)