|
2 | 2 |
|
3 | 3 | import me.desht.modularrouters.block.tile.ModularRouterBlockEntity;
|
4 | 4 | import me.desht.modularrouters.core.ModBlocks;
|
5 |
| -import net.minecraft.core.HolderLookup; |
6 |
| -import net.minecraft.nbt.CompoundTag; |
7 | 5 | import net.minecraft.world.item.ItemStack;
|
8 | 6 | import net.neoforged.neoforge.capabilities.Capabilities;
|
| 7 | +import net.neoforged.neoforge.capabilities.ItemCapability; |
9 | 8 | import net.neoforged.neoforge.energy.IEnergyStorage;
|
10 | 9 | import net.neoforged.neoforge.fluids.capability.IFluidHandlerItem;
|
11 | 10 | import net.neoforged.neoforge.items.ItemStackHandler;
|
| 11 | +import org.jetbrains.annotations.Nullable; |
| 12 | + |
| 13 | +import java.util.IdentityHashMap; |
| 14 | +import java.util.concurrent.atomic.AtomicBoolean; |
12 | 15 |
|
13 | 16 | public class BufferHandler extends ItemStackHandler {
|
| 17 | + // we mask null keys in the cap cache map with this object to avoid needing to check containsKey |
| 18 | + private static final Object NULL = new Object(); |
| 19 | + |
14 | 20 | private final ModularRouterBlockEntity router;
|
15 | 21 |
|
16 |
| - private IEnergyStorage energyStorage; |
17 |
| - private IFluidHandlerItem fluidHandler; |
| 22 | + // default expected size of 2 is reasonable for most routers |
| 23 | + private final IdentityHashMap<ItemCapability<?, Void>, Object> capabilityCache = new IdentityHashMap<>(4); |
18 | 24 |
|
19 | 25 | public BufferHandler(ModularRouterBlockEntity router) {
|
20 | 26 | super(router.getBufferSlotCount());
|
21 | 27 | this.router = router;
|
22 |
| - |
23 |
| - setupFluidAndEnergyCaps(); |
24 | 28 | }
|
25 | 29 |
|
26 | 30 | @Override
|
27 | 31 | public void onContentsChanged(int slot) {
|
28 |
| - ItemStack stack = getStackInSlot(slot); |
| 32 | + // small optimisation in case we don't have any caps requested already |
| 33 | + if (!capabilityCache.isEmpty()) { |
| 34 | + ItemStack stack = getStackInSlot(slot); |
29 | 35 |
|
30 |
| - IFluidHandlerItem newFluidHandler = stack.getCapability(Capabilities.FluidHandler.ITEM); |
31 |
| - IEnergyStorage newEnergyStorage = stack.getCapability(Capabilities.EnergyStorage.ITEM); |
| 36 | + var modified = new AtomicBoolean(); |
32 | 37 |
|
33 |
| - if (newFluidHandler != fluidHandler || newEnergyStorage != energyStorage) { |
34 |
| - fluidHandler = newFluidHandler; |
35 |
| - energyStorage = newEnergyStorage; |
| 38 | + // replace and invalidate if necessary the capabilities we previously returned |
| 39 | + //noinspection rawtypes,unchecked |
| 40 | + capabilityCache.replaceAll((cap, old) -> revalidate(stack, (ItemCapability) cap, old, modified)); |
36 | 41 |
|
37 |
| - router.invalidateCapabilities(); |
| 42 | + if (modified.get()) { |
| 43 | + router.invalidateCapabilities(); |
38 | 44 |
|
39 |
| - // in case any pipes/cables need to connect/disconnect |
40 |
| - router.nonNullLevel().updateNeighborsAt(router.getBlockPos(), ModBlocks.MODULAR_ROUTER.get()); |
| 45 | + // in case any pipes/cables need to connect/disconnect |
| 46 | + router.nonNullLevel().updateNeighborsAt(router.getBlockPos(), ModBlocks.MODULAR_ROUTER.get()); |
| 47 | + } |
41 | 48 | }
|
42 | 49 |
|
43 | 50 | router.setChanged(); // will also update comparator output
|
44 | 51 | }
|
45 | 52 |
|
46 |
| - @Override |
47 |
| - public void deserializeNBT(HolderLookup.Provider provider, CompoundTag nbt) { |
48 |
| - super.deserializeNBT(provider, nbt); |
49 |
| - |
50 |
| - setupFluidAndEnergyCaps(); |
| 53 | + private <T> T revalidate(ItemStack stack, ItemCapability<T, Void> cap, T old, AtomicBoolean modified) { |
| 54 | + var newVal = maskNull(stack.getCapability(cap)); |
| 55 | + if (newVal != old) { |
| 56 | + modified.compareAndSet(false, true); |
| 57 | + } |
| 58 | + return newVal; |
51 | 59 | }
|
52 | 60 |
|
53 | 61 | public IFluidHandlerItem getFluidHandler() {
|
54 |
| - return fluidHandler; |
| 62 | + return getCapability(Capabilities.FluidHandler.ITEM); |
55 | 63 | }
|
56 | 64 |
|
57 | 65 | public IEnergyStorage getEnergyStorage() {
|
58 |
| - return energyStorage; |
| 66 | + return getCapability(Capabilities.EnergyStorage.ITEM); |
59 | 67 | }
|
60 | 68 |
|
61 |
| - private void setupFluidAndEnergyCaps() { |
62 |
| - ItemStack stack = getStackInSlot(0); |
| 69 | + @Nullable |
| 70 | + @SuppressWarnings("unchecked") |
| 71 | + public <T> T getCapability(ItemCapability<T, Void> cap) { |
| 72 | + var cached = capabilityCache.get(cap); |
| 73 | + if (cached == null) { |
| 74 | + cached = maskNull(getStackInSlot(0).getCapability(cap)); |
| 75 | + capabilityCache.put(cap, cached); |
| 76 | + } |
| 77 | + return cached == NULL ? null : (T)cached; |
| 78 | + } |
63 | 79 |
|
64 |
| - fluidHandler = stack.getCapability(Capabilities.FluidHandler.ITEM); |
65 |
| - energyStorage = stack.getCapability(Capabilities.EnergyStorage.ITEM); |
| 80 | + @SuppressWarnings("unchecked") |
| 81 | + private static <T> T maskNull(T value) { |
| 82 | + return value == null ? (T) NULL : value; |
66 | 83 | }
|
67 | 84 | }
|
0 commit comments