package crazypants.enderio.machine.transceiver; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import net.minecraft.block.Block; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; 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; import com.enderio.core.common.util.FluidUtil; import com.enderio.core.common.util.ItemUtil; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; import com.google.common.collect.SetMultimap; import crazypants.enderio.ModObject; import crazypants.enderio.conduit.item.FilterRegister; import crazypants.enderio.conduit.item.filter.ItemFilter; import crazypants.enderio.config.Config; import crazypants.enderio.machine.AbstractPoweredTaskEntity; import crazypants.enderio.machine.ContinuousTask; import crazypants.enderio.machine.IItemBuffer; import crazypants.enderio.machine.IoMode; import crazypants.enderio.machine.SlotDefinition; import crazypants.enderio.network.PacketHandler; import crazypants.enderio.power.BasicCapacitor; import crazypants.enderio.power.ICapacitor; import crazypants.enderio.power.IInternalPowerHandler; import crazypants.enderio.power.PowerDistributor; import crazypants.enderio.rail.EnderRailController; public class TileTransceiver extends AbstractPoweredTaskEntity implements IFluidHandler, IItemBuffer, IInternalPowerHandler { //Power will only be sent to other transceivers is the buffer is higher than this amount private static final float MIN_POWER_TO_SEND = 0.5f; private final SetMultimap<ChannelType, Channel> sendChannels = MultimapBuilder.enumKeys(ChannelType.class).hashSetValues().build(); private final SetMultimap<ChannelType, Channel> recieveChannels = MultimapBuilder.enumKeys(ChannelType.class).hashSetValues().build(); private ICapacitor capacitor = new BasicCapacitor(Config.transceiverMaxIoRF * 2, 500000, Config.transceiverMaxIoRF); private boolean sendChannelsDirty = false; private boolean recieveChannelsDirty = false; private boolean registered = false; private Map<ForgeDirection, IFluidHandler> neighbourFluidHandlers = null; private PowerDistributor powerDistributor; private final EnderRailController railController; private boolean inFluidFill = false; private boolean inGetTankInfo = false; private ItemFilter sendItemFilter; private ItemFilter recieveItemFilter; private boolean bufferStacks = true; public TileTransceiver() { super(new SlotDefinition(8, 8, 0)); currentTask = new ContinuousTask(Config.transceiverUpkeepCostRF); railController = new EnderRailController(this); sendItemFilter = new ItemFilter(true); recieveItemFilter = new ItemFilter(true); } public EnderRailController getRailController() { return railController; } public boolean isRedstoneChecksPassed() { return redstoneCheckPassed; } @Override protected boolean processTasks(boolean redstoneChecksPassed) { boolean res = super.processTasks(redstoneChecksPassed); if(!redstoneChecksPassed) { return res; } //NB: Fluid done synchronously processPower(); processItems(); return res; } @Override public void doUpdate() { if (!registered && !worldObj.isRemote) { ServerChannelRegister.instance.register(this); registered = true; removeUnregsiteredChannels(sendChannels); removeUnregsiteredChannels(recieveChannels); } super.doUpdate(); if (!worldObj.isRemote) { railController.doTick(); if(sendChannelsDirty) { PacketHandler.sendToAllAround(new PacketSendRecieveChannelList(this, true), this, 256); sendChannelsDirty = false; } if(recieveChannelsDirty) { PacketHandler.sendToAllAround(new PacketSendRecieveChannelList(this, false), this, 256); recieveChannelsDirty = false; } } } @Override public void invalidate() { super.invalidate(); if(registered && worldObj != null && !worldObj.isRemote) { ServerChannelRegister.instance.dergister(this); registered = false; } } @Override public void onChunkUnload() { super.onChunkUnload(); if(registered && worldObj != null && !worldObj.isRemote) { ServerChannelRegister.instance.dergister(this); registered = false; } } private void removeUnregsiteredChannels(SetMultimap<ChannelType, Channel> chans) { List<Channel> toRemove = new ArrayList<Channel>(); for (Channel chan : chans.values()) { if (!ServerChannelRegister.instance.getChannelsForType(chan.getType()).contains(chan)) { toRemove.add(chan); } } for (Channel chan : toRemove) { removeChannel(chan, chans); } } @Override public String getMachineName() { return ModObject.blockTransceiver.unlocalisedName; } @Override public boolean isActive() { return hasPower(); } @Override public ICapacitor getCapacitor() { return capacitor; } @Override public int getPowerUsePerTick() { return Config.transceiverUpkeepCostRF; } public Set<Channel> getSendChannels(ChannelType type) { return sendChannels.get(type); } public Set<Channel> getRecieveChannels(ChannelType type) { return recieveChannels.get(type); } public void addSendChanel(Channel channel) { addChannel(channel, sendChannels); } public void addRecieveChanel(Channel channel) { addChannel(channel, recieveChannels); } public void removeSendChanel(Channel channel) { removeChannel(channel, sendChannels); } public void removeRecieveChanel(Channel channel) { removeChannel(channel, recieveChannels); } private void addChannel(Channel channel, SetMultimap<ChannelType, Channel> channels) { if (channel == null) { return; } Collection<Channel> chans = channels.get(channel.getType()); if (chans.add(channel)) { if (channels == sendChannels) { sendChannelsDirty = true; } else { recieveChannelsDirty = true; } } } private void removeChannel(Channel channel, SetMultimap<ChannelType, Channel> channnels) { if(channel == null) { return; } Set<Channel> chans = channnels.get(channel.getType()); if (chans.remove(channel)) { if (channnels == sendChannels) { sendChannelsDirty = true; } else { recieveChannelsDirty = true; } } } @Override public void readCustomNBT(NBTTagCompound nbtRoot) { super.readCustomNBT(nbtRoot); railController.readFromNBT(nbtRoot); currentTask = new ContinuousTask(Config.transceiverUpkeepCostRF); } @Override public void writeCustomNBT(NBTTagCompound nbtRoot) { super.writeCustomNBT(nbtRoot); railController.writeToNBT(nbtRoot); } @Override public void readCommon(NBTTagCompound nbtRoot) { super.readCommon(nbtRoot); readChannels(nbtRoot, sendChannels, "sendChannels"); readChannels(nbtRoot, recieveChannels, "recieveChannels"); if(nbtRoot.hasKey("sendItemFilter")) { NBTTagCompound itemRoot = nbtRoot.getCompoundTag("sendItemFilter"); sendItemFilter.copyFrom((ItemFilter) FilterRegister.loadFilterFromNbt(itemRoot)); } if(nbtRoot.hasKey("recieveItemFilter")) { NBTTagCompound itemRoot = nbtRoot.getCompoundTag("recieveItemFilter"); recieveItemFilter.copyFrom((ItemFilter) FilterRegister.loadFilterFromNbt(itemRoot)); } if(nbtRoot.hasKey("bufferStacks")) { bufferStacks = nbtRoot.getBoolean("bufferStacks"); } else { bufferStacks = true; } } static void readChannels(NBTTagCompound nbtRoot, SetMultimap<ChannelType, Channel> channels, String key) { channels.clear(); if(!nbtRoot.hasKey(key)) { return; } NBTTagList tags = (NBTTagList) nbtRoot.getTag(key); for (int i = 0; i < tags.tagCount(); i++) { NBTTagCompound chanelTag = tags.getCompoundTagAt(i); Channel channel = Channel.readFromNBT(chanelTag); if(channel != null) { channels.put(channel.getType(), channel); } } } @Override public void writeCommon(NBTTagCompound nbtRoot) { super.writeCommon(nbtRoot); NBTTagList channelTags = createTagList(sendChannels); nbtRoot.setTag("sendChannels", channelTags); channelTags = createTagList(recieveChannels); nbtRoot.setTag("recieveChannels", channelTags); if(sendItemFilter != null) { NBTTagCompound itemRoot = new NBTTagCompound(); FilterRegister.writeFilterToNbt(sendItemFilter, itemRoot); nbtRoot.setTag("sendItemFilter", itemRoot); } if(recieveItemFilter != null) { NBTTagCompound itemRoot = new NBTTagCompound(); FilterRegister.writeFilterToNbt(recieveItemFilter, itemRoot); nbtRoot.setTag("recieveItemFilter", itemRoot); } nbtRoot.setBoolean("bufferStacks", bufferStacks); } static NBTTagList createTagList(SetMultimap<ChannelType, Channel> channels) { NBTTagList res = new NBTTagList(); for (Channel channel : channels.values()) { NBTTagCompound chanTag = new NBTTagCompound(); channel.writeToNBT(chanTag); res.appendTag(chanTag); } return res; } void setSendChannels(Multimap<? extends ChannelType, ? extends Channel> channels) { sendChannels.clear(); sendChannels.putAll(channels); } void setRecieveChannels(Multimap<? extends ChannelType, ? extends Channel> channels) { recieveChannels.clear(); recieveChannels.putAll(channels); } SetMultimap<ChannelType, Channel> getSendChannels() { return sendChannels; } SetMultimap<ChannelType, Channel> getReceiveChannels() { return recieveChannels; } //---------------- Power Handling @Override public int extractEnergy(ForgeDirection from, int maxExtract, boolean simulate) { return 0; } private void processPower() { Set<Channel> sendTo = getSendChannels(ChannelType.POWER); int canSend = getMaxSendableEnergy(); if(canSend > 0 && !sendTo.isEmpty()) { Iterator<Channel> iter = sendTo.iterator(); while (canSend > 0 && iter.hasNext()) { ServerChannelRegister.instance.sendPower(this, canSend, iter.next()); canSend = getMaxSendableEnergy(); } } canSend = getMaxSendableEnergy(); if(canSend > 0 && !getRecieveChannels(ChannelType.POWER).isEmpty()) { if(powerDistributor == null) { powerDistributor = new PowerDistributor(getLocation()); } int used = powerDistributor.transmitEnergy(worldObj, canSend); usePower(used); } } private int getMaxSendableEnergy() { return getEnergyStored() - (int) (MIN_POWER_TO_SEND * getMaxEnergyStored()); } private float getEnergyStoredRatio() { return (float) getEnergyStored() / getMaxEnergyStored(); } @Override public void onNeighborBlockChange(Block blockId) { super.onNeighborBlockChange(blockId); if(powerDistributor != null) { powerDistributor.neighboursChanged(); } neighbourFluidHandlers = null; } private boolean hasRecieveChannel(Set<Channel> channels, ChannelType type) { boolean hasChannel = false; for (Channel chan : channels) { if(getRecieveChannels(type).contains(chan)) { hasChannel = true; break; } } return hasChannel; } //---------------- Fluid Handling @Override public boolean canFill(ForgeDirection from, Fluid fluid) { if(inFluidFill) { return false; } try { inFluidFill = true; if(getSendChannels(ChannelType.FLUID).isEmpty()) { return false; } return ServerChannelRegister.instance.canFill(this, getSendChannels(ChannelType.FLUID), fluid); } finally { inFluidFill = false; } } public boolean canReceive(Set<Channel> channels, Fluid fluid) { if(inFluidFill) { return false; } if(!hasRecieveChannel(channels, ChannelType.FLUID)) { return false; } Map<ForgeDirection, IFluidHandler> handlers = getNeighbouringFluidHandlers(); for (Entry<ForgeDirection, IFluidHandler> entry : handlers.entrySet()) { if(entry.getValue().canFill(entry.getKey().getOpposite(), fluid)) { return true; } } return false; } @Override public int fill(ForgeDirection from, FluidStack resource, boolean doFill) { if(inFluidFill) { return 0; } try { inFluidFill = true; if(getSendChannels(ChannelType.FLUID).isEmpty() || !redstoneCheckPassed || !getIoMode(from).canRecieveInput()) { return 0; } return ServerChannelRegister.instance.fill(this, getSendChannels(ChannelType.FLUID), resource, doFill); } finally { inFluidFill = false; } } public int recieveFluid(Set<Channel> channels, FluidStack resource, boolean doFill) { if(inFluidFill) { return 0; } if(!hasRecieveChannel(channels, ChannelType.FLUID) || !redstoneCheckPassed) { return 0; } Map<ForgeDirection, IFluidHandler> handlers = getNeighbouringFluidHandlers(); for (Entry<ForgeDirection, IFluidHandler> entry : handlers.entrySet()) { IoMode mode = getIoMode(entry.getKey()); if(mode.canOutput()) { int res = entry.getValue().fill(entry.getKey().getOpposite(), resource, doFill); if(res > 0) { return res; } } } return 0; } @Override public FluidTankInfo[] getTankInfo(ForgeDirection from) { if(inGetTankInfo) { return new FluidTankInfo[0]; } try { inGetTankInfo = true; return ServerChannelRegister.instance.getTankInfoForChannels(this, getSendChannels(ChannelType.FLUID)); } finally { inGetTankInfo = false; } } public void getRecieveTankInfo(List<FluidTankInfo> infos, Set<Channel> channels) { if(inGetTankInfo) { return; } try { inGetTankInfo = true; if(!hasRecieveChannel(channels, ChannelType.FLUID)) { return; } Map<ForgeDirection, IFluidHandler> fluidHandlers = getNeighbouringFluidHandlers(); for (Entry<ForgeDirection, IFluidHandler> entry : fluidHandlers.entrySet()) { FluidTankInfo[] tanks = entry.getValue().getTankInfo(entry.getKey().getOpposite()); if(tanks != null) { for (FluidTankInfo info : tanks) { infos.add(info); } } } } finally { inGetTankInfo = false; } } Map<ForgeDirection, IFluidHandler> getNeighbouringFluidHandlers() { if(neighbourFluidHandlers == null) { neighbourFluidHandlers = FluidUtil.getNeighbouringFluidHandlers(worldObj, getLocation()); } return neighbourFluidHandlers; } //Pulling liquids not supported @Override public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) { return null; } @Override public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) { return null; } @Override public boolean canDrain(ForgeDirection from, Fluid fluid) { return false; } //---------------- item handling @Override public int getInventoryStackLimit() { return bufferStacks ? 64 : 1; } @Override public boolean isBufferStacks() { return bufferStacks; } @Override public void setBufferStacks(boolean bufferStacks) { this.bufferStacks = bufferStacks; } private void processItems() { Set<Channel> sendItemChannels = getSendChannels(ChannelType.ITEM); if(!sendItemChannels.isEmpty()) { for (int i = slotDefinition.minInputSlot; i <= slotDefinition.maxInputSlot; i++) { ItemStack toSend = getStackInSlot(i); if(toSend != null) { ServerChannelRegister.instance.sendItem(this, sendItemChannels, i, toSend); } } } } public ItemFilter getSendItemFilter() { return sendItemFilter; } public ItemFilter getReceiveItemFilter() { return recieveItemFilter; } public ItemFilter getRecieveItemFilter() { return recieveItemFilter; } public void setRecieveItemFilter(ItemFilter recieveItemFilter) { this.recieveItemFilter = recieveItemFilter; } public void setSendItemFilter(ItemFilter sendItemFilter) { this.sendItemFilter = sendItemFilter; } @Override protected boolean isMachineItemValidForSlot(int slot, ItemStack itemstack) { if(itemstack == null) { return false; } if(slotDefinition.isInputSlot(slot)) { if(!getSendItemFilter().doesItemPassFilter(null, itemstack)) { return false; } } else if(slotDefinition.isOutputSlot(slot)) { if(!getReceiveItemFilter().doesItemPassFilter(null, itemstack)) { return false; } } return true; } @Override public boolean canInsertItem(int slot, ItemStack itemstack, int j) { if(itemstack == null) { return false; } //only allow 1 stack per type if(slotDefinition.isInputSlot(slot)) { Set<Channel> chans = getSendChannels().get(ChannelType.ITEM); if(chans == null || chans.size() == 0) { return false; } if(!getSendItemFilter().doesItemPassFilter(null, itemstack)) { return false; } for (int i = slotDefinition.getMinInputSlot(); i <= slotDefinition.getMaxInputSlot(); i++) { if(i != slot) { if(ItemUtil.areStacksEqual(itemstack, getStackInSlot(i))) { return false; } } } } else if(slotDefinition.isOutputSlot(slot)) { if(!getRecieveItemFilter().doesItemPassFilter(null, itemstack)) { return false; } for (int i = slotDefinition.getMinOutputSlot(); i <= slotDefinition.getMaxOutputSlot(); i++) { if(i != slot) { if(ItemUtil.areStacksEqual(itemstack, getStackInSlot(i))) { return false; } } } } return super.canInsertItem(slot, itemstack, j); } }