/** * Copyright (c) 2011-2015, SpaceToad and the BuildCraft Team * http://www.mod-buildcraft.com * <p/> * BuildCraft is distributed under the terms of the Minecraft Mod Public * License 1.0, or MMPL. Please check the contents of the license located in * http://www.mod-buildcraft.com/MMPL-1.0.txt */ package buildcraft.transport; import java.util.List; import org.apache.logging.log4j.Level; import io.netty.buffer.ByteBuf; import net.minecraft.block.Block; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraftforge.common.util.Constants; 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 cofh.api.energy.IEnergyHandler; import buildcraft.BuildCraftCore; import buildcraft.BuildCraftTransport; import buildcraft.api.core.BCLog; import buildcraft.api.core.EnumColor; import buildcraft.api.core.IIconProvider; import buildcraft.api.core.ISerializable; import buildcraft.api.core.Position; import buildcraft.api.gates.IGateExpansion; import buildcraft.api.power.IRedstoneEngineReceiver; import buildcraft.api.tiles.IDebuggable; import buildcraft.api.transport.IPipe; import buildcraft.api.transport.IPipeConnection; import buildcraft.api.transport.IPipeTile; import buildcraft.api.transport.PipeManager; import buildcraft.api.transport.PipeWire; import buildcraft.api.transport.pluggable.IFacadePluggable; import buildcraft.api.transport.pluggable.PipePluggable; import buildcraft.core.DefaultProps; import buildcraft.core.internal.IDropControlInventory; import buildcraft.core.lib.ITileBufferHolder; import buildcraft.core.lib.TileBuffer; import buildcraft.core.lib.network.IGuiReturnHandler; import buildcraft.core.lib.network.ISyncedTile; import buildcraft.core.lib.network.Packet; import buildcraft.core.lib.network.PacketTileState; import buildcraft.core.lib.utils.Utils; import buildcraft.transport.ItemFacade.FacadeState; import buildcraft.transport.gates.GateFactory; import buildcraft.transport.gates.GatePluggable; import buildcraft.transport.pluggable.PlugPluggable; public class TileGenericPipe extends TileEntity implements IFluidHandler, IPipeTile, ITileBufferHolder, IEnergyHandler, IDropControlInventory, ISyncedTile, ISolidSideTile, IGuiReturnHandler, IRedstoneEngineReceiver, IDebuggable, IPipeConnection { public boolean initialized = false; public final PipeRenderState renderState = new PipeRenderState(); public final PipePluggableState pluggableState = new PipePluggableState(); public final CoreState coreState = new CoreState(); public boolean[] pipeConnectionsBuffer = new boolean[6]; public Pipe pipe; public int redstoneInput; public int[] redstoneInputSide = new int[ForgeDirection.VALID_DIRECTIONS.length]; protected boolean deletePipe = false; protected boolean sendClientUpdate = false; protected boolean blockNeighborChange = false; protected int blockNeighborChangedSides = 0; protected boolean refreshRenderState = false; protected boolean pipeBound = false; protected boolean resyncGateExpansions = false; protected boolean attachPluggables = false; protected SideProperties sideProperties = new SideProperties(); private TileBuffer[] tileBuffer; private int glassColor = -1; public static class CoreState implements ISerializable { public int pipeId = -1; @Override public void writeData(ByteBuf data) { data.writeInt(pipeId); } @Override public void readData(ByteBuf data) { pipeId = data.readInt(); } } public static class SideProperties { PipePluggable[] pluggables = new PipePluggable[ForgeDirection.VALID_DIRECTIONS.length]; public void writeToNBT(NBTTagCompound nbt) { for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { PipePluggable pluggable = pluggables[i]; final String key = "pluggable[" + i + "]"; if (pluggable == null) { nbt.removeTag(key); } else { NBTTagCompound pluggableData = new NBTTagCompound(); pluggableData.setString("pluggableName", PipeManager.getPluggableName(pluggable.getClass())); pluggable.writeToNBT(pluggableData); nbt.setTag(key, pluggableData); } } } public void readFromNBT(NBTTagCompound nbt) { for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { final String key = "pluggable[" + i + "]"; if (!nbt.hasKey(key)) { continue; } try { NBTTagCompound pluggableData = nbt.getCompoundTag(key); Class<?> pluggableClass = null; // Migration support for 6.1.x/6.2.x if (pluggableData.hasKey("pluggableClass")) { String c = pluggableData.getString("pluggableClass"); if ("buildcraft.transport.gates.ItemGate$GatePluggable".equals(c)) { pluggableClass = GatePluggable.class; } else if ("buildcraft.transport.ItemFacade$FacadePluggable".equals(c)) { pluggableClass = FacadePluggable.class; } else if ("buildcraft.transport.ItemPlug$PlugPluggable".equals(c)) { pluggableClass = PlugPluggable.class; } else if ("buildcraft.transport.gates.ItemRobotStation$RobotStationPluggable".equals(c) || "buildcraft.transport.ItemRobotStation$RobotStationPluggable".equals(c)) { pluggableClass = PipeManager.getPluggableByName("robotStation"); } } else { pluggableClass = PipeManager.getPluggableByName(pluggableData.getString("pluggableName")); } if (pluggableClass != null) { if (!PipePluggable.class.isAssignableFrom(pluggableClass)) { BCLog.logger.warn("Wrong pluggable class: " + pluggableClass); continue; } PipePluggable pluggable = (PipePluggable) pluggableClass.newInstance(); pluggable.readFromNBT(pluggableData); pluggables[i] = pluggable; } } catch (Exception e) { BCLog.logger.warn("Failed to load side state"); e.printStackTrace(); } } // Migration code for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { PipePluggable pluggable = null; if (nbt.hasKey("facadeState[" + i + "]")) { pluggable = new FacadePluggable(FacadeState.readArray(nbt.getTagList("facadeState[" + i + "]", Constants.NBT.TAG_COMPOUND))); } else { // Migration support for 5.0.x and 6.0.x if (nbt.hasKey("facadeBlocks[" + i + "]")) { // 5.0.x Block block = (Block) Block.blockRegistry.getObjectById(nbt.getInteger("facadeBlocks[" + i + "]")); int blockId = nbt.getInteger("facadeBlocks[" + i + "]"); if (blockId != 0) { int metadata = nbt.getInteger("facadeMeta[" + i + "]"); pluggable = new FacadePluggable(new FacadeState[]{FacadeState.create(block, metadata)}); } } else if (nbt.hasKey("facadeBlocksStr[" + i + "][0]")) { // 6.0.x FacadeState mainState = FacadeState.create( (Block) Block.blockRegistry.getObject(nbt.getString("facadeBlocksStr[" + i + "][0]")), nbt.getInteger("facadeMeta[" + i + "][0]") ); if (nbt.hasKey("facadeBlocksStr[" + i + "][1]")) { FacadeState phasedState = FacadeState.create( (Block) Block.blockRegistry.getObject(nbt.getString("facadeBlocksStr[" + i + "][1]")), nbt.getInteger("facadeMeta[" + i + "][1]"), PipeWire.fromOrdinal(nbt.getInteger("facadeWires[" + i + "]")) ); pluggable = new FacadePluggable(new FacadeState[]{mainState, phasedState}); } else { pluggable = new FacadePluggable(new FacadeState[]{mainState}); } } } if (nbt.getBoolean("plug[" + i + "]")) { pluggable = new PlugPluggable(); } if (pluggable != null) { pluggables[i] = pluggable; } } } public void rotateLeft() { PipePluggable[] newPluggables = new PipePluggable[ForgeDirection.VALID_DIRECTIONS.length]; for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) { newPluggables[dir.getRotation(ForgeDirection.UP).ordinal()] = pluggables[dir.ordinal()]; } pluggables = newPluggables; } public boolean dropItem(TileGenericPipe pipe, ForgeDirection direction, EntityPlayer player) { boolean result = false; PipePluggable pluggable = pluggables[direction.ordinal()]; if (pluggable != null) { pluggable.onDetachedPipe(pipe, direction); if (!pipe.getWorld().isRemote) { ItemStack[] stacks = pluggable.getDropItems(pipe); if (stacks != null) { for (ItemStack stack : stacks) { Utils.dropTryIntoPlayerInventory(pipe.worldObj, pipe.xCoord, pipe.yCoord, pipe.zCoord, stack, player); } } } result = true; } return result; } public void invalidate() { for (PipePluggable p : pluggables) { if (p != null) { p.invalidate(); } } } public void validate(TileGenericPipe pipe) { for (ForgeDirection d : ForgeDirection.VALID_DIRECTIONS) { PipePluggable p = pluggables[d.ordinal()]; if (p != null) { p.validate(pipe, d); } } } } public TileGenericPipe() { } @Override public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); if (glassColor >= 0) { nbt.setByte("stainedColor", (byte) glassColor); } for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { final String key = "redstoneInputSide[" + i + "]"; nbt.setByte(key, (byte) redstoneInputSide[i]); } if (pipe != null) { nbt.setInteger("pipeId", Item.getIdFromItem(pipe.item)); pipe.writeToNBT(nbt); } else { nbt.setInteger("pipeId", coreState.pipeId); } sideProperties.writeToNBT(nbt); } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); glassColor = nbt.hasKey("stainedColor") ? nbt.getByte("stainedColor") : -1; redstoneInput = 0; for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { final String key = "redstoneInputSide[" + i + "]"; if (nbt.hasKey(key)) { redstoneInputSide[i] = nbt.getByte(key); if (redstoneInputSide[i] > redstoneInput) { redstoneInput = redstoneInputSide[i]; } } else { redstoneInputSide[i] = 0; } } coreState.pipeId = nbt.getInteger("pipeId"); pipe = BlockGenericPipe.createPipe((Item) Item.getItemById(coreState.pipeId)); bindPipe(); if (pipe != null) { pipe.readFromNBT(nbt); } else { BCLog.logger.log(Level.WARN, "Pipe failed to load from NBT at {0},{1},{2}", xCoord, yCoord, zCoord); deletePipe = true; } sideProperties.readFromNBT(nbt); attachPluggables = true; } @Override public void invalidate() { initialized = false; tileBuffer = null; if (pipe != null) { pipe.invalidate(); } sideProperties.invalidate(); super.invalidate(); } @Override public void validate() { super.validate(); initialized = false; tileBuffer = null; bindPipe(); if (pipe != null) { pipe.validate(); } sideProperties.validate(this); } protected void notifyBlockChanged() { worldObj.notifyBlockOfNeighborChange(xCoord, yCoord, zCoord, getBlock()); scheduleRenderUpdate(); sendUpdateToClient(); if (pipe != null) { pipe.scheduleWireUpdate(); } } @Override public void updateEntity() { if (!worldObj.isRemote) { if (deletePipe) { worldObj.setBlockToAir(xCoord, yCoord, zCoord); } if (pipe == null) { return; } if (!initialized) { initialize(pipe); } } if (attachPluggables) { attachPluggables = false; // Attach callback for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { if (sideProperties.pluggables[i] != null) { pipe.eventBus.registerHandler(sideProperties.pluggables[i]); sideProperties.pluggables[i].onAttachedPipe(this, ForgeDirection.getOrientation(i)); } } notifyBlockChanged(); } if (!BlockGenericPipe.isValid(pipe)) { return; } pipe.updateEntity(); for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) { PipePluggable p = getPipePluggable(direction); if (p != null) { p.update(this, direction); } } if (worldObj.isRemote) { if (resyncGateExpansions) { syncGateExpansions(); } return; } if (blockNeighborChange) { for (int i = 0; i < 6; i++) { if ((blockNeighborChangedSides & (1 << i)) != 0) { blockNeighborChangedSides ^= 1 << i; computeConnection(ForgeDirection.getOrientation(i)); } } pipe.onNeighborBlockChange(0); blockNeighborChange = false; refreshRenderState = true; } if (refreshRenderState) { refreshRenderState(); refreshRenderState = false; } if (sendClientUpdate) { sendClientUpdate = false; if (worldObj instanceof WorldServer) { WorldServer world = (WorldServer) worldObj; Packet updatePacket = getBCDescriptionPacket(); for (Object o : world.playerEntities) { EntityPlayerMP player = (EntityPlayerMP) o; if (world.getPlayerManager().isPlayerWatchingChunk(player, xCoord >> 4, zCoord >> 4)) { BuildCraftCore.instance.sendToPlayer(player, updatePacket); } } } } } public void initializeFromItemMetadata(int i) { if (i >= 1 && i <= 16) { setPipeColor((i - 1) & 15); } else { setPipeColor(-1); } } public int getItemMetadata() { return getPipeColor() >= 0 ? (1 + getPipeColor()) : 0; } public int getPipeColor() { return worldObj.isRemote ? renderState.getGlassColor() : this.glassColor; } public boolean setPipeColor(int color) { if (!worldObj.isRemote && color >= -1 && color < 16 && glassColor != color) { glassColor = color; notifyBlockChanged(); worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord, blockType); return true; } return false; } /** * PRECONDITION: worldObj must not be null */ protected void refreshRenderState() { renderState.setGlassColor((byte) glassColor); // Pipe connections; for (ForgeDirection o : ForgeDirection.VALID_DIRECTIONS) { renderState.pipeConnectionMatrix.setConnected(o, this.pipeConnectionsBuffer[o.ordinal()]); } // Pipe Textures for (int i = 0; i < 7; i++) { ForgeDirection o = ForgeDirection.getOrientation(i); renderState.textureMatrix.setIconIndex(o, pipe.getIconIndex(o)); } // WireState for (PipeWire color : PipeWire.values()) { renderState.wireMatrix.setWire(color, pipe.wireSet[color.ordinal()]); for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) { renderState.wireMatrix.setWireConnected(color, direction, pipe.isWireConnectedTo(this.getTile(direction), color, direction)); } boolean lit = pipe.wireSignalStrength[color.ordinal()] > 0; switch (color) { case RED: renderState.wireMatrix.setWireIndex(color, lit ? WireIconProvider.Texture_Red_Lit : WireIconProvider.Texture_Red_Dark); break; case BLUE: renderState.wireMatrix.setWireIndex(color, lit ? WireIconProvider.Texture_Blue_Lit : WireIconProvider.Texture_Blue_Dark); break; case GREEN: renderState.wireMatrix.setWireIndex(color, lit ? WireIconProvider.Texture_Green_Lit : WireIconProvider.Texture_Green_Dark); break; case YELLOW: renderState.wireMatrix.setWireIndex(color, lit ? WireIconProvider.Texture_Yellow_Lit : WireIconProvider.Texture_Yellow_Dark); break; default: break; } } /* TODO: Rewrite the requiresRenderUpdate API to run on the server side instead of the client side to save network bandwidth */ pluggableState.setPluggables(sideProperties.pluggables); if (renderState.isDirty()) { renderState.clean(); } sendUpdateToClient(); } public void initialize(Pipe<?> pipe) { initialized = false; this.blockType = getBlockType(); if (pipe == null) { BCLog.logger.log(Level.WARN, "Pipe failed to initialize at {0},{1},{2}, deleting", xCoord, yCoord, zCoord); worldObj.setBlockToAir(xCoord, yCoord, zCoord); return; } this.pipe = pipe; for (ForgeDirection o : ForgeDirection.VALID_DIRECTIONS) { TileEntity tile = getTile(o); if (tile instanceof ITileBufferHolder) { ((ITileBufferHolder) tile).blockCreated(o, BuildCraftTransport.genericPipeBlock, this); } if (tile instanceof IPipeTile) { ((IPipeTile) tile).scheduleNeighborChange(); } } bindPipe(); computeConnections(); scheduleNeighborChange(); scheduleRenderUpdate(); if (!pipe.isInitialized()) { pipe.initialize(); } initialized = true; } private void bindPipe() { if (!pipeBound && pipe != null) { pipe.setTile(this); coreState.pipeId = Item.getIdFromItem(pipe.item); pipeBound = true; } } public boolean isInitialized() { return initialized; } public void scheduleNeighborChange() { blockNeighborChange = true; blockNeighborChangedSides = 0x3F; } public void scheduleNeighborChange(ForgeDirection direction) { blockNeighborChange = true; blockNeighborChangedSides |= direction == ForgeDirection.UNKNOWN ? 0x3F : (1 << direction.ordinal()); } @Override public boolean canInjectItems(ForgeDirection from) { if (getPipeType() != IPipeTile.PipeType.ITEM) { return false; } return isPipeConnected(from); } @Override public int injectItem(ItemStack payload, boolean doAdd, ForgeDirection from, EnumColor color) { if (BlockGenericPipe.isValid(pipe) && pipe.transport instanceof PipeTransportItems && isPipeConnected(from) && pipe.inputOpen(from)) { if (doAdd) { Position itemPos = new Position(xCoord + 0.5, yCoord + 0.5, zCoord + 0.5, from.getOpposite()); itemPos.moveBackwards(0.4); TravelingItem pipedItem = TravelingItem.make(itemPos.x, itemPos.y, itemPos.z, payload); if (pipedItem.isCorrupted()) { return 0; } pipedItem.color = color; ((PipeTransportItems) pipe.transport).injectItem(pipedItem, itemPos.orientation); } return payload.stackSize; } return 0; } @Override public int injectItem(ItemStack payload, boolean doAdd, ForgeDirection from) { return injectItem(payload, doAdd, from, null); } @Override public PipeType getPipeType() { if (BlockGenericPipe.isValid(pipe)) { return pipe.transport.getPipeType(); } return null; } @Override public int x() { return xCoord; } @Override public int y() { return yCoord; } @Override public int z() { return zCoord; } /* SMP */ public Packet getBCDescriptionPacket() { bindPipe(); updateCoreState(); PacketTileState packet = new PacketTileState(this.xCoord, this.yCoord, this.zCoord); if (pipe != null && pipe.transport != null) { pipe.transport.sendDescriptionPacket(); } packet.addStateForSerialization((byte) 0, coreState); packet.addStateForSerialization((byte) 1, renderState); packet.addStateForSerialization((byte) 2, pluggableState); if (pipe instanceof ISerializable) { packet.addStateForSerialization((byte) 3, (ISerializable) pipe); } return packet; } @Override public net.minecraft.network.Packet getDescriptionPacket() { return Utils.toPacket(getBCDescriptionPacket(), 1); } public void sendUpdateToClient() { sendClientUpdate = true; } @Override public void blockRemoved(ForgeDirection from) { } public TileBuffer[] getTileCache() { if (tileBuffer == null && pipe != null) { tileBuffer = TileBuffer.makeBuffer(worldObj, xCoord, yCoord, zCoord, pipe.transport.delveIntoUnloadedChunks()); } return tileBuffer; } @Override public void blockCreated(ForgeDirection from, Block block, TileEntity tile) { TileBuffer[] cache = getTileCache(); if (cache != null) { cache[from.getOpposite().ordinal()].set(block, tile); } } @Override public Block getBlock(ForgeDirection to) { TileBuffer[] cache = getTileCache(); if (cache != null) { return cache[to.ordinal()].getBlock(); } else { return null; } } @Override public TileEntity getTile(ForgeDirection to) { return getTile(to, false); } public TileEntity getTile(ForgeDirection to, boolean forceUpdate) { TileBuffer[] cache = getTileCache(); if (cache != null) { return cache[to.ordinal()].getTile(forceUpdate); } else { return null; } } protected boolean canPipeConnect_internal(TileEntity with, ForgeDirection side) { if (!(pipe instanceof IPipeConnectionForced) || !((IPipeConnectionForced) pipe).ignoreConnectionOverrides(side)) { if (with instanceof IPipeConnection) { IPipeConnection.ConnectOverride override = ((IPipeConnection) with).overridePipeConnection(pipe.transport.getPipeType(), side.getOpposite()); if (override != IPipeConnection.ConnectOverride.DEFAULT) { return override == IPipeConnection.ConnectOverride.CONNECT; } } } if (with instanceof IPipeTile) { IPipeTile other = (IPipeTile) with; if (other.hasBlockingPluggable(side.getOpposite())) { return false; } if (other.getPipeColor() >= 0 && glassColor >= 0 && other.getPipeColor() != glassColor) { return false; } Pipe<?> otherPipe = (Pipe<?>) other.getPipe(); if (!BlockGenericPipe.isValid(otherPipe)) { return false; } if (!otherPipe.canPipeConnect(this, side.getOpposite())) { return false; } } return pipe.canPipeConnect(with, side); } /** * Checks if this tile can connect to another tile * * @param with - The other Tile * @param side - The orientation to get to the other tile ('with') * @return true if pipes are considered connected */ protected boolean canPipeConnect(TileEntity with, ForgeDirection side) { if (with == null) { return false; } if (hasBlockingPluggable(side)) { return false; } if (!BlockGenericPipe.isValid(pipe)) { return false; } return canPipeConnect_internal(with, side); } public boolean hasBlockingPluggable(ForgeDirection side) { PipePluggable pluggable = getPipePluggable(side); if (pluggable == null) { return false; } if (pluggable instanceof IPipeConnection) { IPipe neighborPipe = getNeighborPipe(side); if (neighborPipe != null) { IPipeConnection.ConnectOverride override = ((IPipeConnection) pluggable).overridePipeConnection(neighborPipe.getTile().getPipeType(), side); if (override == IPipeConnection.ConnectOverride.CONNECT) { return true; } else if (override == IPipeConnection.ConnectOverride.DISCONNECT) { return false; } } } return pluggable.isBlocking(this, side); } protected void computeConnections() { for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { computeConnection(side); } } protected void computeConnection(ForgeDirection side) { TileBuffer[] cache = getTileCache(); if (cache == null) { return; } TileBuffer t = cache[side.ordinal()]; // For blocks which are not loaded, keep the old connection value. if (t.exists() || !initialized) { t.refresh(); pipeConnectionsBuffer[side.ordinal()] = canPipeConnect(t.getTile(), side); } } @Override public boolean isPipeConnected(ForgeDirection with) { if (worldObj.isRemote) { return renderState.pipeConnectionMatrix.isConnected(with); } else { return pipeConnectionsBuffer[with.ordinal()]; } } @Override public boolean doDrop() { if (BlockGenericPipe.isValid(pipe)) { return pipe.doDrop(); } else { return false; } } @Override public void onChunkUnload() { if (pipe != null) { pipe.onChunkUnload(); } } /** * ITankContainer implementation * */ @Override public int fill(ForgeDirection from, FluidStack resource, boolean doFill) { if (BlockGenericPipe.isValid(pipe) && pipe.transport instanceof IFluidHandler && !hasBlockingPluggable(from)) { return ((IFluidHandler) pipe.transport).fill(from, resource, doFill); } else { return 0; } } @Override public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) { if (BlockGenericPipe.isValid(pipe) && pipe.transport instanceof IFluidHandler && !hasBlockingPluggable(from)) { return ((IFluidHandler) pipe.transport).drain(from, maxDrain, doDrain); } else { return null; } } @Override public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) { if (BlockGenericPipe.isValid(pipe) && pipe.transport instanceof IFluidHandler && !hasBlockingPluggable(from)) { return ((IFluidHandler) pipe.transport).drain(from, resource, doDrain); } else { return null; } } @Override public boolean canFill(ForgeDirection from, Fluid fluid) { if (BlockGenericPipe.isValid(pipe) && pipe.transport instanceof IFluidHandler && !hasBlockingPluggable(from)) { return ((IFluidHandler) pipe.transport).canFill(from, fluid); } else { return false; } } @Override public boolean canDrain(ForgeDirection from, Fluid fluid) { if (BlockGenericPipe.isValid(pipe) && pipe.transport instanceof IFluidHandler && !hasBlockingPluggable(from)) { return ((IFluidHandler) pipe.transport).canDrain(from, fluid); } else { return false; } } @Override public FluidTankInfo[] getTankInfo(ForgeDirection from) { return null; } public void scheduleRenderUpdate() { refreshRenderState = true; } public boolean hasFacade(ForgeDirection direction) { if (direction == null || direction == ForgeDirection.UNKNOWN) { return false; } else { return sideProperties.pluggables[direction.ordinal()] instanceof IFacadePluggable; } } public boolean hasGate(ForgeDirection direction) { if (direction == null || direction == ForgeDirection.UNKNOWN) { return false; } else { return sideProperties.pluggables[direction.ordinal()] instanceof GatePluggable; } } public boolean setPluggable(ForgeDirection direction, PipePluggable pluggable) { return setPluggable(direction, pluggable, null); } public boolean setPluggable(ForgeDirection direction, PipePluggable pluggable, EntityPlayer player) { if (worldObj != null && worldObj.isRemote) { return false; } if (direction == null || direction == ForgeDirection.UNKNOWN) { return false; } // Remove old pluggable if (sideProperties.pluggables[direction.ordinal()] != null) { sideProperties.dropItem(this, direction, player); pipe.eventBus.unregisterHandler(sideProperties.pluggables[direction.ordinal()]); } sideProperties.pluggables[direction.ordinal()] = pluggable; if (pluggable != null) { pipe.eventBus.registerHandler(pluggable); pluggable.onAttachedPipe(this, direction); } notifyBlockChanged(); return true; } protected void updateCoreState() { } public boolean hasEnabledFacade(ForgeDirection direction) { return hasFacade(direction) && !((FacadePluggable) getPipePluggable(direction)).isTransparent(); } // Legacy public void setGate(Gate gate, int direction) { if (sideProperties.pluggables[direction] == null) { gate.setDirection(ForgeDirection.getOrientation(direction)); pipe.gates[direction] = gate; sideProperties.pluggables[direction] = new GatePluggable(gate); } } @SideOnly(Side.CLIENT) public IIconProvider getPipeIcons() { if (pipe == null) { return null; } return pipe.getIconProvider(); } @Override public ISerializable getStateInstance(byte stateId) { switch (stateId) { case 0: return coreState; case 1: return renderState; case 2: return pluggableState; case 3: return (ISerializable) pipe; } throw new RuntimeException("Unknown state requested: " + stateId + " this is a bug!"); } @Override public void afterStateUpdated(byte stateId) { if (!worldObj.isRemote) { return; } switch (stateId) { case 0: if (pipe != null) { break; } if (coreState.pipeId != 0) { initialize(BlockGenericPipe.createPipe((Item) Item.itemRegistry.getObjectById(coreState.pipeId))); } if (pipe == null) { break; } worldObj.markBlockRangeForRenderUpdate(xCoord, yCoord, zCoord, xCoord, yCoord, zCoord); break; case 1: { if (renderState.needsRenderUpdate()) { worldObj.markBlockRangeForRenderUpdate(xCoord, yCoord, zCoord, xCoord, yCoord, zCoord); renderState.clean(); } break; } case 2: { PipePluggable[] newPluggables = pluggableState.getPluggables(); // mark for render update if necessary for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { PipePluggable old = sideProperties.pluggables[i]; PipePluggable newer = newPluggables[i]; if (old == null && newer == null) { continue; } else if (old != null && newer != null && old.getClass() == newer.getClass()) { if (newer.requiresRenderUpdate(old)) { worldObj.markBlockRangeForRenderUpdate(xCoord, yCoord, zCoord, xCoord, yCoord, zCoord); break; } } else { // one of them is null but not the other, so update worldObj.markBlockRangeForRenderUpdate(xCoord, yCoord, zCoord, xCoord, yCoord, zCoord); break; } } sideProperties.pluggables = newPluggables.clone(); for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { final PipePluggable pluggable = getPipePluggable(ForgeDirection.getOrientation(i)); if (pluggable != null && pluggable instanceof GatePluggable) { final GatePluggable gatePluggable = (GatePluggable) pluggable; Gate gate = pipe.gates[i]; if (gate == null || gate.logic != gatePluggable.getLogic() || gate.material != gatePluggable.getMaterial()) { pipe.gates[i] = GateFactory.makeGate(pipe, gatePluggable.getMaterial(), gatePluggable.getLogic(), ForgeDirection.getOrientation(i)); } } else { pipe.gates[i] = null; } } syncGateExpansions(); break; } } } private void syncGateExpansions() { resyncGateExpansions = false; for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { Gate gate = pipe.gates[i]; if (gate == null) { continue; } GatePluggable gatePluggable = (GatePluggable) sideProperties.pluggables[i]; if (gatePluggable.getExpansions().length > 0) { for (IGateExpansion expansion : gatePluggable.getExpansions()) { if (expansion != null) { if (!gate.expansions.containsKey(expansion)) { gate.addGateExpansion(expansion); } } else { resyncGateExpansions = true; } } } } } @Override @SideOnly(Side.CLIENT) public double getMaxRenderDistanceSquared() { return DefaultProps.PIPE_CONTENTS_RENDER_DIST * DefaultProps.PIPE_CONTENTS_RENDER_DIST; } @Override public boolean shouldRefresh(Block oldBlock, Block newBlock, int oldMeta, int newMeta, World world, int x, int y, int z) { return oldBlock != newBlock; } @Override public boolean isSolidOnSide(ForgeDirection side) { if (hasPipePluggable(side) && getPipePluggable(side).isSolidOnSide(this, side)) { return true; } if (BlockGenericPipe.isValid(pipe) && pipe instanceof ISolidSideTile) { if (((ISolidSideTile) pipe).isSolidOnSide(side)) { return true; } } return false; } @Override public PipePluggable getPipePluggable(ForgeDirection side) { if (side == null || side == ForgeDirection.UNKNOWN) { return null; } return sideProperties.pluggables[side.ordinal()]; } @Override public boolean hasPipePluggable(ForgeDirection side) { if (side == null || side == ForgeDirection.UNKNOWN) { return false; } return sideProperties.pluggables[side.ordinal()] != null; } public Block getBlock() { return getBlockType(); } @Override public World getWorld() { return worldObj; } public boolean isUseableByPlayer(EntityPlayer player) { return worldObj.getTileEntity(xCoord, yCoord, zCoord) == this; } @Override public void writeGuiData(ByteBuf data) { if (BlockGenericPipe.isValid(pipe) && pipe instanceof IGuiReturnHandler) { ((IGuiReturnHandler) pipe).writeGuiData(data); } } @Override public void readGuiData(ByteBuf data, EntityPlayer sender) { if (BlockGenericPipe.isValid(pipe) && pipe instanceof IGuiReturnHandler) { ((IGuiReturnHandler) pipe).readGuiData(data, sender); } } private IEnergyHandler internalGetEnergyHandler(ForgeDirection side) { if (hasPipePluggable(side)) { PipePluggable pluggable = getPipePluggable(side); if (pluggable instanceof IEnergyHandler) { return (IEnergyHandler) pluggable; } else if (pluggable.isBlocking(this, side)) { return null; } } if (pipe instanceof IEnergyHandler) { return (IEnergyHandler) pipe; } return null; } @Override public boolean canConnectEnergy(ForgeDirection from) { IEnergyHandler handler = internalGetEnergyHandler(from); if (handler != null) { return handler.canConnectEnergy(from); } else { return false; } } @Override public int receiveEnergy(ForgeDirection from, int maxReceive, boolean simulate) { IEnergyHandler handler = internalGetEnergyHandler(from); if (handler != null) { return handler.receiveEnergy(from, maxReceive, simulate); } else { return 0; } } @Override public int extractEnergy(ForgeDirection from, int maxExtract, boolean simulate) { IEnergyHandler handler = internalGetEnergyHandler(from); if (handler != null) { return handler.extractEnergy(from, maxExtract, simulate); } else { return 0; } } @Override public int getEnergyStored(ForgeDirection from) { IEnergyHandler handler = internalGetEnergyHandler(from); if (handler != null) { return handler.getEnergyStored(from); } else { return 0; } } @Override public int getMaxEnergyStored(ForgeDirection from) { IEnergyHandler handler = internalGetEnergyHandler(from); if (handler != null) { return handler.getMaxEnergyStored(from); } else { return 0; } } @Override public Block getNeighborBlock(ForgeDirection dir) { return getBlock(dir); } @Override public TileEntity getNeighborTile(ForgeDirection dir) { return getTile(dir); } @Override public IPipe getNeighborPipe(ForgeDirection dir) { TileEntity neighborTile = getTile(dir); if (neighborTile instanceof IPipeTile) { return ((IPipeTile) neighborTile).getPipe(); } else { return null; } } @Override public IPipe getPipe() { return pipe; } @Override public boolean canConnectRedstoneEngine(ForgeDirection side) { if (pipe instanceof IRedstoneEngineReceiver) { return ((IRedstoneEngineReceiver) pipe).canConnectRedstoneEngine(side); } else { return (getPipeType() != PipeType.POWER) && (getPipeType() != PipeType.STRUCTURE); } } @Override public void getDebugInfo(List<String> info, ForgeDirection side, ItemStack debugger, EntityPlayer player) { if (pipe instanceof IDebuggable) { ((IDebuggable) pipe).getDebugInfo(info, side, debugger, player); } if (pipe.transport instanceof IDebuggable) { ((IDebuggable) pipe.transport).getDebugInfo(info, side, debugger, player); } if (getPipePluggable(side) != null && getPipePluggable(side) instanceof IDebuggable) { ((IDebuggable) getPipePluggable(side)).getDebugInfo(info, side, debugger, player); } } @Override public ConnectOverride overridePipeConnection(PipeType type, ForgeDirection with) { if (type == PipeType.POWER && hasPipePluggable(with) && getPipePluggable(with) instanceof IEnergyHandler) { return ConnectOverride.CONNECT; } return ConnectOverride.DEFAULT; } }