package mekanism.common.multipart; import io.netty.buffer.ByteBuf; import java.util.ArrayList; import java.util.Collections; import java.util.List; import mcmultipart.MCMultiPartMod; import mcmultipart.block.TileMultipartContainer; import mcmultipart.multipart.IMultipart; import mcmultipart.multipart.INormallyOccludingPart; import mcmultipart.multipart.Multipart; import mcmultipart.multipart.OcclusionHelper; import mcmultipart.raytrace.PartMOP; import mcmultipart.raytrace.RayTraceUtils; import mcmultipart.raytrace.RayTraceUtils.AdvancedRayTraceResultPart; import mekanism.api.Coord4D; import mekanism.api.EnumColor; import mekanism.api.IConfigurable; import mekanism.api.transmitters.IBlockableConnection; import mekanism.api.transmitters.ITransmitter; import mekanism.api.transmitters.TransmissionType; import mekanism.api.util.CapabilityUtils; import mekanism.common.MekanismItems; import mekanism.common.Tier; import mekanism.common.base.ITileNetwork; import mekanism.common.capabilities.Capabilities; import mekanism.common.multipart.TransmitterType.Size; import mekanism.common.util.MekanismUtils; import net.minecraft.block.Block; import net.minecraft.block.properties.IProperty; import net.minecraft.block.state.BlockStateContainer; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.PacketBuffer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.BlockRenderLayer; import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.IStringSerializable; import net.minecraft.util.ITickable; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.util.text.TextComponentString; import net.minecraft.world.World; import net.minecraftforge.client.model.obj.OBJModel.OBJProperty; import net.minecraftforge.client.model.obj.OBJModel.OBJState; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.property.ExtendedBlockState; import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.IUnlistedProperty; public abstract class PartSidedPipe extends Multipart implements INormallyOccludingPart, /*ISlotOccludingPart, ISidedHollowConnect, JIconHitEffects, INeighborTileChange,*/ ITileNetwork, IBlockableConnection, IConfigurable, ITransmitter, ITickable { public static AxisAlignedBB[] smallSides = new AxisAlignedBB[7]; public static AxisAlignedBB[] largeSides = new AxisAlignedBB[7]; public int delayTicks; public EnumFacing testingSide = null; public byte currentAcceptorConnections = 0x00; public byte currentTransmitterConnections = 0x00; public boolean sendDesc = false; public boolean redstonePowered = false; public boolean redstoneReactive = false; public boolean forceUpdate = true; public boolean redstoneSet = false; public ConnectionType[] connectionTypes = {ConnectionType.NORMAL, ConnectionType.NORMAL, ConnectionType.NORMAL, ConnectionType.NORMAL, ConnectionType.NORMAL, ConnectionType.NORMAL}; public TileEntity[] cachedAcceptors = new TileEntity[6]; static { smallSides[0] = new AxisAlignedBB(0.3, 0.0, 0.3, 0.7, 0.3, 0.7); smallSides[1] = new AxisAlignedBB(0.3, 0.7, 0.3, 0.7, 1.0, 0.7); smallSides[2] = new AxisAlignedBB(0.3, 0.3, 0.0, 0.7, 0.7, 0.3); smallSides[3] = new AxisAlignedBB(0.3, 0.3, 0.7, 0.7, 0.7, 1.0); smallSides[4] = new AxisAlignedBB(0.0, 0.3, 0.3, 0.3, 0.7, 0.7); smallSides[5] = new AxisAlignedBB(0.7, 0.3, 0.3, 1.0, 0.7, 0.7); smallSides[6] = new AxisAlignedBB(0.3, 0.3, 0.3, 0.7, 0.7, 0.7); largeSides[0] = new AxisAlignedBB(0.25, 0.0, 0.25, 0.75, 0.25, 0.75); largeSides[1] = new AxisAlignedBB(0.25, 0.75, 0.25, 0.75, 1.0, 0.75); largeSides[2] = new AxisAlignedBB(0.25, 0.25, 0.0, 0.75, 0.75, 0.25); largeSides[3] = new AxisAlignedBB(0.25, 0.25, 0.75, 0.75, 0.75, 1.0); largeSides[4] = new AxisAlignedBB(0.0, 0.25, 0.25, 0.25, 0.75, 0.75); largeSides[5] = new AxisAlignedBB(0.75, 0.25, 0.25, 1.0, 0.75, 0.75); largeSides[6] = new AxisAlignedBB(0.25, 0.25, 0.25, 0.75, 0.75, 0.75); } public static IMultipart getPartType(TransmitterType type) { switch(type) { case UNIVERSAL_CABLE_BASIC: return new PartUniversalCable(Tier.CableTier.BASIC); case UNIVERSAL_CABLE_ADVANCED: return new PartUniversalCable(Tier.CableTier.ADVANCED); case UNIVERSAL_CABLE_ELITE: return new PartUniversalCable(Tier.CableTier.ELITE); case UNIVERSAL_CABLE_ULTIMATE: return new PartUniversalCable(Tier.CableTier.ULTIMATE); case MECHANICAL_PIPE_BASIC: return new PartMechanicalPipe(Tier.PipeTier.BASIC); case MECHANICAL_PIPE_ADVANCED: return new PartMechanicalPipe(Tier.PipeTier.ADVANCED); case MECHANICAL_PIPE_ELITE: return new PartMechanicalPipe(Tier.PipeTier.ELITE); case MECHANICAL_PIPE_ULTIMATE: return new PartMechanicalPipe(Tier.PipeTier.ULTIMATE); case PRESSURIZED_TUBE_BASIC: return new PartPressurizedTube(Tier.TubeTier.BASIC); case PRESSURIZED_TUBE_ADVANCED: return new PartPressurizedTube(Tier.TubeTier.ADVANCED); case PRESSURIZED_TUBE_ELITE: return new PartPressurizedTube(Tier.TubeTier.ELITE); case PRESSURIZED_TUBE_ULTIMATE: return new PartPressurizedTube(Tier.TubeTier.ULTIMATE); case LOGISTICAL_TRANSPORTER_BASIC: return new PartLogisticalTransporter(Tier.TransporterTier.BASIC); case LOGISTICAL_TRANSPORTER_ADVANCED: return new PartLogisticalTransporter(Tier.TransporterTier.ADVANCED); case LOGISTICAL_TRANSPORTER_ELITE: return new PartLogisticalTransporter(Tier.TransporterTier.ELITE); case LOGISTICAL_TRANSPORTER_ULTIMATE: return new PartLogisticalTransporter(Tier.TransporterTier.ULTIMATE); case RESTRICTIVE_TRANSPORTER: return new PartRestrictiveTransporter(); case DIVERSION_TRANSPORTER: return new PartDiversionTransporter(); case THERMODYNAMIC_CONDUCTOR_BASIC: return new PartThermodynamicConductor(Tier.ConductorTier.BASIC); case THERMODYNAMIC_CONDUCTOR_ADVANCED: return new PartThermodynamicConductor(Tier.ConductorTier.ADVANCED); case THERMODYNAMIC_CONDUCTOR_ELITE: return new PartThermodynamicConductor(Tier.ConductorTier.ELITE); case THERMODYNAMIC_CONDUCTOR_ULTIMATE: return new PartThermodynamicConductor(Tier.ConductorTier.ULTIMATE); default: return null; } } public static boolean connectionMapContainsSide(byte connections, EnumFacing side) { byte tester = (byte)(1 << side.ordinal()); return (connections & tester) > 0; } public static byte setConnectionBit(byte connections, boolean toSet, EnumFacing side) { return (byte)((connections & ~(byte)(1 << side.ordinal())) | (byte)((toSet ? 1 : 0) << side.ordinal())); } @Override public void update() { if(getWorld().isRemote) { if(delayTicks == 5) { delayTicks = 6; /* don't refresh again */ refreshConnections(); } else if(delayTicks < 5) { delayTicks++; } } if(!getWorld().isRemote) { if(forceUpdate) { refreshConnections(); forceUpdate = false; } if(sendDesc) { sendUpdatePacket(); sendDesc = false; } } } public boolean handlesRedstone() { return true; } public boolean renderCenter() { return false; } public boolean transparencyRender() { return false; } public byte getPossibleTransmitterConnections() { byte connections = 0x00; if(handlesRedstone() && redstoneReactive && redstonePowered) { return connections; } for(EnumFacing side : EnumFacing.values()) { if(canConnectMutual(side)) { TileEntity tileEntity = getWorld().getTileEntity(getPos().offset(side)); if(tileEntity != null && CapabilityUtils.hasCapability(tileEntity, Capabilities.GRID_TRANSMITTER_CAPABILITY, side.getOpposite()) && TransmissionType.checkTransmissionType(CapabilityUtils.getCapability(tileEntity, Capabilities.GRID_TRANSMITTER_CAPABILITY, side.getOpposite()), getTransmitterType().getTransmission()) && isValidTransmitter(tileEntity)) { connections |= 1 << side.ordinal(); } } } return connections; } public boolean getPossibleAcceptorConnection(EnumFacing side) { if(handlesRedstone() && redstoneReactive && redstonePowered) { return false; } if(canConnectMutual(side)) { TileEntity tileEntity = getWorld().getTileEntity(getPos().offset(side)); if(isValidAcceptor(tileEntity, side)) { if(cachedAcceptors[side.ordinal()] != tileEntity) { cachedAcceptors[side.ordinal()] = tileEntity; markDirtyAcceptor(side); } return true; } } if(cachedAcceptors[side.ordinal()] != null) { cachedAcceptors[side.ordinal()] = null; markDirtyAcceptor(side); } return false; } public boolean getPossibleTransmitterConnection(EnumFacing side) { if(handlesRedstone() && redstoneReactive && redstonePowered) { return false; } if(canConnectMutual(side)) { TileEntity tileEntity = getWorld().getTileEntity(getPos().offset(side)); if(CapabilityUtils.hasCapability(tileEntity, Capabilities.GRID_TRANSMITTER_CAPABILITY, side.getOpposite()) && TransmissionType.checkTransmissionType(CapabilityUtils.getCapability(tileEntity, Capabilities.GRID_TRANSMITTER_CAPABILITY, side.getOpposite()), getTransmitterType().getTransmission()) && isValidTransmitter(tileEntity)) { return true; } } return false; } public byte getPossibleAcceptorConnections() { byte connections = 0x00; if(handlesRedstone() && redstoneReactive && redstonePowered) { return connections; } for(EnumFacing side : EnumFacing.values()) { if(canConnectMutual(side)) { Coord4D coord = new Coord4D(getPos(), getWorld()).offset(side); if(!getWorld().isRemote && !coord.exists(getWorld())) { forceUpdate = true; continue; } TileEntity tileEntity = coord.getTileEntity(getWorld()); if(isValidAcceptor(tileEntity, side)) { if(cachedAcceptors[side.ordinal()] != tileEntity) { cachedAcceptors[side.ordinal()] = tileEntity; markDirtyAcceptor(side); } connections |= 1 << side.ordinal(); continue; } } if(cachedAcceptors[side.ordinal()] != null) { cachedAcceptors[side.ordinal()] = null; markDirtyAcceptor(side); } } return connections; } public byte getAllCurrentConnections() { return (byte)(currentTransmitterConnections | currentAcceptorConnections); } protected boolean isValidTransmitter(TileEntity tileEntity) { return true; } @Override public float getHardness(PartMOP partHit) { return 3.5F; } /* @Override public boolean occlusionTest(IMultipart other) { return NormalOcclusionTest.apply(this, other); } */ @Override public void addSelectionBoxes(List<AxisAlignedBB> list) { if(getContainer() != null) { for(EnumFacing side : EnumFacing.values()) { int ord = side.ordinal(); byte connections = getAllCurrentConnections(); if(connectionMapContainsSide(connections, side) || side == testingSide) { list.add(getTransmitterType().getSize() == Size.SMALL ? smallSides[ord] : largeSides[ord]); } } } list.add(getTransmitterType().getSize() == Size.SMALL ? smallSides[6] : largeSides[6]); } public abstract TransmitterType getTransmitterType(); @Override public void addCollisionBoxes(AxisAlignedBB mask, List<AxisAlignedBB> list, Entity collidingEntity) { if(getContainer() != null) { for(EnumFacing side : EnumFacing.values()) { int ord = side.ordinal(); byte connections = getAllCurrentConnections(); if(connectionMapContainsSide(connections, side) || side == testingSide) { AxisAlignedBB box = getTransmitterType().getSize() == Size.SMALL ? smallSides[ord] : largeSides[ord]; if(box.intersectsWith(mask)) list.add(box); } } } AxisAlignedBB box = getTransmitterType().getSize() == Size.SMALL ? smallSides[6] : largeSides[6]; if(box.intersectsWith(mask)) list.add(box); } @Override public void addOcclusionBoxes(List<AxisAlignedBB> list) { addSelectionBoxes(list); } /* @Override public EnumSet<PartSlot> getSlotMask() { return EnumSet.of(PartSlot.CENTER); } @Override public EnumSet<PartSlot> getOccludedSlots() { return EnumSet.of(PartSlot.CENTER); //TODO implement properly } @Override public TextureAtlasSprite getBreakingIcon(Object subPart, EnumFacing side) { return getCenterIcon(true); } @Override public TextureAtlasSprite getBrokenIcon(EnumFacing side) { return getCenterIcon(true); } @Override public Cuboid6 getBounds() { return getTransmitterType().getSize() == Size.SMALL ? smallSides[6] : largeSides[6]; } @Override public int getHollowSize(EnumFacing side) { EnumFacing direction = EnumFacing.getOrientation(side); if(connectionMapContainsSide(getAllCurrentConnections(), direction) || direction == testingSide) { return getTransmitterType().getSize().centerSize+1; } return 0; } */ public abstract boolean isValidAcceptor(TileEntity tile, EnumFacing side); @Override public boolean canConnectMutual(EnumFacing side) { if(!canConnect(side)) return false; TileEntity tile = getWorld().getTileEntity(getPos().offset(side)); if(!CapabilityUtils.hasCapability(tile, Capabilities.BLOCKABLE_CONNECTION_CAPABILITY, side.getOpposite())) { return true; } return CapabilityUtils.getCapability(tile, Capabilities.BLOCKABLE_CONNECTION_CAPABILITY, side.getOpposite()).canConnect(side.getOpposite()); } @Override public boolean canConnect(EnumFacing side) { if(!redstoneSet) { if(redstoneReactive) { redstonePowered = MekanismUtils.isGettingPowered(getWorld(), new Coord4D(getPos(), getWorld())); } else { redstonePowered = false; } redstoneSet = true; } if(handlesRedstone() && redstoneReactive && redstonePowered) { return false; } testingSide = side; IMultipart testPart = new OcclusionHelper.NormallyOccludingPart(getTransmitterType().getSize() == Size.SMALL ? smallSides[side.ordinal()] : largeSides[side.ordinal()]); boolean unblocked = OcclusionHelper.occlusionTest(testPart, (part) -> part==this, getContainer().getParts());//getContainer().canReplacePart(this, this); testingSide = null; return unblocked; } @Override public void readUpdatePacket(PacketBuffer packet) { currentTransmitterConnections = packet.readByte(); currentAcceptorConnections = packet.readByte(); for(int i = 0; i < 6; i++) { connectionTypes[i] = ConnectionType.values()[packet.readInt()]; } notifyPartUpdate(); markRenderUpdate(); } @Override public void writeUpdatePacket(PacketBuffer packet) { packet.writeByte(currentTransmitterConnections); packet.writeByte(currentAcceptorConnections); for(int i = 0; i < 6; i++) { packet.writeInt(connectionTypes[i].ordinal()); } } @Override public void readFromNBT(NBTTagCompound nbtTags) { super.readFromNBT(nbtTags); redstoneReactive = nbtTags.getBoolean("redstoneReactive"); for(int i = 0; i < 6; i++) { connectionTypes[i] = ConnectionType.values()[nbtTags.getInteger("connection" + i)]; } } @Override public NBTTagCompound writeToNBT(NBTTagCompound nbtTags) { super.writeToNBT(nbtTags); nbtTags.setBoolean("redstoneReactive", redstoneReactive); for(int i = 0; i < 6; i++) { nbtTags.setInteger("connection" + i, connectionTypes[i].ordinal()); } return nbtTags; } @Override public boolean onActivated(EntityPlayer player, EnumHand hand, ItemStack stack, PartMOP hit) { if(stack == null) { return false; } if(MekanismUtils.hasUsableWrench(player, getPos()) && player.isSneaking()) { if(!getWorld().isRemote) { MultipartMekanism.dropItems(this); getContainer().removePart(this); } return true; } return false; } @Override public List<ItemStack> getDrops() { return Collections.singletonList(getPickBlock(null, null)); } @Override public ItemStack getPickBlock(EntityPlayer player, PartMOP hit) { return new ItemStack(MekanismItems.PartTransmitter, 1, getTransmitterType().ordinal()); } protected void onRefresh() {} public void refreshConnections() { if(redstoneReactive) { redstonePowered = MekanismUtils.isGettingPowered(getWorld(), new Coord4D(getPos(), getWorld())); } else { redstonePowered = false; } redstoneSet = true; if(!getWorld().isRemote) { byte possibleTransmitters = getPossibleTransmitterConnections(); byte possibleAcceptors = getPossibleAcceptorConnections(); if((possibleTransmitters | possibleAcceptors) != getAllCurrentConnections()) { sendDesc = true; } currentTransmitterConnections = possibleTransmitters; currentAcceptorConnections = possibleAcceptors; } } public void refreshConnections(EnumFacing side) { if(!getWorld().isRemote) { boolean possibleTransmitter = getPossibleTransmitterConnection(side); boolean possibleAcceptor = getPossibleAcceptorConnection(side); if((possibleTransmitter || possibleAcceptor) != connectionMapContainsSide(getAllCurrentConnections(), side)) { sendDesc = true; } currentTransmitterConnections = setConnectionBit(currentTransmitterConnections, possibleTransmitter, side); currentAcceptorConnections = setConnectionBit(currentAcceptorConnections, possibleAcceptor, side); } } protected void onModeChange(EnumFacing side) { markDirtyAcceptor(side); } protected void markDirtyTransmitters() { notifyTileChange(); } protected void markDirtyAcceptor(EnumFacing side) {} public abstract void onWorldJoin(); public abstract void onWorldSeparate(); @Override public void onRemoved() { onWorldSeparate(); super.onRemoved(); } @Override public void onUnloaded() { onWorldSeparate(); super.onRemoved(); } @Override public void onAdded() { onWorldJoin(); super.onAdded(); refreshConnections(); } @Override public void onLoaded() { onWorldJoin(); super.onLoaded(); } @Override public void onNeighborTileChange(EnumFacing side) { refreshConnections(side); } @Override public void onNeighborBlockChange(Block block) { if(handlesRedstone()) { boolean prevPowered = redstonePowered; refreshConnections(); if(prevPowered != redstonePowered) { markDirtyTransmitters(); } } else { refreshConnections(); } } @Override public void onPartChanged(IMultipart part) { super.onPartChanged(part); byte transmittersBefore = currentTransmitterConnections; refreshConnections(); if(transmittersBefore != currentTransmitterConnections) { markDirtyTransmitters(); } } @Override public void handlePacketData(ByteBuf dataStream) throws Exception {} @Override public ArrayList<Object> getNetworkedData(ArrayList<Object> data) { return data; } public ConnectionType getConnectionType(EnumFacing side) { return getConnectionType(side, getAllCurrentConnections(), currentTransmitterConnections, connectionTypes); } public static ConnectionType getConnectionType(EnumFacing side, byte allConnections, byte transmitterConnections, ConnectionType[] types) { if(!connectionMapContainsSide(allConnections, side)) { return ConnectionType.NONE; } else if(connectionMapContainsSide(transmitterConnections, side)) { return ConnectionType.NORMAL; } return types[side.ordinal()]; } public List<EnumFacing> getConnections(ConnectionType type) { List<EnumFacing> sides = new ArrayList<EnumFacing>(); for(EnumFacing side : EnumFacing.values()) { if(getConnectionType(side) == type) { sides.add(side); } } return sides; } @Override public EnumActionResult onSneakRightClick(EntityPlayer player, EnumFacing side) { if(!getWorld().isRemote) { PartMOP hit = reTrace(getWorld(), getPos(), player); if(hit == null) { return EnumActionResult.PASS; } else { EnumFacing hitSide = sideHit(hit.subHit + 1); if(hitSide != null) { connectionTypes[hitSide.ordinal()] = connectionTypes[hitSide.ordinal()].next(); sendDesc = true; onModeChange(EnumFacing.getFront(hitSide.ordinal())); player.addChatMessage(new TextComponentString("Connection type changed to " + connectionTypes[hitSide.ordinal()].toString())); return EnumActionResult.SUCCESS; } else { return onConfigure(player, 6, side); } } } return EnumActionResult.SUCCESS; } private PartMOP reTrace(World world, BlockPos pos, EntityPlayer player) { Vec3d start = RayTraceUtils.getStart(player); Vec3d end = RayTraceUtils.getEnd(player); AdvancedRayTraceResultPart result = ((TileMultipartContainer)world.getTileEntity(pos)).getPartContainer().collisionRayTrace(start, end); return result == null ? null : result.hit; } protected EnumFacing sideHit(int boxIndex) { List<EnumFacing> list = new ArrayList<>(); if(getContainer() != null) { for(EnumFacing side : EnumFacing.values()) { int ord = side.ordinal(); byte connections = getAllCurrentConnections(); if(connectionMapContainsSide(connections, side)) { list.add(side); } } } if(boxIndex < list.size()) return list.get(boxIndex); return null; } protected EnumActionResult onConfigure(EntityPlayer player, int part, EnumFacing side) { return EnumActionResult.PASS; } public EnumColor getRenderColor() { return null; } @Override public EnumActionResult onRightClick(EntityPlayer player, EnumFacing side) { if(!getWorld().isRemote && handlesRedstone()) { redstoneReactive ^= true; refreshConnections(); notifyTileChange(); player.addChatMessage(new TextComponentString(EnumColor.DARK_BLUE + "[Mekanism]" + EnumColor.GREY + " Redstone sensitivity turned " + EnumColor.INDIGO + (redstoneReactive ? "on." : "off."))); } return EnumActionResult.SUCCESS; } @Override public ResourceLocation getModelPath() { return getType(); } @Override public BlockStateContainer createBlockState() { return new ExtendedBlockState(MCMultiPartMod.multipart, new IProperty[0], new IUnlistedProperty[] {OBJProperty.INSTANCE, ColorProperty.INSTANCE, ConnectionProperty.INSTANCE}); } public List<String> getVisibleGroups() { List<String> visible = new ArrayList<>(); for(EnumFacing side : EnumFacing.values()) { visible.add(side.getName() + getConnectionType(side).getName().toUpperCase()); } return visible; } @Override public IBlockState getExtendedState(IBlockState state) { ConnectionProperty connectionProp = new ConnectionProperty(getAllCurrentConnections(), currentTransmitterConnections, connectionTypes, renderCenter()); return ((IExtendedBlockState)state).withProperty(OBJProperty.INSTANCE, new OBJState(getVisibleGroups(), true)).withProperty(ConnectionProperty.INSTANCE, connectionProp); } @Override public boolean canRenderInLayer(BlockRenderLayer layer) { return layer == BlockRenderLayer.CUTOUT || (transparencyRender() && layer == BlockRenderLayer.TRANSLUCENT); } public void notifyTileChange() { MekanismUtils.notifyLoadedNeighborsOfTileChange(getWorld(), new Coord4D(getPos(), getWorld())); } @Override public boolean hasCapability(Capability<?> capability, EnumFacing facing) { return capability == Capabilities.CONFIGURABLE_CAPABILITY || capability == Capabilities.TILE_NETWORK_CAPABILITY || capability == Capabilities.BLOCKABLE_CONNECTION_CAPABILITY || super.hasCapability(capability, facing); } @Override public <T> T getCapability(Capability<T> capability, EnumFacing facing) { if(capability == Capabilities.CONFIGURABLE_CAPABILITY || capability == Capabilities.TILE_NETWORK_CAPABILITY || capability == Capabilities.BLOCKABLE_CONNECTION_CAPABILITY) { return (T)this; } return super.getCapability(capability, facing); } public static enum ConnectionType implements IStringSerializable { NORMAL, PUSH, PULL, NONE; public ConnectionType next() { if(ordinal() == values().length-1) { return NORMAL; } return values()[ordinal()+1]; } @Override public String getName() { return name().toLowerCase(); } } @Override public boolean shouldBreakingUseExtendedState() { return true; } }