Applied-Energistics-2-tiler.../src/main/java/appeng/tile/storage/TileChest.java

853 lines
28 KiB
Java

/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, 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 <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.tile.storage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import appeng.api.AEApi;
import appeng.api.config.*;
import appeng.api.implementations.tiles.IColorableTile;
import appeng.api.implementations.tiles.IMEChest;
import appeng.api.networking.GridFlags;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridNode;
import appeng.api.networking.energy.IEnergyGrid;
import appeng.api.networking.events.*;
import appeng.api.networking.events.MENetworkPowerStorage.PowerEventType;
import appeng.api.networking.security.*;
import appeng.api.networking.storage.IBaseMonitor;
import appeng.api.networking.storage.IStorageGrid;
import appeng.api.storage.*;
import appeng.api.storage.data.IAEFluidStack;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IAEStack;
import appeng.api.util.AEColor;
import appeng.api.util.IConfigManager;
import appeng.helpers.IPriorityHost;
import appeng.me.GridAccessException;
import appeng.me.storage.MEInventoryHandler;
import appeng.tile.TileEvent;
import appeng.tile.events.TileEventType;
import appeng.tile.grid.AENetworkPowerTile;
import appeng.tile.inventory.AppEngInternalInventory;
import appeng.tile.inventory.InvOperation;
import appeng.util.ConfigManager;
import appeng.util.IConfigManagerHost;
import appeng.util.Platform;
import appeng.util.item.AEFluidStack;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTankInfo;
import net.minecraftforge.fluids.IFluidHandler;
public class TileChest extends AENetworkPowerTile
implements IMEChest, IFluidHandler, ITerminalHost, IPriorityHost, IConfigManagerHost,
IColorableTile {
protected static final ChestNoHandler NO_HANDLER = new ChestNoHandler();
protected static final int[] SIDES = { 0 };
protected static final int[] FRONT = { 1 };
protected static final int[] NO_SLOTS = {};
protected final AppEngInternalInventory inv = new AppEngInternalInventory(this, 2);
protected final BaseActionSource mySrc = new MachineSource(this);
protected final IConfigManager config = new ConfigManager(this);
protected ItemStack storageType;
protected long lastStateChange = 0;
protected int priority = 0;
protected int state = 0;
protected boolean wasActive = false;
protected AEColor paintedColor = AEColor.Transparent;
protected boolean isCached = false;
protected ICellHandler cellHandler;
protected MEMonitorHandler itemCell;
protected MEMonitorHandler fluidCell;
public TileChest() {
this.setInternalMaxPower(PowerMultiplier.CONFIG.multiply(40));
this.getProxy().setFlags(GridFlags.REQUIRE_CHANNEL);
this.config.registerSetting(Settings.SORT_BY, SortOrder.NAME);
this.config.registerSetting(Settings.VIEW_MODE, ViewItems.ALL);
this.config.registerSetting(Settings.SORT_DIRECTION, SortDir.ASCENDING);
this.setInternalPublicPowerStorage(true);
this.setInternalPowerFlow(AccessRestriction.WRITE);
}
@Override
protected void PowerEvent(final PowerEventType x) {
if (x == PowerEventType.REQUEST_POWER) {
try {
this.getProxy().getGrid().postEvent(
new MENetworkPowerStorage(this, PowerEventType.REQUEST_POWER)
);
} catch (final GridAccessException e) {
// :(
}
} else {
this.recalculateDisplay();
}
}
protected void recalculateDisplay() {
final int oldState = this.state;
for (int x = 0; x < this.getCellCount(); x++) {
this.state |= (this.getCellStatus(x) << (3 * x));
}
if (this.isPowered()) {
this.state |= 0x40;
} else {
this.state &= ~0x40;
}
final boolean currentActive = this.getProxy().isActive();
if (this.wasActive != currentActive) {
this.wasActive = currentActive;
try {
this.getProxy().getGrid().postEvent(new MENetworkCellArrayUpdate());
} catch (final GridAccessException e) {
// :P
}
}
if (oldState != this.state) {
this.markForUpdate();
}
}
@Override
public int getCellCount() {
return 1;
}
protected IMEInventoryHandler getHandler(final StorageChannel channel)
throws ChestNoHandler {
if (!this.isCached) {
this.itemCell = null;
this.fluidCell = null;
final ItemStack is = this.inv.getStackInSlot(1);
if (is != null) {
this.isCached = true;
this.cellHandler = AEApi.instance().registries().cell().getHandler(is);
if (this.cellHandler != null) {
double power = 1.0;
final IMEInventoryHandler<IAEItemStack> itemCell
= this.cellHandler.getCellInventory(
is, this, StorageChannel.ITEMS
);
final IMEInventoryHandler<IAEFluidStack> fluidCell
= this.cellHandler.getCellInventory(
is, this, StorageChannel.FLUIDS
);
if (itemCell != null) {
power += this.cellHandler.cellIdleDrain(is, itemCell);
} else if (fluidCell != null) {
power += this.cellHandler.cellIdleDrain(is, fluidCell);
}
this.getProxy().setIdlePowerUsage(power);
this.itemCell = this.wrap(itemCell);
this.fluidCell = this.wrap(fluidCell);
}
}
}
switch (channel) {
case FLUIDS:
if (this.fluidCell == null) {
throw NO_HANDLER;
}
return this.fluidCell;
case ITEMS:
if (this.itemCell == null) {
throw NO_HANDLER;
}
return this.itemCell;
default:
}
return null;
}
protected <StackType extends IAEStack> MEMonitorHandler<StackType>
wrap(final IMEInventoryHandler h) {
if (h == null) {
return null;
}
final MEInventoryHandler ih = new MEInventoryHandler(h, h.getChannel());
ih.setPriority(this.priority);
final MEMonitorHandler<StackType> g = new ChestMonitorHandler<StackType>(ih);
g.addListener(new ChestNetNotifier(h.getChannel()), g);
return g;
}
@Override
public int getCellStatus(final int slot) {
if (Platform.isClient()) {
return (this.state >> (slot * 3)) & 3;
}
final ItemStack cell = this.inv.getStackInSlot(1);
final ICellHandler ch = AEApi.instance().registries().cell().getHandler(cell);
if (ch != null) {
try {
final IMEInventoryHandler handler = this.getHandler(StorageChannel.ITEMS);
if (handler instanceof ChestMonitorHandler) {
return ch.getStatusForCell(
cell, ((ChestMonitorHandler) handler).getInternalHandler()
);
}
} catch (final ChestNoHandler ignored) {}
try {
final IMEInventoryHandler handler
= this.getHandler(StorageChannel.FLUIDS);
if (handler instanceof ChestMonitorHandler) {
return ch.getStatusForCell(
cell, ((ChestMonitorHandler) handler).getInternalHandler()
);
}
} catch (final ChestNoHandler ignored) {}
}
return 0;
}
@Override
public boolean isPowered() {
if (Platform.isClient()) {
return (this.state & 0x40) == 0x40;
}
boolean gridPowered = this.getAECurrentPower() > 64;
if (!gridPowered) {
try {
gridPowered = this.getProxy().getEnergy().isNetworkPowered();
} catch (final GridAccessException ignored) {}
}
return super.getAECurrentPower() > 1 || gridPowered;
}
@Override
public boolean isCellBlinking(final int slot) {
final long now = this.worldObj.getTotalWorldTime();
if (now - this.lastStateChange > 8) {
return false;
}
return ((this.state >> (slot * 3 + 2)) & 0x01) == 0x01;
}
@Override
protected double extractAEPower(final double amt, final Actionable mode) {
double stash = 0.0;
try {
final IEnergyGrid eg = this.getProxy().getEnergy();
stash = eg.extractAEPower(amt, mode, PowerMultiplier.ONE);
if (stash >= amt) {
return stash;
}
} catch (final GridAccessException e) {
// no grid :(
}
// local battery!
return super.extractAEPower(amt - stash, mode) + stash;
}
@TileEvent(TileEventType.TICK)
public void Tick_TileChest() {
if (this.worldObj.isRemote) {
return;
}
final double idleUsage = this.getProxy().getIdlePowerUsage();
try {
if (!this.getProxy().getEnergy().isNetworkPowered()) {
final double powerUsed = this.extractAEPower(
idleUsage, Actionable.MODULATE, PowerMultiplier.CONFIG
); // drain
if (powerUsed + 0.1 >= idleUsage != (this.state & 0x40) > 0) {
this.recalculateDisplay();
}
}
} catch (final GridAccessException e) {
final double powerUsed = this.extractAEPower(
this.getProxy().getIdlePowerUsage(),
Actionable.MODULATE,
PowerMultiplier.CONFIG
); // drain
if (powerUsed + 0.1 >= idleUsage != (this.state & 0x40) > 0) {
this.recalculateDisplay();
}
}
if (this.inv.getStackInSlot(0) != null) {
this.tryToStoreContents();
}
}
@TileEvent(TileEventType.NETWORK_WRITE)
public void writeToStream_TileChest(final ByteBuf data) {
if (this.worldObj.getTotalWorldTime() - this.lastStateChange > 8) {
this.state = 0;
} else {
this.state &= 0x24924924; // just keep the blinks...
}
for (int x = 0; x < this.getCellCount(); x++) {
this.state |= (this.getCellStatus(x) << (3 * x));
}
if (this.isPowered()) {
this.state |= 0x40;
} else {
this.state &= ~0x40;
}
data.writeByte(this.state);
data.writeByte(this.paintedColor.ordinal());
final ItemStack is = this.inv.getStackInSlot(1);
if (is == null) {
data.writeInt(0);
} else {
data.writeInt(
(is.getItemDamage() << Platform.DEF_OFFSET)
| Item.getIdFromItem(is.getItem())
);
}
}
@TileEvent(TileEventType.NETWORK_READ)
public boolean readFromStream_TileChest(final ByteBuf data) {
final int oldState = this.state;
final ItemStack oldType = this.storageType;
this.state = data.readByte();
final AEColor oldPaintedColor = this.paintedColor;
this.paintedColor = AEColor.values()[data.readByte()];
final int item = data.readInt();
if (item == 0) {
this.storageType = null;
} else {
this.storageType = new ItemStack(
Item.getItemById(item & 0xffff), 1, item >> Platform.DEF_OFFSET
);
}
this.lastStateChange = this.worldObj.getTotalWorldTime();
return oldPaintedColor != this.paintedColor
|| (this.state & 0xDB6DB6DB) != (oldState & 0xDB6DB6DB)
|| !Platform.isSameItemPrecise(oldType, this.storageType);
}
@TileEvent(TileEventType.WORLD_NBT_READ)
public void readFromNBT_TileChest(final NBTTagCompound data) {
this.config.readFromNBT(data);
this.priority = data.getInteger("priority");
if (data.hasKey("paintedColor")) {
this.paintedColor = AEColor.values()[data.getByte("paintedColor")];
}
}
@TileEvent(TileEventType.WORLD_NBT_WRITE)
public void writeToNBT_TileChest(final NBTTagCompound data) {
this.config.writeToNBT(data);
data.setInteger("priority", this.priority);
data.setByte("paintedColor", (byte) this.paintedColor.ordinal());
}
@MENetworkEventSubscribe
public void powerRender(final MENetworkPowerStatusChange c) {
this.recalculateDisplay();
}
@MENetworkEventSubscribe
public void channelRender(final MENetworkChannelsChanged c) {
this.recalculateDisplay();
}
@Override
public IMEMonitor getItemInventory() {
return this.itemCell;
}
@Override
public IMEMonitor getFluidInventory() {
return this.fluidCell;
}
@Override
public IInventory getInternalInventory() {
return this.inv;
}
@Override
public void setInventorySlotContents(final int i, final ItemStack itemstack) {
this.inv.setInventorySlotContents(i, itemstack);
this.tryToStoreContents();
}
@Override
public void onChangeInventory(
final IInventory inv,
final int slot,
final InvOperation mc,
final ItemStack removed,
final ItemStack added
) {
if (slot == 1) {
this.itemCell = null;
this.fluidCell = null;
this.isCached = false; // recalculate the storage cell.
try {
this.getProxy().getGrid().postEvent(new MENetworkCellArrayUpdate());
final IStorageGrid gs = this.getProxy().getStorage();
Platform.postChanges(gs, removed, added, this.mySrc);
} catch (final GridAccessException ignored) {}
// update the neighbors
if (this.worldObj != null) {
Platform.notifyBlocksOfNeighbors(
this.worldObj, this.xCoord, this.yCoord, this.zCoord
);
this.markForUpdate();
}
}
}
@Override
public boolean
canInsertItem(final int slotIndex, final ItemStack insertingItem, final int side) {
if (slotIndex == 1) {
if (AEApi.instance().registries().cell().getCellInventory(
insertingItem, this, StorageChannel.ITEMS
)
!= null) {
return true;
}
if (AEApi.instance().registries().cell().getCellInventory(
insertingItem, this, StorageChannel.FLUIDS
)
!= null) {
return true;
}
} else {
try {
final IMEInventory<IAEItemStack> cell
= this.getHandler(StorageChannel.ITEMS);
final IAEItemStack returns = cell.injectItems(
AEApi.instance().storage().createItemStack(this.inv.getStackInSlot(0)
),
Actionable.SIMULATE,
this.mySrc
);
return returns == null
|| returns.getStackSize() != insertingItem.stackSize;
} catch (final ChestNoHandler ignored) {}
}
return false;
}
@Override
public boolean
canExtractItem(final int slotIndex, final ItemStack extractedItem, final int side) {
return slotIndex == 1;
}
@Override
public int[] getAccessibleSlotsBySide(final ForgeDirection side) {
if (ForgeDirection.SOUTH == side) {
return FRONT;
}
if (this.isPowered()) {
try {
if (this.getHandler(StorageChannel.ITEMS) != null) {
return SIDES;
}
} catch (final ChestNoHandler e) {
// nope!
}
}
return NO_SLOTS;
}
protected void tryToStoreContents() {
try {
if (this.getStackInSlot(0) != null) {
final IMEInventory<IAEItemStack> cell
= this.getHandler(StorageChannel.ITEMS);
final IAEItemStack returns = Platform.poweredInsert(
this,
cell,
AEApi.instance().storage().createItemStack(this.inv.getStackInSlot(0)
),
this.mySrc
);
if (returns == null) {
this.inv.setInventorySlotContents(0, null);
} else {
this.inv.setInventorySlotContents(0, returns.getItemStack());
}
}
} catch (final ChestNoHandler ignored) {}
}
@Override
public List<IMEInventoryHandler> getCellArray(final StorageChannel channel) {
if (this.getProxy().isActive()) {
try {
return Collections.singletonList(this.getHandler(channel));
} catch (final ChestNoHandler e) {
// :P
}
}
return new ArrayList<IMEInventoryHandler>();
}
@Override
public int getPriority() {
return this.priority;
}
@Override
public void setPriority(final int newValue) {
this.priority = newValue;
this.itemCell = null;
this.fluidCell = null;
this.isCached = false; // recalculate the storage cell.
try {
this.getProxy().getGrid().postEvent(new MENetworkCellArrayUpdate());
} catch (final GridAccessException e) {
// :P
}
}
@Override
public void blinkCell(final int slot) {
final long now = this.worldObj.getTotalWorldTime();
if (now - this.lastStateChange > 8) {
this.state = 0;
}
this.lastStateChange = now;
this.state |= 1 << (slot * 3 + 2);
this.recalculateDisplay();
}
@Override
public int
fill(final ForgeDirection from, final FluidStack resource, final boolean doFill) {
final double req = resource.amount / 500.0;
final double available
= this.extractAEPower(req, Actionable.SIMULATE, PowerMultiplier.CONFIG);
if (available >= req - 0.01) {
try {
final IMEInventoryHandler h = this.getHandler(StorageChannel.FLUIDS);
this.extractAEPower(req, Actionable.MODULATE, PowerMultiplier.CONFIG);
final IAEStack results = h.injectItems(
AEFluidStack.create(resource),
doFill ? Actionable.MODULATE : Actionable.SIMULATE,
this.mySrc
);
if (results == null) {
return resource.amount;
}
return resource.amount - (int) results.getStackSize();
} catch (final ChestNoHandler ignored) {}
}
return 0;
}
@Override
public FluidStack
drain(final ForgeDirection from, final FluidStack resource, final boolean doDrain) {
return null;
}
@Override
public FluidStack
drain(final ForgeDirection from, final int maxDrain, final boolean doDrain) {
return null;
}
@Override
public boolean canFill(final ForgeDirection from, final Fluid fluid) {
try {
final IMEInventoryHandler h = this.getHandler(StorageChannel.FLUIDS);
return h.canAccept(AEFluidStack.create(new FluidStack(fluid, 1)));
} catch (final ChestNoHandler ignored) {}
return false;
}
@Override
public boolean canDrain(final ForgeDirection from, final Fluid fluid) {
return false;
}
@Override
public FluidTankInfo[] getTankInfo(final ForgeDirection from) {
try {
final IMEInventoryHandler h = this.getHandler(StorageChannel.FLUIDS);
if (h.getChannel() == StorageChannel.FLUIDS) {
return new FluidTankInfo[] { new FluidTankInfo(null, 1) }; // eh?
}
} catch (final ChestNoHandler ignored) {}
return null;
}
@Override
public IStorageMonitorable
getMonitorable(final ForgeDirection side, final BaseActionSource src) {
if (Platform.canAccess(this.getProxy(), src) && side != this.getForward()) {
return this;
}
return null;
}
public ItemStack getStorageType() {
if (this.isPowered()) {
return this.storageType;
}
return null;
}
@Override
public IConfigManager getConfigManager() {
return this.config;
}
@Override
public void updateSetting(
final IConfigManager manager, final Enum settingName, final Enum newValue
) {}
public boolean openGui(
final EntityPlayer p, final ICellHandler ch, final ItemStack cell, final int side
) {
try {
final IMEInventoryHandler invHandler = this.getHandler(StorageChannel.ITEMS);
if (ch != null && invHandler != null) {
ch.openChestGui(p, this, ch, invHandler, cell, StorageChannel.ITEMS);
return true;
}
} catch (final ChestNoHandler e) {
// :P
}
try {
final IMEInventoryHandler invHandler = this.getHandler(StorageChannel.FLUIDS);
if (ch != null && invHandler != null) {
ch.openChestGui(p, this, ch, invHandler, cell, StorageChannel.FLUIDS);
return true;
}
} catch (final ChestNoHandler e) {
// :P
}
return false;
}
@Override
public AEColor getColor() {
return this.paintedColor;
}
@Override
public boolean recolourBlock(
final ForgeDirection side, final AEColor newPaintedColor, final EntityPlayer who
) {
if (this.paintedColor == newPaintedColor) {
return false;
}
this.paintedColor = newPaintedColor;
this.markDirty();
this.markForUpdate();
return true;
}
@Override
public void saveChanges(final IMEInventory cellInventory) {
this.worldObj.markTileEntityChunkModified(
this.xCoord, this.yCoord, this.zCoord, this
);
}
protected static class ChestNoHandler extends Exception {
protected static final long serialVersionUID = 7995805326136526631L;
}
protected class ChestNetNotifier<T extends IAEStack<T>>
implements IMEMonitorHandlerReceiver<T> {
protected final StorageChannel chan;
public ChestNetNotifier(final StorageChannel chan) {
this.chan = chan;
}
@Override
public boolean isValid(final Object verificationToken) {
if (this.chan == StorageChannel.ITEMS) {
return verificationToken == TileChest.this.itemCell;
}
if (this.chan == StorageChannel.FLUIDS) {
return verificationToken == TileChest.this.fluidCell;
}
return false;
}
@Override
public void postChange(
final IBaseMonitor<T> monitor,
final Iterable<T> change,
final BaseActionSource source
) {
if (source == TileChest.this.mySrc
|| (source instanceof PlayerSource
&& ((PlayerSource) source).via == TileChest.this)) {
try {
if (TileChest.this.getProxy().isActive()) {
TileChest.this.getProxy()
.getStorage()
.postAlterationOfStoredItems(
this.chan, change, TileChest.this.mySrc
);
}
} catch (final GridAccessException e) {
// :(
}
}
TileChest.this.blinkCell(0);
}
@Override
public void onListUpdate() {
// not used here
}
}
protected class ChestMonitorHandler<T extends IAEStack> extends MEMonitorHandler<T> {
public ChestMonitorHandler(final IMEInventoryHandler<T> t) {
super(t);
}
protected IMEInventoryHandler<T> getInternalHandler() {
final IMEInventoryHandler<T> h = this.getHandler();
if (h instanceof MEInventoryHandler) {
return (IMEInventoryHandler<T>) ((MEInventoryHandler) h).getInternal();
}
return this.getHandler();
}
@Override
public T
injectItems(final T input, final Actionable mode, final BaseActionSource src) {
if (src.isPlayer()
&& !this.securityCheck(
((PlayerSource) src).player, SecurityPermissions.INJECT
)) {
return input;
}
return super.injectItems(input, mode, src);
}
protected boolean securityCheck(
final EntityPlayer player, final SecurityPermissions requiredPermission
) {
if (TileChest.this.getTile() instanceof IActionHost
&& requiredPermission != null) {
final IGridNode gn
= ((IActionHost) TileChest.this.getTile()).getActionableNode();
if (gn != null) {
final IGrid g = gn.getGrid();
if (g != null) {
final boolean requirePower = false;
if (requirePower) {
final IEnergyGrid eg = g.getCache(IEnergyGrid.class);
if (!eg.isNetworkPowered()) {
return false;
}
}
final ISecurityGrid sg = g.getCache(ISecurityGrid.class);
if (sg.hasPermission(player, requiredPermission)) {
return true;
}
}
}
return false;
}
return true;
}
@Override
public T
extractItems(final T request, final Actionable mode, final BaseActionSource src) {
if (src.isPlayer()
&& !this.securityCheck(
((PlayerSource) src).player, SecurityPermissions.EXTRACT
)) {
return null;
}
return super.extractItems(request, mode, src);
}
}
}