improved network caching

This commit is contained in:
Ellpeck 2020-05-09 12:57:25 +02:00
parent c21a5ddbb0
commit 172dcc7429
10 changed files with 152 additions and 76 deletions

View file

@ -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<? extends INBTSerializable<CompoundNBT>> items) {
ListNBT list = new ListNBT();
for (INBTSerializable<CompoundNBT> item : items)
list.add(item.serializeNBT());
return list;
}
public static <T extends INBTSerializable<CompoundNBT>> List<T> deserializeAll(ListNBT list, Function<CompoundNBT, T> supplier) {
List<T> 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);
}

View file

@ -68,7 +68,7 @@ public class PipeFrameEntity extends ItemFrameEntity implements IEntityAdditiona
ItemStack stack = this.getDisplayedItem();
if (!stack.isEmpty()) {
List<NetworkLocation> 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;
}

View file

@ -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 + '}';
}
}

View file

@ -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<CompoundNBT> {
public final BlockPos pipePos;
public final Direction direction;
public final BlockPos pos;
public final IItemHandler handler;
private Map<Integer, ItemStack> items;
public BlockPos pipePos;
public Direction direction;
private Map<Integer, ItemStack> 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<Integer> getStackSlots(ItemStack stack, ItemEqualityType... equalityTypes) {
if (this.isEmpty())
public List<Integer> 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<ItemStack> getItems() {
return this.items.values();
public Map<Integer, ItemStack> 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<Integer, ItemStack> 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"));
}
}

View file

@ -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<CompoundNBT> {
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");
}
}

View file

@ -268,20 +268,6 @@ public class PipeItem implements INBTSerializable<CompoundNBT>, ILiquidContainer
return ret;
}
public static ListNBT serializeAll(Collection<PipeItem> items) {
ListNBT list = new ListNBT();
for (PipeItem item : items)
list.add(item.serializeNBT());
return list;
}
public static List<PipeItem> deserializeAll(ListNBT list) {
List<PipeItem> 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;

View file

@ -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<CompoundNBT>, 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<CompoundNBT>, 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<CompoundNBT>, 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<CompoundNBT>, 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<NetworkLock> getNetworkLocks(BlockPos pos) {

View file

@ -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<PipeItem> 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

View file

@ -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;
}
}

View file

@ -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<EquatableItemStack, NetworkItem> 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);
}