/* * This file is part of Applied Energistics 2. * Copyright (c) 2013 - 2018, AlgorithmX2, All rights reserved. * * Applied Energistics 2 is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Applied Energistics 2 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Applied Energistics 2. If not, see . */ package appeng.me.storage; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraftforge.items.IItemHandler; import appeng.api.config.FuzzyMode; import appeng.api.implementations.items.IStorageCell; import appeng.api.storage.cells.CellState; import appeng.api.storage.cells.ICellInventory; import appeng.api.storage.cells.ISaveProvider; import appeng.api.storage.data.IAEStack; import appeng.api.storage.data.IItemList; /** * @author DrummerMC * @version rv6 - 2018-01-17 * @since rv6 2018-01-17 */ public abstract class AbstractCellInventory> implements ICellInventory { private static final int MAX_ITEM_TYPES = 63; private static final String ITEM_TYPE_TAG = "it"; private static final String ITEM_COUNT_TAG = "ic"; private static final String ITEM_SLOT = "#"; private static final String ITEM_SLOT_COUNT = "@"; protected static final String ITEM_PRE_FORMATTED_COUNT = "PF"; protected static final String ITEM_PRE_FORMATTED_SLOT = "PF#"; protected static final String ITEM_PRE_FORMATTED_NAME = "PN"; protected static final String ITEM_PRE_FORMATTED_FUZZY = "FP"; private static final String[] ITEM_SLOT_KEYS = new String[MAX_ITEM_TYPES]; private static final String[] ITEM_SLOT_COUNT_KEYS = new String[MAX_ITEM_TYPES]; private final CompoundNBT tagCompound; protected final ISaveProvider container; private int maxItemTypes = MAX_ITEM_TYPES; private short storedItems = 0; private int storedItemCount = 0; protected IItemList cellItems; private final ItemStack i; protected final IStorageCell cellType; protected final int itemsPerByte; private boolean isPersisted = true; static { for (int x = 0; x < MAX_ITEM_TYPES; x++) { ITEM_SLOT_KEYS[x] = ITEM_SLOT + x; ITEM_SLOT_COUNT_KEYS[x] = ITEM_SLOT_COUNT + x; } } protected AbstractCellInventory(final IStorageCell cellType, final ItemStack o, final ISaveProvider container) { this.i = o; this.cellType = cellType; this.itemsPerByte = this.cellType.getChannel().getUnitsPerByte(); this.maxItemTypes = this.cellType.getTotalTypes(this.i); if (this.maxItemTypes > MAX_ITEM_TYPES) { this.maxItemTypes = MAX_ITEM_TYPES; } if (this.maxItemTypes < 1) { this.maxItemTypes = 1; } this.container = container; this.tagCompound = o.getOrCreateTag(); this.storedItems = this.tagCompound.getShort(ITEM_TYPE_TAG); this.storedItemCount = this.tagCompound.getInt(ITEM_COUNT_TAG); this.cellItems = null; } protected IItemList getCellItems() { if (this.cellItems == null) { this.cellItems = this.getChannel().createList(); this.loadCellItems(); } return this.cellItems; } @Override public void persist() { if (this.isPersisted) { return; } int itemCount = 0; // add new pretty stuff... int x = 0; for (final T v : this.cellItems) { itemCount += v.getStackSize(); final CompoundNBT g = new CompoundNBT(); v.writeToNBT(g); this.tagCompound.put(ITEM_SLOT_KEYS[x], g); this.tagCompound.putInt(ITEM_SLOT_COUNT_KEYS[x], (int) v.getStackSize()); x++; } final short oldStoredItems = this.storedItems; this.storedItems = (short) this.cellItems.size(); if (this.cellItems.isEmpty()) { this.tagCompound.remove(ITEM_TYPE_TAG); } else { this.tagCompound.putShort(ITEM_TYPE_TAG, this.storedItems); } this.storedItemCount = itemCount; if (itemCount == 0) { this.tagCompound.remove(ITEM_COUNT_TAG); } else { this.tagCompound.putInt(ITEM_COUNT_TAG, itemCount); } // clean any old crusty stuff... for (; x < oldStoredItems && x < this.maxItemTypes; x++) { this.tagCompound.remove(ITEM_SLOT_KEYS[x]); this.tagCompound.remove(ITEM_SLOT_COUNT_KEYS[x]); } this.isPersisted = true; } protected void saveChanges() { // recalculate values this.storedItems = (short) this.cellItems.size(); this.storedItemCount = 0; for (final T v : this.cellItems) { this.storedItemCount += v.getStackSize(); } this.isPersisted = false; if (this.container != null) { this.container.saveChanges(this); } else { // if there is no ISaveProvider, store to NBT immediately this.persist(); } } private void loadCellItems() { if (this.cellItems == null) { this.cellItems = this.getChannel().createList(); } this.cellItems.resetStatus(); // clears totals and stuff. final int types = (int) this.getStoredItemTypes(); boolean needsUpdate = false; for (int slot = 0; slot < types; slot++) { CompoundNBT compoundTag = this.tagCompound.getCompound(ITEM_SLOT_KEYS[slot]); int stackSize = this.tagCompound.getInt(ITEM_SLOT_COUNT_KEYS[slot]); needsUpdate |= !this.loadCellItem(compoundTag, stackSize); } if (needsUpdate) { this.saveChanges(); } } /** * Load a single item. * * @param compoundTag * @param stackSize * @return true when successfully loaded */ protected abstract boolean loadCellItem(CompoundNBT compoundTag, int stackSize); @Override public IItemList getAvailableItems(final IItemList out) { for (final T item : this.getCellItems()) { out.add(item); } return out; } @Override public ItemStack getItemStack() { return this.i; } @Override public double getIdleDrain() { return this.cellType.getIdleDrain(); } @Override public FuzzyMode getFuzzyMode() { return this.cellType.getFuzzyMode(this.i); } @Override public IItemHandler getConfigInventory() { return this.cellType.getConfigInventory(this.i); } @Override public IItemHandler getUpgradesInventory() { return this.cellType.getUpgradesInventory(this.i); } @Override public int getBytesPerType() { return this.cellType.getBytesPerType(this.i); } @Override public boolean canHoldNewItem() { final long bytesFree = this.getFreeBytes(); return (bytesFree > this.getBytesPerType() || (bytesFree == this.getBytesPerType() && this.getUnusedItemCount() > 0)) && this.getRemainingItemTypes() > 0; } @Override public long getTotalBytes() { return this.cellType.getBytes(this.i); } @Override public long getFreeBytes() { return this.getTotalBytes() - this.getUsedBytes(); } @Override public long getTotalItemTypes() { return this.maxItemTypes; } @Override public long getStoredItemCount() { return this.storedItemCount; } @Override public long getStoredItemTypes() { return this.storedItems; } @Override public long getRemainingItemTypes() { final long basedOnStorage = this.getFreeBytes() / this.getBytesPerType(); final long baseOnTotal = this.getTotalItemTypes() - this.getStoredItemTypes(); return basedOnStorage > baseOnTotal ? baseOnTotal : basedOnStorage; } @Override public long getUsedBytes() { final long bytesForItemCount = (this.getStoredItemCount() + this.getUnusedItemCount()) / this.itemsPerByte; return this.getStoredItemTypes() * this.getBytesPerType() + bytesForItemCount; } @Override public long getRemainingItemCount() { final long remaining = this.getFreeBytes() * this.itemsPerByte + this.getUnusedItemCount(); return remaining > 0 ? remaining : 0; } @Override public int getUnusedItemCount() { final int div = (int) (this.getStoredItemCount() % 8); if (div == 0) { return 0; } return this.itemsPerByte - div; } @Override public CellState getStatusForCell() { if (this.getStoredItemTypes() == 0) { return CellState.EMPTY; } if (this.canHoldNewItem()) { return CellState.NOT_EMPTY; } if (this.getRemainingItemCount() > 0) { return CellState.TYPES_FULL; } return CellState.FULL; } }