store active crafts globally in the network (and add a command to clear all)

This commit is contained in:
Ell 2024-11-28 12:15:44 +01:00
parent 0942be0725
commit 1b0c36d5e5
7 changed files with 100 additions and 83 deletions

View file

@ -43,6 +43,12 @@ public final class Events {
PipeNetwork.get(source.getLevel()).unlock(); PipeNetwork.get(source.getLevel()).unlock();
source.sendSuccess(() -> Component.literal("Resolved all network locks in the world"), true); source.sendSuccess(() -> Component.literal("Resolved all network locks in the world"), true);
return 0; return 0;
}))
.then(Commands.literal("uncraft").executes(c -> {
var source = c.getSource();
PipeNetwork.get(source.getLevel()).cancelCrafts();
source.sendSuccess(() -> Component.literal("Canceled all active crafts in the world"), true);
return 0;
}))); })));
} }

View file

@ -15,6 +15,8 @@ import java.util.List;
public class ActiveCraft implements INBTSerializable<CompoundTag> { public class ActiveCraft implements INBTSerializable<CompoundTag> {
public BlockPos pipe;
public int moduleSlot;
public List<ItemStack> travelingIngredients = new ArrayList<>(); public List<ItemStack> travelingIngredients = new ArrayList<>();
public List<NetworkLock> ingredientsToRequest; public List<NetworkLock> ingredientsToRequest;
public BlockPos resultDestPipe; public BlockPos resultDestPipe;
@ -23,7 +25,9 @@ public class ActiveCraft implements INBTSerializable<CompoundTag> {
// 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. // 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 boolean canceled;
public ActiveCraft(List<NetworkLock> ingredientsToRequest, BlockPos resultDestPipe, ItemStack resultStackRemain) { public ActiveCraft(BlockPos pipe, int moduleSlot, List<NetworkLock> ingredientsToRequest, BlockPos resultDestPipe, ItemStack resultStackRemain) {
this.pipe = pipe;
this.moduleSlot = moduleSlot;
this.ingredientsToRequest = ingredientsToRequest; this.ingredientsToRequest = ingredientsToRequest;
this.resultDestPipe = resultDestPipe; this.resultDestPipe = resultDestPipe;
this.resultStackRemain = resultStackRemain; this.resultStackRemain = resultStackRemain;
@ -36,6 +40,8 @@ public class ActiveCraft implements INBTSerializable<CompoundTag> {
@Override @Override
public @UnknownNullability CompoundTag serializeNBT(HolderLookup.Provider provider) { public @UnknownNullability CompoundTag serializeNBT(HolderLookup.Provider provider) {
var ret = new CompoundTag(); var ret = new CompoundTag();
ret.putLong("pipe", this.pipe.asLong());
ret.putInt("module_slot", this.moduleSlot);
ret.put("ingredients_to_request", Utility.serializeAll(this.ingredientsToRequest, n -> n.serializeNBT(provider))); 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.put("traveling_ingredients", Utility.serializeAll(this.travelingIngredients, s -> (CompoundTag) s.save(provider, new CompoundTag())));
ret.putLong("result_dest_pipe", this.resultDestPipe.asLong()); ret.putLong("result_dest_pipe", this.resultDestPipe.asLong());
@ -47,6 +53,8 @@ public class ActiveCraft implements INBTSerializable<CompoundTag> {
@Override @Override
public void deserializeNBT(HolderLookup.Provider provider, CompoundTag nbt) { public void deserializeNBT(HolderLookup.Provider provider, CompoundTag nbt) {
this.pipe = BlockPos.of(nbt.getLong("pipe"));
this.moduleSlot = nbt.getInt("module_slot");
this.ingredientsToRequest = Utility.deserializeAll(nbt.getList("ingredients_to_request", Tag.TAG_COMPOUND), t -> new NetworkLock(provider, t)); 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.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.resultDestPipe = BlockPos.of(nbt.getLong("result_dest_pipe"));

View file

@ -55,6 +55,7 @@ public class PipeNetwork extends SavedData implements GraphListener<BlockPos, Ne
private final Map<BlockPos, PipeBlockEntity> tileCache = new HashMap<>(); private final Map<BlockPos, PipeBlockEntity> tileCache = new HashMap<>();
private final ListMultimap<BlockPos, IPipeItem> pipeItems = ArrayListMultimap.create(); private final ListMultimap<BlockPos, IPipeItem> pipeItems = ArrayListMultimap.create();
private final ListMultimap<BlockPos, NetworkLock> networkLocks = ArrayListMultimap.create(); private final ListMultimap<BlockPos, NetworkLock> networkLocks = ArrayListMultimap.create();
private final ListMultimap<BlockPos, ActiveCraft> activeCrafts = ArrayListMultimap.create();
private Level level; private Level level;
public PipeNetwork() { public PipeNetwork() {
@ -74,6 +75,8 @@ public class PipeNetwork extends SavedData implements GraphListener<BlockPos, Ne
this.pipeItems.put(item.getCurrentPipe(), item); this.pipeItems.put(item.getCurrentPipe(), item);
for (var lock : Utility.deserializeAll(nbt.getList("locks", Tag.TAG_COMPOUND), t -> new NetworkLock(provider, t))) for (var lock : Utility.deserializeAll(nbt.getList("locks", Tag.TAG_COMPOUND), t -> new NetworkLock(provider, t)))
this.createNetworkLock(lock); this.createNetworkLock(lock);
for (var craft : Utility.deserializeAll(nbt.getList("crafts", Tag.TAG_COMPOUND), t -> new ActiveCraft(provider, t)))
this.activeCrafts.put(craft.pipe, craft);
} }
@Override @Override
@ -121,6 +124,7 @@ public class PipeNetwork extends SavedData implements GraphListener<BlockPos, Ne
nbt.put("edges", edges); nbt.put("edges", edges);
nbt.put("items", Utility.serializeAll(this.pipeItems.values(), i -> i.serializeNBT(provider))); nbt.put("items", Utility.serializeAll(this.pipeItems.values(), i -> i.serializeNBT(provider)));
nbt.put("locks", Utility.serializeAll(this.networkLocks.values(), l -> l.serializeNBT(provider))); nbt.put("locks", Utility.serializeAll(this.networkLocks.values(), l -> l.serializeNBT(provider)));
nbt.put("crafts", Utility.serializeAll(this.activeCrafts.values(), c -> c.serializeNBT(provider)));
return nbt; return nbt;
} }
@ -321,18 +325,17 @@ public class PipeNetwork extends SavedData implements GraphListener<BlockPos, Ne
var craftingPipes = this.getAllCraftables(node).stream().map(c -> this.getPipe(c.getLeft())).distinct().iterator(); var craftingPipes = this.getAllCraftables(node).stream().map(c -> this.getPipe(c.getLeft())).distinct().iterator();
while (craftingPipes.hasNext()) { while (craftingPipes.hasNext()) {
var pipe = craftingPipes.next(); var pipe = craftingPipes.next();
for (var craft : pipe.activeCrafts) { for (var craft : pipe.getActiveCrafts()) {
var data = craft.getRight(); if (!includeCanceled && craft.canceled)
if (!includeCanceled && data.canceled)
continue; continue;
// add up all the items that should go to the same location // add up all the items that should go to the same location
var existing = items.stream() var existing = items.stream()
.filter(s -> s.getLeft().equals(data.resultDestPipe) && ItemEquality.compareItems(s.getRight(), data.resultStackRemain, equalityTypes)) .filter(s -> s.getLeft().equals(craft.resultDestPipe) && ItemEquality.compareItems(s.getRight(), craft.resultStackRemain, equalityTypes))
.findFirst(); .findFirst();
if (existing.isPresent()) { if (existing.isPresent()) {
existing.get().getRight().grow(data.resultStackRemain.getCount()); existing.get().getRight().grow(craft.resultStackRemain.getCount());
} else { } else {
items.add(Pair.of(data.resultDestPipe, data.resultStackRemain.copy())); items.add(Pair.of(craft.resultDestPipe, craft.resultStackRemain.copy()));
} }
} }
} }
@ -456,6 +459,10 @@ public class PipeNetwork extends SavedData implements GraphListener<BlockPos, Ne
this.networkLocks.clear(); this.networkLocks.clear();
} }
public void cancelCrafts() {
this.activeCrafts.entries().removeIf(c -> c.getValue().markCanceledOrResolve(this));
}
private List<NetworkEdge> createAllEdges(BlockPos pos, BlockState state, boolean ignoreCurrBlocked) { private List<NetworkEdge> createAllEdges(BlockPos pos, BlockState state, boolean ignoreCurrBlocked) {
this.startProfile("create_all_edges"); this.startProfile("create_all_edges");
List<NetworkEdge> edges = new ArrayList<>(); List<NetworkEdge> edges = new ArrayList<>();
@ -534,7 +541,7 @@ public class PipeNetwork extends SavedData implements GraphListener<BlockPos, Ne
this.startProfile("clear_node_cache"); this.startProfile("clear_node_cache");
// remove caches for the nodes // remove caches for the nodes
for (var node : nodes) for (var node : nodes)
this.nodeToConnectedNodes.keySet().remove(node); this.nodeToConnectedNodes.remove(node);
// remove caches that contain the nodes as a destination // remove caches that contain the nodes as a destination
this.nodeToConnectedNodes.values().removeIf(cached -> nodes.stream().anyMatch(cached::contains)); this.nodeToConnectedNodes.values().removeIf(cached -> nodes.stream().anyMatch(cached::contains));
this.endProfile(); this.endProfile();
@ -544,6 +551,10 @@ public class PipeNetwork extends SavedData implements GraphListener<BlockPos, Ne
return this.pipeItems.get(pos); return this.pipeItems.get(pos);
} }
public List<ActiveCraft> getActiveCrafts(BlockPos pos) {
return this.activeCrafts.get(pos);
}
public Stream<IPipeItem> getPipeItemsOnTheWay(BlockPos goalInv) { public Stream<IPipeItem> getPipeItemsOnTheWay(BlockPos goalInv) {
this.startProfile("get_pipe_items_on_the_way"); this.startProfile("get_pipe_items_on_the_way");
var ret = this.pipeItems.values().stream().filter(i -> i.getDestInventory().equals(goalInv)); var ret = this.pipeItems.values().stream().filter(i -> i.getDestInventory().equals(goalInv));

View file

@ -270,8 +270,8 @@ public class PipeBlock extends BaseEntityBlock implements SimpleWaterloggedBlock
network.onPipeChanged(pos, state); network.onPipeChanged(pos, state);
if (worldIn.getBlockEntity(pos) instanceof PipeBlockEntity pipe) { if (worldIn.getBlockEntity(pos) instanceof PipeBlockEntity pipe) {
pipe.getItems().clear(); pipe.getItems().clear();
for (var craft : pipe.activeCrafts) { for (var craft : pipe.getActiveCrafts()) {
for (var lock : craft.getRight().ingredientsToRequest) for (var lock : craft.ingredientsToRequest)
network.resolveNetworkLock(lock); network.resolveNetworkLock(lock);
} }
} }

View file

@ -70,11 +70,11 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC
PipeBlockEntity.this.setChanged(); PipeBlockEntity.this.setChanged();
} }
}; };
public final List<Pair<Integer, ActiveCraft>> activeCrafts = new ArrayList<>();
public PressurizerBlockEntity pressurizer; public PressurizerBlockEntity pressurizer;
public BlockState cover; public BlockState cover;
public int moduleDropCheck; public int moduleDropCheck;
protected List<IPipeItem> items; private List<IPipeItem> itemCache;
private List<ActiveCraft> activeCraftCache;
private int lastItemAmount; private int lastItemAmount;
private int priority; private int priority;
private final Lazy<Integer> workRandomizer = Lazy.of(() -> this.level.random.nextInt(200)); private final Lazy<Integer> workRandomizer = Lazy.of(() -> this.level.random.nextInt(200));
@ -99,14 +99,6 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC
compound.putInt("module_drop_check", this.moduleDropCheck); compound.putInt("module_drop_check", this.moduleDropCheck);
if (this.cover != null) if (this.cover != null)
compound.put("cover", NbtUtils.writeBlockState(this.cover)); compound.put("cover", NbtUtils.writeBlockState(this.cover));
var crafts = new ListTag();
for (var craft : this.activeCrafts) {
var tag = new CompoundTag();
tag.putInt("module_slot", craft.getLeft());
tag.put("data", craft.getRight().serializeNBT(provider));
crafts.add(tag);
}
compound.put("active_crafts", crafts);
} }
@Override @Override
@ -114,12 +106,6 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC
this.modules.deserializeNBT(provider, compound.getCompound("modules")); this.modules.deserializeNBT(provider, compound.getCompound("modules"));
this.moduleDropCheck = compound.getInt("module_drop_check"); this.moduleDropCheck = compound.getInt("module_drop_check");
this.cover = compound.contains("cover") ? NbtUtils.readBlockState(this.level != null ? this.level.holderLookup(Registries.BLOCK) : BuiltInRegistries.BLOCK.asLookup(), compound.getCompound("cover")) : null; this.cover = compound.contains("cover") ? NbtUtils.readBlockState(this.level != null ? this.level.holderLookup(Registries.BLOCK) : BuiltInRegistries.BLOCK.asLookup(), compound.getCompound("cover")) : null;
this.activeCrafts.clear();
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 ActiveCraft(provider, tag.getCompound("data"))));
}
super.loadAdditional(compound, provider); super.loadAdditional(compound, provider);
} }
@ -145,9 +131,15 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC
} }
public List<IPipeItem> getItems() { public List<IPipeItem> getItems() {
if (this.items == null) if (this.itemCache == null)
this.items = PipeNetwork.get(this.level).getItemsInPipe(this.worldPosition); this.itemCache = PipeNetwork.get(this.level).getItemsInPipe(this.worldPosition);
return this.items; return this.itemCache;
}
public List<ActiveCraft> getActiveCrafts() {
if (this.activeCraftCache == null)
this.activeCraftCache = PipeNetwork.get(this.level).getActiveCrafts(this.worldPosition);
return this.activeCraftCache;
} }
public void addNewItem(IPipeItem item) { public void addNewItem(IPipeItem item) {

View file

@ -70,15 +70,14 @@ public class CraftingModuleItem extends ModuleItem {
return; return;
var slot = tile.getModuleSlot(module); var slot = tile.getModuleSlot(module);
var network = PipeNetwork.get(tile.getLevel()); var network = PipeNetwork.get(tile.getLevel());
if (!tile.activeCrafts.isEmpty()) { var crafts = tile.getActiveCrafts();
var activeCraft = tile.activeCrafts.getFirst(); if (!crafts.isEmpty()) {
if (activeCraft.getLeft() == slot) { var craft = crafts.getFirst();
var craftData = activeCraft.getRight(); if (craft.moduleSlot == slot) {
// process crafting ingredient requests // process crafting ingredient requests
if (!craftData.ingredientsToRequest.isEmpty()) { if (!craft.ingredientsToRequest.isEmpty()) {
network.startProfile("crafting_ingredients"); network.startProfile("crafting_ingredients");
var lock = craftData.ingredientsToRequest.getFirst(); var lock = craft.ingredientsToRequest.getFirst();
var equalityTypes = ItemFilter.getEqualityTypes(tile); var equalityTypes = ItemFilter.getEqualityTypes(tile);
var dest = tile.getAvailableDestination(Direction.values(), lock.stack, true, true); var dest = tile.getAvailableDestination(Direction.values(), lock.stack, true, true);
if (dest != null) { if (dest != null) {
@ -87,12 +86,12 @@ public class CraftingModuleItem extends ModuleItem {
if (!ensureItemOrder || network.getPipeItemsOnTheWay(dest.getLeft()).findAny().isEmpty()) { if (!ensureItemOrder || network.getPipeItemsOnTheWay(dest.getLeft()).findAny().isEmpty()) {
var requestRemain = network.requestExistingItem(lock.location, tile.getBlockPos(), dest.getLeft(), lock, dest.getRight(), equalityTypes); var requestRemain = network.requestExistingItem(lock.location, tile.getBlockPos(), dest.getLeft(), lock, dest.getRight(), equalityTypes);
network.resolveNetworkLock(lock); network.resolveNetworkLock(lock);
craftData.ingredientsToRequest.remove(lock); craft.ingredientsToRequest.remove(lock);
craftData.inProgress = true; craft.inProgress = true;
var traveling = lock.stack.copy(); var traveling = lock.stack.copy();
traveling.shrink(requestRemain.getCount()); traveling.shrink(requestRemain.getCount());
craftData.travelingIngredients.add(traveling); craft.travelingIngredients.add(traveling);
// if we couldn't fit all items into the destination, create another request for the rest // if we couldn't fit all items into the destination, create another request for the rest
var remain = lock.stack.copy(); var remain = lock.stack.copy();
@ -100,8 +99,8 @@ public class CraftingModuleItem extends ModuleItem {
if (!remain.isEmpty()) { if (!remain.isEmpty()) {
var remainRequest = new NetworkLock(lock.location, remain); var remainRequest = new NetworkLock(lock.location, remain);
// if we're ensuring item order, we need to insert the remaining request at the start so that it gets processed first // if we're ensuring item order, we need to insert the remaining request at the start so that it gets processed first
var index = ensureItemOrder ? 0 : craftData.ingredientsToRequest.size(); var index = ensureItemOrder ? 0 : craft.ingredientsToRequest.size();
craftData.ingredientsToRequest.add(index, remainRequest); craft.ingredientsToRequest.add(index, remainRequest);
network.createNetworkLock(remainRequest); network.createNetworkLock(remainRequest);
} }
} }
@ -110,19 +109,19 @@ public class CraftingModuleItem extends ModuleItem {
} }
// pull requested crafting results from the network once they are stored // pull requested crafting results from the network once they are stored
if (!craftData.resultStackRemain.isEmpty()) { if (!craft.resultStackRemain.isEmpty()) {
network.startProfile("crafting_results"); network.startProfile("crafting_results");
var items = network.getOrderedNetworkItems(tile.getBlockPos()); var items = network.getOrderedNetworkItems(tile.getBlockPos());
var equalityTypes = ItemFilter.getEqualityTypes(tile); var equalityTypes = ItemFilter.getEqualityTypes(tile);
var destPipe = network.getPipe(craftData.resultDestPipe); var destPipe = network.getPipe(craft.resultDestPipe);
if (destPipe != null) { if (destPipe != null) {
var dest = destPipe.getAvailableDestinationOrConnectable(craftData.resultStackRemain, true, true); var dest = destPipe.getAvailableDestinationOrConnectable(craft.resultStackRemain, true, true);
if (dest != null) { if (dest != null) {
for (var item : items) { for (var item : items) {
var requestRemain = network.requestExistingItem(item, craftData.resultDestPipe, dest.getLeft(), null, dest.getRight(), equalityTypes); var requestRemain = network.requestExistingItem(item, craft.resultDestPipe, dest.getLeft(), null, dest.getRight(), equalityTypes);
craftData.resultStackRemain.shrink(dest.getRight().getCount() - requestRemain.getCount()); craft.resultStackRemain.shrink(dest.getRight().getCount() - requestRemain.getCount());
if (craftData.resultStackRemain.isEmpty()) { if (craft.resultStackRemain.isEmpty()) {
tile.activeCrafts.remove(activeCraft); crafts.remove(craft);
break; break;
} }
} }
@ -210,8 +209,8 @@ public class CraftingModuleItem extends ModuleItem {
var result = stack.copy(); var result = stack.copy();
result.shrink(remain.getCount()); result.shrink(remain.getCount());
var activeCraft = new ActiveCraft(locks, destPipe, result); var activeCraft = new ActiveCraft(tile.getBlockPos(), slot, locks, destPipe, result);
tile.activeCrafts.add(Pair.of(slot, activeCraft)); tile.getActiveCrafts().add(activeCraft);
crafts.add(activeCraft); crafts.add(activeCraft);
return Pair.of(remain, crafts); return Pair.of(remain, crafts);
@ -221,12 +220,12 @@ public class CraftingModuleItem extends ModuleItem {
public ItemStack store(ItemStack module, PipeBlockEntity tile, ItemStack stack, Direction direction) { public ItemStack store(ItemStack module, PipeBlockEntity tile, ItemStack stack, Direction direction) {
var slot = tile.getModuleSlot(module); var slot = tile.getModuleSlot(module);
var equalityTypes = ItemFilter.getEqualityTypes(tile); var equalityTypes = ItemFilter.getEqualityTypes(tile);
var matchingCraft = tile.activeCrafts.stream() var crafts = tile.getActiveCrafts();
.filter(c -> c.getLeft() == slot && !c.getRight().getTravelingIngredient(stack, equalityTypes).isEmpty()) var craft = crafts.stream()
.filter(c -> c.moduleSlot == slot && !c.getTravelingIngredient(stack, equalityTypes).isEmpty())
.findAny().orElse(null); .findAny().orElse(null);
if (matchingCraft != null) { if (craft != null) {
var data = matchingCraft.getRight(); craft.travelingIngredients.remove(craft.getTravelingIngredient(stack, equalityTypes));
data.travelingIngredients.remove(data.getTravelingIngredient(stack, equalityTypes));
if (module.get(Contents.TYPE).insertSingles) { if (module.get(Contents.TYPE).insertSingles) {
var handler = tile.getItemHandler(direction); var handler = tile.getItemHandler(direction);
@ -241,8 +240,8 @@ public class CraftingModuleItem extends ModuleItem {
} }
// if we canceled the request and all input items are delivered (ie the machine actually got what it expected), remove it from the queue // if we canceled the request and all input items are delivered (ie the machine actually got what it expected), remove it from the queue
if (data.canceled && data.travelingIngredients.size() <= 0 && data.ingredientsToRequest.size() <= 0) if (craft.canceled && craft.travelingIngredients.size() <= 0 && craft.ingredientsToRequest.size() <= 0)
tile.activeCrafts.remove(matchingCraft); crafts.remove(craft);
} }
return stack; return stack;
} }
@ -259,6 +258,7 @@ public class CraftingModuleItem extends ModuleItem {
} }
private static Stack<ItemStack> addDependency(Stack<ItemStack> deps, ItemStack module) { private static Stack<ItemStack> addDependency(Stack<ItemStack> deps, ItemStack module) {
// noinspection unchecked
deps = (Stack<ItemStack>) deps.clone(); deps = (Stack<ItemStack>) deps.clone();
deps.push(module); deps.push(module);
return deps; return deps;

View file

@ -215,7 +215,7 @@ public class ItemTerminalBlockEntity extends BlockEntity implements IPipeConnect
for (var craftable : network.getAllCraftables(pipe.getBlockPos())) { for (var craftable : network.getAllCraftables(pipe.getBlockPos())) {
var otherPipe = network.getPipe(craftable.getLeft()); var otherPipe = network.getPipe(craftable.getLeft());
if (otherPipe != null) if (otherPipe != null)
otherPipe.activeCrafts.removeIf(c -> c.getRight().markCanceledOrResolve(network)); otherPipe.getActiveCrafts().removeIf(c -> c.markCanceledOrResolve(network));
} }
var lookingPlayers = this.getLookingPlayers(); var lookingPlayers = this.getLookingPlayers();
if (lookingPlayers.length > 0) if (lookingPlayers.length > 0)