From 172dcc7429d40c8eda0750de93109cee78a9d944 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Sat, 9 May 2020 12:57:25 +0200 Subject: [PATCH] improved network caching --- .../java/de/ellpeck/prettypipes/Utility.java | 20 +++++ .../prettypipes/entities/PipeFrameEntity.java | 2 +- .../prettypipes/network/NetworkItem.java | 5 -- .../prettypipes/network/NetworkLocation.java | 86 +++++++++++++------ .../prettypipes/network/NetworkLock.java | 34 +++++++- .../ellpeck/prettypipes/network/PipeItem.java | 14 --- .../prettypipes/network/PipeNetwork.java | 30 +++---- .../prettypipes/pipe/PipeTileEntity.java | 6 +- .../retrieval/RetrievalModuleItem.java | 10 ++- .../terminal/ItemTerminalTileEntity.java | 21 +++-- 10 files changed, 152 insertions(+), 76 deletions(-) diff --git a/src/main/java/de/ellpeck/prettypipes/Utility.java b/src/main/java/de/ellpeck/prettypipes/Utility.java index 0efc7c3..c1ca0da 100644 --- a/src/main/java/de/ellpeck/prettypipes/Utility.java +++ b/src/main/java/de/ellpeck/prettypipes/Utility.java @@ -1,6 +1,7 @@ package de.ellpeck.prettypipes; import de.ellpeck.prettypipes.items.IModule; +import de.ellpeck.prettypipes.network.PipeItem; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.resources.I18n; import net.minecraft.entity.player.PlayerEntity; @@ -8,14 +9,19 @@ import net.minecraft.inventory.InventoryHelper; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.Slot; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.*; import net.minecraft.world.World; +import net.minecraftforge.common.util.INBTSerializable; import net.minecraftforge.items.IItemHandler; import org.apache.commons.lang3.tuple.Pair; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.function.Function; import java.util.stream.IntStream; @@ -94,6 +100,20 @@ public final class Utility { return ItemStack.EMPTY; } + public static ListNBT serializeAll(Collection> items) { + ListNBT list = new ListNBT(); + for (INBTSerializable item : items) + list.add(item.serializeNBT()); + return list; + } + + public static > List deserializeAll(ListNBT list, Function supplier) { + List items = new ArrayList<>(); + for (int i = 0; i < list.size(); i++) + items.add(supplier.apply(list.getCompound(i))); + return items; + } + public interface IMergeItemStack { boolean mergeItemStack(ItemStack stack, int startIndex, int endIndex, boolean reverseDirection); } diff --git a/src/main/java/de/ellpeck/prettypipes/entities/PipeFrameEntity.java b/src/main/java/de/ellpeck/prettypipes/entities/PipeFrameEntity.java index cce301b..c05e9e3 100644 --- a/src/main/java/de/ellpeck/prettypipes/entities/PipeFrameEntity.java +++ b/src/main/java/de/ellpeck/prettypipes/entities/PipeFrameEntity.java @@ -68,7 +68,7 @@ public class PipeFrameEntity extends ItemFrameEntity implements IEntityAdditiona ItemStack stack = this.getDisplayedItem(); if (!stack.isEmpty()) { List items = network.getOrderedNetworkItems(node); - int amount = items.stream().mapToInt(i -> i.getItemAmount(stack)).sum(); + int amount = items.stream().mapToInt(i -> i.getItemAmount(this.world, stack)).sum(); this.dataManager.set(AMOUNT, amount); return; } diff --git a/src/main/java/de/ellpeck/prettypipes/network/NetworkItem.java b/src/main/java/de/ellpeck/prettypipes/network/NetworkItem.java index c081001..7edadb5 100644 --- a/src/main/java/de/ellpeck/prettypipes/network/NetworkItem.java +++ b/src/main/java/de/ellpeck/prettypipes/network/NetworkItem.java @@ -30,9 +30,4 @@ public class NetworkItem { stack.setCount(this.amount); return stack; } - - @Override - public String toString() { - return "NetworkItem{" + "locations=" + this.locations + ", item=" + this.item + ", amount=" + this.amount + '}'; - } } diff --git a/src/main/java/de/ellpeck/prettypipes/network/NetworkLocation.java b/src/main/java/de/ellpeck/prettypipes/network/NetworkLocation.java index 104cdfd..32ac759 100644 --- a/src/main/java/de/ellpeck/prettypipes/network/NetworkLocation.java +++ b/src/main/java/de/ellpeck/prettypipes/network/NetworkLocation.java @@ -3,61 +3,99 @@ package de.ellpeck.prettypipes.network; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import de.ellpeck.prettypipes.misc.ItemEqualityType; +import de.ellpeck.prettypipes.pipe.PipeTileEntity; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.NBTUtil; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.common.util.INBTSerializable; import net.minecraftforge.items.IItemHandler; import org.apache.commons.lang3.tuple.Pair; import java.util.*; import java.util.stream.Collectors; -public class NetworkLocation { +public class NetworkLocation implements INBTSerializable { - public final BlockPos pipePos; - public final Direction direction; - public final BlockPos pos; - public final IItemHandler handler; - private Map items; + public BlockPos pipePos; + public Direction direction; + private Map itemCache; + private IItemHandler handlerCache; - public NetworkLocation(BlockPos pipePos, Direction direction, IItemHandler handler) { + public NetworkLocation(BlockPos pipePos, Direction direction) { this.pipePos = pipePos; this.direction = direction; - this.pos = pipePos.offset(direction); - this.handler = handler; } - public void addItem(int slot, ItemStack stack) { - if (this.items == null) - this.items = new HashMap<>(); - this.items.put(slot, stack); + public NetworkLocation(CompoundNBT nbt) { + this.deserializeNBT(nbt); } - public List getStackSlots(ItemStack stack, ItemEqualityType... equalityTypes) { - if (this.isEmpty()) + public List getStackSlots(World world, ItemStack stack, ItemEqualityType... equalityTypes) { + if (this.isEmpty(world)) return Collections.emptyList(); - return this.items.entrySet().stream() + return this.getItems(world).entrySet().stream() .filter(e -> ItemEqualityType.compareItems(e.getValue(), stack, equalityTypes)) .map(Map.Entry::getKey) .collect(Collectors.toList()); } - public int getItemAmount(ItemStack stack, ItemEqualityType... equalityTypes) { - return this.items.values().stream() + public int getItemAmount(World world, ItemStack stack, ItemEqualityType... equalityTypes) { + if (this.isEmpty(world)) + return 0; + return this.getItems(world).values().stream() .filter(i -> ItemEqualityType.compareItems(stack, i, equalityTypes)) .mapToInt(ItemStack::getCount).sum(); } - public Collection getItems() { - return this.items.values(); + public Map getItems(World world) { + if (this.itemCache == null) { + IItemHandler handler = this.getItemHandler(world); + if (handler != null) { + for (int i = 0; i < handler.getSlots(); i++) { + ItemStack found = handler.extractItem(i, Integer.MAX_VALUE, true); + if (found.isEmpty()) + continue; + if (this.itemCache == null) + this.itemCache = new HashMap<>(); + this.itemCache.put(i, found); + } + } + } + return this.itemCache; } - public boolean isEmpty() { - return this.items == null || this.items.isEmpty(); + public IItemHandler getItemHandler(World world) { + if (this.handlerCache == null) { + PipeNetwork network = PipeNetwork.get(world); + PipeTileEntity pipe = network.getPipe(this.pipePos); + this.handlerCache = pipe.getItemHandler(this.direction, null); + } + return this.handlerCache; + } + + public boolean isEmpty(World world) { + Map items = this.getItems(world); + return items == null || items.isEmpty(); + } + + public BlockPos getPos() { + return this.pipePos.offset(this.direction); } @Override - public String toString() { - return this.items.values().toString(); + public CompoundNBT serializeNBT() { + CompoundNBT nbt = new CompoundNBT(); + nbt.put("pipe_pos", NBTUtil.writeBlockPos(this.pipePos)); + nbt.putInt("direction", this.direction.getIndex()); + return nbt; + } + + @Override + public void deserializeNBT(CompoundNBT nbt) { + this.pipePos = NBTUtil.readBlockPos(nbt.getCompound("pipe_pos")); + this.direction = Direction.byIndex(nbt.getInt("direction")); } } diff --git a/src/main/java/de/ellpeck/prettypipes/network/NetworkLock.java b/src/main/java/de/ellpeck/prettypipes/network/NetworkLock.java index 80697c1..e0ffce9 100644 --- a/src/main/java/de/ellpeck/prettypipes/network/NetworkLock.java +++ b/src/main/java/de/ellpeck/prettypipes/network/NetworkLock.java @@ -1,14 +1,40 @@ package de.ellpeck.prettypipes.network; -public class NetworkLock { +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraftforge.common.util.INBTSerializable; - public final NetworkLocation location; - public final int slot; - public final int amount; +import java.util.Collection; + +public class NetworkLock implements INBTSerializable { + + public NetworkLocation location; + public int slot; + public int amount; public NetworkLock(NetworkLocation location, int slot, int amount) { this.location = location; this.slot = slot; this.amount = amount; } + + public NetworkLock(CompoundNBT nbt) { + this.deserializeNBT(nbt); + } + + @Override + public CompoundNBT serializeNBT() { + CompoundNBT nbt = new CompoundNBT(); + nbt.put("location", this.location.serializeNBT()); + nbt.putInt("slot", this.slot); + nbt.putInt("amount", this.amount); + return nbt; + } + + @Override + public void deserializeNBT(CompoundNBT nbt) { + this.location = new NetworkLocation(nbt.getCompound("location")); + this.slot = nbt.getInt("slot"); + this.amount = nbt.getInt("amount"); + } } diff --git a/src/main/java/de/ellpeck/prettypipes/network/PipeItem.java b/src/main/java/de/ellpeck/prettypipes/network/PipeItem.java index 34aaced..81786b9 100644 --- a/src/main/java/de/ellpeck/prettypipes/network/PipeItem.java +++ b/src/main/java/de/ellpeck/prettypipes/network/PipeItem.java @@ -268,20 +268,6 @@ public class PipeItem implements INBTSerializable, ILiquidContainer return ret; } - public static ListNBT serializeAll(Collection items) { - ListNBT list = new ListNBT(); - for (PipeItem item : items) - list.add(item.serializeNBT()); - return list; - } - - public static List deserializeAll(ListNBT list) { - List items = new ArrayList<>(); - for (int i = 0; i < list.size(); i++) - items.add(new PipeItem(list.getCompound(i))); - return items; - } - @Override public boolean canContainFluid(IBlockReader worldIn, BlockPos pos, BlockState state, Fluid fluidIn) { return true; diff --git a/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java b/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java index ae2a54a..8828638 100644 --- a/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java +++ b/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java @@ -17,11 +17,13 @@ import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.ListNBT; import net.minecraft.nbt.NBTUtil; import net.minecraft.util.Direction; +import net.minecraft.util.Util; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ICapabilitySerializable; import net.minecraftforge.common.util.Constants; +import net.minecraftforge.common.util.Constants.NBT; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.items.IItemHandler; import org.apache.commons.lang3.tuple.Pair; @@ -77,7 +79,8 @@ public class PipeNetwork implements ICapabilitySerializable, GraphL for (NetworkEdge edge : this.graph.edgeSet()) edges.add(edge.serializeNBT()); nbt.put("edges", edges); - nbt.put("items", PipeItem.serializeAll(this.pipeItems.values())); + nbt.put("items", Utility.serializeAll(this.pipeItems.values())); + nbt.put("locks", Utility.serializeAll(this.networkLocks.values())); return nbt; } @@ -85,15 +88,18 @@ public class PipeNetwork implements ICapabilitySerializable, GraphL public void deserializeNBT(CompoundNBT nbt) { this.graph.removeAllVertices(new ArrayList<>(this.graph.vertexSet())); this.pipeItems.clear(); + this.networkLocks.clear(); - ListNBT nodes = nbt.getList("nodes", Constants.NBT.TAG_COMPOUND); + ListNBT nodes = nbt.getList("nodes", NBT.TAG_COMPOUND); for (int i = 0; i < nodes.size(); i++) this.graph.addVertex(NBTUtil.readBlockPos(nodes.getCompound(i))); - ListNBT edges = nbt.getList("edges", Constants.NBT.TAG_COMPOUND); + ListNBT edges = nbt.getList("edges", NBT.TAG_COMPOUND); for (int i = 0; i < edges.size(); i++) this.addEdge(new NetworkEdge(edges.getCompound(i))); - for (PipeItem item : PipeItem.deserializeAll(nbt.getList("items", Constants.NBT.TAG_COMPOUND))) + for (PipeItem item : Utility.deserializeAll(nbt.getList("items", NBT.TAG_COMPOUND), PipeItem::new)) this.pipeItems.put(item.getCurrentPipe(), item); + for (NetworkLock lock : Utility.deserializeAll(nbt.getList("locks", NBT.TAG_COMPOUND), NetworkLock::new)) + this.createNetworkLock(lock); } public void addNode(BlockPos pos, BlockState state) { @@ -194,16 +200,10 @@ public class PipeNetwork implements ICapabilitySerializable, GraphL if (handler == null) continue; // check if this handler already exists (double-connected pipes, double chests etc.) - if (info.stream().anyMatch(l -> l.handler == handler)) + if (info.stream().anyMatch(l -> l.getItemHandler(this.world) == handler)) continue; - NetworkLocation location = new NetworkLocation(dest, dir, handler); - for (int i = 0; i < handler.getSlots(); i++) { - ItemStack found = handler.extractItem(i, Integer.MAX_VALUE, true); - if (found.isEmpty()) - continue; - location.addItem(i, found); - } - if (!location.isEmpty()) + NetworkLocation location = new NetworkLocation(dest, dir); + if (!location.isEmpty(this.world)) info.add(location); } } @@ -212,11 +212,11 @@ public class PipeNetwork implements ICapabilitySerializable, GraphL } public void createNetworkLock(NetworkLock lock) { - this.networkLocks.put(lock.location.pos, lock); + this.networkLocks.put(lock.location.getPos(), lock); } public void resolveNetworkLock(NetworkLock lock) { - this.networkLocks.remove(lock.location.pos, lock); + this.networkLocks.remove(lock.location.getPos(), lock); } public List getNetworkLocks(BlockPos pos) { diff --git a/src/main/java/de/ellpeck/prettypipes/pipe/PipeTileEntity.java b/src/main/java/de/ellpeck/prettypipes/pipe/PipeTileEntity.java index aac165e..7cace89 100644 --- a/src/main/java/de/ellpeck/prettypipes/pipe/PipeTileEntity.java +++ b/src/main/java/de/ellpeck/prettypipes/pipe/PipeTileEntity.java @@ -2,6 +2,7 @@ package de.ellpeck.prettypipes.pipe; import de.ellpeck.prettypipes.PrettyPipes; import de.ellpeck.prettypipes.Registry; +import de.ellpeck.prettypipes.Utility; import de.ellpeck.prettypipes.items.IModule; import de.ellpeck.prettypipes.network.PipeItem; import de.ellpeck.prettypipes.network.PipeNetwork; @@ -24,6 +25,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TranslationTextComponent; import net.minecraftforge.common.util.Constants; +import net.minecraftforge.common.util.Constants.NBT; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemHandlerHelper; @@ -77,7 +79,7 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide public CompoundNBT getUpdateTag() { // sync pipe items on load CompoundNBT nbt = this.write(new CompoundNBT()); - nbt.put("items", PipeItem.serializeAll(this.getItems())); + nbt.put("items", Utility.serializeAll(this.getItems())); return nbt; } @@ -86,7 +88,7 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide this.read(nbt); List items = this.getItems(); items.clear(); - items.addAll(PipeItem.deserializeAll(nbt.getList("items", Constants.NBT.TAG_COMPOUND))); + items.addAll(Utility.deserializeAll(nbt.getList("items", NBT.TAG_COMPOUND), PipeItem::new)); } @Override diff --git a/src/main/java/de/ellpeck/prettypipes/pipe/modules/retrieval/RetrievalModuleItem.java b/src/main/java/de/ellpeck/prettypipes/pipe/modules/retrieval/RetrievalModuleItem.java index f0c72cd..5e42202 100644 --- a/src/main/java/de/ellpeck/prettypipes/pipe/modules/retrieval/RetrievalModuleItem.java +++ b/src/main/java/de/ellpeck/prettypipes/pipe/modules/retrieval/RetrievalModuleItem.java @@ -14,6 +14,7 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; +import net.minecraftforge.items.IItemHandler; import java.util.List; @@ -58,11 +59,12 @@ public class RetrievalModuleItem extends ModuleItem { for (NetworkLocation location : locations) { if (location.pipePos.equals(tile.getPos())) continue; - for (int slot : location.getStackSlots(filtered, filter.getEqualityTypes())) { + for (int slot : location.getStackSlots(tile.getWorld(), filtered, filter.getEqualityTypes())) { // try to extract from that location's inventory and send the item - ItemStack stack = location.handler.extractItem(slot, this.maxExtraction, true); - if (network.routeItemToLocation(location.pipePos, location.pos, tile.getPos(), dest, speed -> new PipeItem(stack, speed))) { - location.handler.extractItem(slot, stack.getCount(), false); + IItemHandler handler = location.getItemHandler(tile.getWorld()); + ItemStack stack = handler.extractItem(slot, this.maxExtraction, true); + if (network.routeItemToLocation(location.pipePos, location.getPos(), tile.getPos(), dest, speed -> new PipeItem(stack, speed))) { + handler.extractItem(slot, stack.getCount(), false); return; } } diff --git a/src/main/java/de/ellpeck/prettypipes/terminal/ItemTerminalTileEntity.java b/src/main/java/de/ellpeck/prettypipes/terminal/ItemTerminalTileEntity.java index 3dbd7e4..063deb4 100644 --- a/src/main/java/de/ellpeck/prettypipes/terminal/ItemTerminalTileEntity.java +++ b/src/main/java/de/ellpeck/prettypipes/terminal/ItemTerminalTileEntity.java @@ -28,6 +28,9 @@ import net.minecraft.util.text.Style; import net.minecraft.util.text.TextFormatting; import net.minecraft.util.text.TranslationTextComponent; import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.common.util.Constants.NBT; +import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemStackHandler; import org.apache.commons.lang3.mutable.MutableInt; import org.apache.commons.lang3.tuple.Pair; @@ -81,9 +84,10 @@ public class ItemTerminalTileEntity extends TileEntity implements INamedContaine if (!this.pendingRequests.isEmpty()) { NetworkLock request = this.pendingRequests.remove(); network.resolveNetworkLock(request); - ItemStack extracted = request.location.handler.extractItem(request.slot, request.amount, true); - if (network.routeItemToLocation(request.location.pipePos, request.location.pos, pipe.getPos(), this.pos, speed -> new PipeItem(extracted, speed))) { - request.location.handler.extractItem(request.slot, extracted.getCount(), false); + IItemHandler handler = request.location.getItemHandler(this.world); + ItemStack extracted = handler.extractItem(request.slot, request.amount, true); + if (network.routeItemToLocation(request.location.pipePos, request.location.getPos(), pipe.getPos(), this.pos, speed -> new PipeItem(extracted, speed))) { + handler.extractItem(request.slot, extracted.getCount(), false); update = true; } } @@ -141,11 +145,11 @@ public class ItemTerminalTileEntity extends TileEntity implements INamedContaine int remain = stack.getCount(); locations: for (NetworkLocation location : item.getLocations()) { - for (int slot : location.getStackSlots(stack, ItemEqualityType.NBT)) { - ItemStack inSlot = location.handler.extractItem(slot, Integer.MAX_VALUE, true); + for (int slot : location.getStackSlots(this.world, stack, ItemEqualityType.NBT)) { + ItemStack inSlot = location.getItemHandler(this.world).extractItem(slot, Integer.MAX_VALUE, true); if (inSlot.isEmpty()) continue; - inSlot.shrink(network.getLockedAmount(location.pos, slot)); + inSlot.shrink(network.getLockedAmount(location.getPos(), slot)); if (inSlot.getCount() > 0) { int extract = Math.min(inSlot.getCount(), remain); NetworkLock lock = new NetworkLock(location, slot, extract); @@ -177,7 +181,7 @@ public class ItemTerminalTileEntity extends TileEntity implements INamedContaine PipeTileEntity pipe = this.getConnectedPipe(); Map items = new HashMap<>(); for (NetworkLocation location : network.getOrderedNetworkItems(pipe.getPos())) { - for (ItemStack stack : location.getItems()) { + for (ItemStack stack : location.getItems(this.world).values()) { EquatableItemStack equatable = new EquatableItemStack(stack); NetworkItem item = items.computeIfAbsent(equatable, NetworkItem::new); item.add(location, stack); @@ -190,12 +194,15 @@ public class ItemTerminalTileEntity extends TileEntity implements INamedContaine @Override public CompoundNBT write(CompoundNBT compound) { compound.put("items", this.items.serializeNBT()); + compound.put("requests", Utility.serializeAll(this.pendingRequests)); return super.write(compound); } @Override public void read(CompoundNBT compound) { this.items.deserializeNBT(compound.getCompound("items")); + this.pendingRequests.clear(); + this.pendingRequests.addAll(Utility.deserializeAll(compound.getList("requests", NBT.TAG_COMPOUND), NetworkLock::new)); super.read(compound); }