Files
Applied-Energistics-2/src/main/java/appeng/parts/misc/ItemRepositoryAdapter.java
T
2022-09-17 08:08:02 -03:00

216 lines
8.2 KiB
Java

package appeng.parts.misc;
import appeng.api.AEApi;
import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable;
import appeng.api.config.Settings;
import appeng.api.networking.security.IActionSource;
import appeng.api.networking.storage.IBaseMonitor;
import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.storage.IMEInventory;
import appeng.api.storage.IMEMonitorHandlerReceiver;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IItemList;
import appeng.core.AELog;
import appeng.me.GridAccessException;
import appeng.me.helpers.IGridProxyable;
import appeng.me.storage.ITickingMonitor;
import appeng.util.item.AEItemStack;
import com.google.common.primitives.Ints;
import com.jaquadro.minecraft.storagedrawers.api.capabilities.IItemRepository;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.item.ItemStack;
import java.util.*;
/**
* Wraps an Item Repository in such a way that it can be used as an IMEInventory for items.
* Used by the Storage Bus
*/
class ItemRepositoryAdapter implements IMEInventory<IAEItemStack>, IBaseMonitor<IAEItemStack>, ITickingMonitor {
private final Object2ObjectMap<IMEMonitorHandlerReceiver<IAEItemStack>, Object> listeners = new Object2ObjectOpenHashMap<>();
private IActionSource mySource;
private final IItemRepository itemRepository;
private final IGridProxyable proxyable;
private final InventoryCache cache;
private AccessRestriction access;
ItemRepositoryAdapter(IItemRepository itemRepository, IGridProxyable proxy) {
this.itemRepository = itemRepository;
this.proxyable = proxy;
this.cache = new InventoryCache(this.itemRepository);
if (this.proxyable instanceof PartStorageBus) {
PartStorageBus partStorageBus = (PartStorageBus) this.proxyable;
this.access = ((AccessRestriction) partStorageBus.getConfigManager().getSetting(Settings.ACCESS));
}
this.cache.update();
}
@Override
public IAEItemStack injectItems(IAEItemStack iox, Actionable type, IActionSource src) {
// Try to reuse the cached stack
ItemStack inputStack = iox.getCachedItemStack(iox.getStackSize());
ItemStack remaining;
remaining = this.itemRepository.insertItem(inputStack, type == Actionable.SIMULATE);
// Store the stack in the cache for next time.
if (type == Actionable.SIMULATE) {
iox.setCachedItemStack(inputStack);
} else {
if (!remaining.isEmpty()) {
iox.setCachedItemStack(remaining);
}
}
// At this point, we still have some items left...
if (remaining == inputStack) {
// The stack remained unmodified, target inventory is full
return iox;
}
if (type == Actionable.MODULATE) {
IAEItemStack added = iox.copy().setStackSize(iox.getStackSize() - remaining.getCount());
this.cache.currentlyCached.add(added);
this.postDifference(Collections.singletonList(added));
try {
this.proxyable.getProxy().getTick().alertDevice(this.proxyable.getProxy().getNode());
} catch (GridAccessException ex) {
// meh
}
}
return AEItemStack.fromItemStack(remaining);
}
@Override
public IAEItemStack extractItems(IAEItemStack request, Actionable mode, IActionSource src) {
int remainingSize = Ints.saturatedCast(request.getStackSize());
final boolean simulate = (mode == Actionable.SIMULATE);
ItemStack extracted = this.itemRepository.extractItem(request.getDefinition(), remainingSize, simulate);
if (extracted.getCount() > remainingSize) {
// Something broke. It should never return more than we requested...
// We're going to silently eat the remainder
AELog.warn("Mod that provided item handler %s is broken. Returned %s items while only requesting %d.", this.itemRepository.getClass().getName(), extracted.toString(), remainingSize);
extracted.setCount(remainingSize);
}
if (!extracted.isEmpty()) {
IAEItemStack extractedAEItemStack = AEItemStack.fromItemStack(extracted);
if (mode == Actionable.MODULATE) {
IAEItemStack cachedStack = this.cache.currentlyCached.findPrecise(request);
if (cachedStack != null) {
cachedStack.decStackSize(extractedAEItemStack.getStackSize());
this.postDifference(Collections.singletonList(extractedAEItemStack.copy().setStackSize(-extractedAEItemStack.getStackSize())));
}
try {
this.proxyable.getProxy().getTick().alertDevice(this.proxyable.getProxy().getNode());
} catch (GridAccessException ex) {
// meh
}
}
return extractedAEItemStack;
}
return null;
}
@Override
public IItemList<IAEItemStack> getAvailableItems(IItemList<IAEItemStack> out) {
return this.cache.getAvailableItems(out);
}
@Override
public IStorageChannel<IAEItemStack> getChannel() {
return AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class);
}
@Override
public void addListener(IMEMonitorHandlerReceiver<IAEItemStack> l, Object verificationToken) {
this.listeners.put(l, verificationToken);
}
@Override
public void removeListener(IMEMonitorHandlerReceiver<IAEItemStack> l) {
this.listeners.remove(l);
}
private void postDifference(Iterable<IAEItemStack> a) {
final Iterator<Map.Entry<IMEMonitorHandlerReceiver<IAEItemStack>, Object>> i = this.listeners.entrySet().iterator();
while (i.hasNext()) {
final Map.Entry<IMEMonitorHandlerReceiver<IAEItemStack>, Object> l = i.next();
final IMEMonitorHandlerReceiver<IAEItemStack> key = l.getKey();
if (key.isValid(l.getValue())) {
key.postChange(this, a, this.mySource);
} else {
i.remove();
}
}
}
@Override
public TickRateModulation onTick() {
List<IAEItemStack> changes = this.cache.update();
if (!changes.isEmpty() && access.hasPermission(AccessRestriction.READ)) {
this.postDifference(changes);
return TickRateModulation.URGENT;
} else {
return TickRateModulation.SLOWER;
}
}
@Override
public void setActionSource(final IActionSource mySource) {
this.mySource = mySource;
}
private static class InventoryCache {
private IItemList<IAEItemStack> currentlyCached = AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class).createList();
private final IItemRepository iItemRepository;
public InventoryCache(IItemRepository iItemRepository) {
this.iItemRepository = iItemRepository;
}
public IItemList<IAEItemStack> getAvailableItems(IItemList<IAEItemStack> out) {
currentlyCached.iterator().forEachRemaining(out::add);
return out;
}
public List<IAEItemStack> update() {
final List<IAEItemStack> changes = new ArrayList<>();
IItemList<IAEItemStack> currentlyOnStorage = AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class).createList();
this.iItemRepository.getAllItems().stream().map(s -> AEItemStack.fromItemStack(s.itemPrototype).setStackSize(s.count)).forEach(currentlyOnStorage::add);
for (final IAEItemStack is : currentlyCached) {
is.setStackSize(-is.getStackSize());
}
for (final IAEItemStack is : currentlyOnStorage) {
currentlyCached.add(is);
}
for (final IAEItemStack is : currentlyCached) {
if (is.getStackSize() != 0) {
changes.add(is);
}
}
currentlyCached = currentlyOnStorage;
return changes;
}
}
}