private PowerReceiver getReceiverOnSide(ForgeDirection side) {
   TileEntity tile = tiles[side.ordinal()];
   if (!(tile instanceof IPowerReceptor)) return null;
   IPowerReceptor receptor = (IPowerReceptor) tile;
   PowerReceiver receiver = receptor.getPowerReceiver(side.getOpposite());
   if (receiver == null) return null;
   if (!receiver.getType().canReceiveFromPipes()) return null;
   return receiver;
 }
  private void sendPower() {
    for (ForgeDirection s : ForgeDirection.VALID_DIRECTIONS) {
      TileEntity tile = getTileBuffer(s).getTile();

      if (tile instanceof IPowerReceptor) {
        PowerReceiver receptor = ((IPowerReceptor) tile).getPowerReceiver(s.getOpposite());

        if (receptor != null) {
          receptor.receiveEnergy(PowerHandler.Type.ENGINE, energyStored / 10.0, s.getOpposite());

          energyStored = 0;
        }
      } else if (tile instanceof IEnergyHandler) {
        int energyUsed =
            ((IEnergyHandler) tile).receiveEnergy(s.getOpposite(), energyStored, false);
        energyStored -= energyUsed;
      }
    }
  }
  @Override
  public boolean canPipeConnect(TileEntity tile, ForgeDirection side) {
    if (tile instanceof TileGenericPipe) {
      Pipe pipe2 = ((TileGenericPipe) tile).pipe;
      if (BlockGenericPipe.isValid(pipe2) && !(pipe2.transport instanceof PipeTransportPower))
        return false;
      return true;
    }

    if (tile instanceof IPowerReceptor) {
      IPowerReceptor receptor = (IPowerReceptor) tile;
      PowerReceiver receiver = receptor.getPowerReceiver(side.getOpposite());
      if (receiver != null && receiver.getType().canReceiveFromPipes()) return true;
    }

    if (container.pipe instanceof PipePowerWood && tile instanceof IPowerEmitter) {
      IPowerEmitter emitter = (IPowerEmitter) tile;
      if (emitter.canEmitPowerFrom(side.getOpposite())) return true;
    }

    return false;
  }
  public boolean produceBuildCraft(ForgeDirection outputDirection) {
    if (!this.worldObj.isRemote
        && outputDirection != null
        && outputDirection != ForgeDirection.UNKNOWN) {
      float provide = this.getProvide(outputDirection);

      if (this.getEnergyStored() >= provide && provide > 0) {
        if (Compatibility.isBuildcraftLoaded()) {
          TileEntity tileEntity =
              new Vector3(this)
                  .modifyPositionFromSide(outputDirection)
                  .getTileEntity(this.worldObj);

          if (tileEntity instanceof IPowerReceptor) {
            PowerReceiver receiver =
                ((IPowerReceptor) tileEntity).getPowerReceiver(outputDirection.getOpposite());

            if (receiver != null) {
              if (receiver.powerRequest() > 0) {
                float bc3Provide = provide * Compatibility.TO_BC_RATIO;
                float energyUsed =
                    Math.min(
                        receiver.receiveEnergy(
                            Type.MACHINE, bc3Provide, outputDirection.getOpposite()),
                        bc3Provide);
                this.provideElectricity(energyUsed * Compatibility.TO_BC_RATIO, true);
              }
            }

            return true;
          }
        }
      }
    }

    return false;
  }
  @Override
  public void updateEntity() {
    if (CoreProxy.proxy.isRenderWorld(container.worldObj)) return;

    step();

    init();

    // Send the power to nearby pipes who requested it

    System.arraycopy(displayPower, 0, prevDisplayPower, 0, 6);
    Arrays.fill(displayPower, 0.0F);

    for (int i = 0; i < 6; ++i) {
      if (internalPower[i] > 0) {
        float totalPowerQuery = 0;

        for (int j = 0; j < 6; ++j) {
          if (j != i && powerQuery[j] > 0)
            if (tiles[j] instanceof TileGenericPipe || tiles[j] instanceof IPowerReceptor) {
              totalPowerQuery += powerQuery[j];
            }
        }

        for (int j = 0; j < 6; ++j) {
          if (j != i && powerQuery[j] > 0) {
            float watts = 0.0F;

            PowerReceiver prov = getReceiverOnSide(ForgeDirection.VALID_DIRECTIONS[j]);
            if (prov != null && prov.powerRequest() > 0) {
              watts = (internalPower[i] / totalPowerQuery) * powerQuery[j];
              watts =
                  prov.receiveEnergy(
                      Type.PIPE, watts, ForgeDirection.VALID_DIRECTIONS[j].getOpposite());
              internalPower[i] -= watts;
            } else if (tiles[j] instanceof TileGenericPipe) {
              watts = (internalPower[i] / totalPowerQuery) * powerQuery[j];
              TileGenericPipe nearbyTile = (TileGenericPipe) tiles[j];

              PipeTransportPower nearbyTransport = (PipeTransportPower) nearbyTile.pipe.transport;

              watts =
                  nearbyTransport.receiveEnergy(
                      ForgeDirection.VALID_DIRECTIONS[j].getOpposite(), watts);
              internalPower[i] -= watts;
            }

            displayPower[j] += watts;
            displayPower[i] += watts;
          }
        }
      }
    }

    double highestPower = 0;
    for (int i = 0; i < 6; i++) {
      displayPower[i] =
          (prevDisplayPower[i] * (DISPLAY_SMOOTHING - 1.0F) + displayPower[i]) / DISPLAY_SMOOTHING;
      if (displayPower[i] > highestPower) {
        highestPower = displayPower[i];
      }
    }

    overload += highestPower > maxPower * 0.95 ? 1 : -1;
    if (overload < 0) {
      overload = 0;
    }
    if (overload > OVERLOAD_TICKS) {
      overload = OVERLOAD_TICKS;
    }

    // Compute the tiles requesting energy that are not power pipes

    for (int i = 0; i < 6; ++i) {
      PowerReceiver prov = getReceiverOnSide(ForgeDirection.VALID_DIRECTIONS[i]);
      if (prov != null) {
        float request = prov.powerRequest();

        if (request > 0) {
          requestEnergy(ForgeDirection.VALID_DIRECTIONS[i], request);
        }
      }
    }

    // Sum the amount of energy requested on each side

    int[] transferQuery = new int[6];

    for (int i = 0; i < 6; ++i) {
      transferQuery[i] = 0;

      for (int j = 0; j < 6; ++j) {
        if (j != i) {
          transferQuery[i] += powerQuery[j];
        }
      }

      transferQuery[i] = Math.min(transferQuery[i], maxPower);
    }

    // Transfer the requested energy to nearby pipes

    for (int i = 0; i < 6; ++i) {
      if (transferQuery[i] != 0) {
        if (tiles[i] != null) {
          TileEntity entity = tiles[i];

          if (entity instanceof TileGenericPipe) {
            TileGenericPipe nearbyTile = (TileGenericPipe) entity;

            if (nearbyTile.pipe == null) {
              continue;
            }

            PipeTransportPower nearbyTransport = (PipeTransportPower) nearbyTile.pipe.transport;
            nearbyTransport.requestEnergy(
                ForgeDirection.VALID_DIRECTIONS[i].getOpposite(), transferQuery[i]);
          }
        }
      }
    }

    if (tracker.markTimeIfDelay(container.worldObj, 2 * BuildCraftCore.updateFactor)) {
      PacketPowerUpdate packet =
          new PacketPowerUpdate(container.xCoord, container.yCoord, container.zCoord);

      double displayFactor = MAX_DISPLAY / 1024.0;
      for (int i = 0; i < clientDisplayPower.length; i++) {
        clientDisplayPower[i] = (short) (displayPower[i] * displayFactor + .9999);
      }

      packet.displayPower = clientDisplayPower;
      packet.overload = isOverloaded();
      CoreProxy.proxy.sendToPlayers(
          packet.getPacket(),
          container.worldObj,
          container.xCoord,
          container.yCoord,
          container.zCoord,
          DefaultProps.PIPE_CONTENTS_RENDER_DIST);
    }
  }
  /**
   * Complete the energy transfer. Called internally on server tick end.
   *
   * @return Amount of energy SENT to all acceptors
   */
  private float doProduce() {
    float sent = 0.0F;

    if (!this.availableAcceptors.isEmpty()) {
      float energyNeeded = this.totalRequested;
      float energyAvailable = this.totalEnergy;
      float reducor = 1.0F;
      float energyStorageReducor = 1.0F;

      if (energyNeeded > energyAvailable) {
        // If not enough energy, try reducing what goes into energy storage (if any)
        energyNeeded -= this.totalStorageExcess;
        // If there's still not enough, put the minimum into energy storage (if any) and, anyhow,
        // reduce everything proportionately
        if (energyNeeded > energyAvailable) {
          energyStorageReducor = 0F;
          reducor = energyAvailable / energyNeeded;
        } else {
          // Energyavailable exceeds the total needed but only if storage does not fill all in one
          // go - this is a common situation
          energyStorageReducor = (energyAvailable - energyNeeded) / this.totalStorageExcess;
        }
      }

      float currentSending;
      float sentToAcceptor;
      int tierProduced = Math.min(this.producersTierGC, this.networkTierGC);

      TileEntity debugTE = null;
      try {
        for (TileEntity tileEntity : this.availableAcceptors) {
          debugTE = tileEntity;
          // Exit the loop if there is no energy left at all (should normally not happen, should be
          // some even for the last acceptor)
          if (sent >= energyAvailable) {
            break;
          }

          // The base case is to give each acceptor what it is requesting
          currentSending = this.energyRequests.get(tileEntity);

          // If it's an energy store, we may need to damp it down if energyStorageReducor is less
          // than 1
          if (currentSending > EnergyNetwork.ENERGY_STORAGE_LEVEL) {
            currentSending =
                EnergyNetwork.ENERGY_STORAGE_LEVEL
                    + (currentSending - EnergyNetwork.ENERGY_STORAGE_LEVEL) * energyStorageReducor;
          }

          // Reduce everything proportionately if there is not enough energy for all needs
          currentSending *= reducor;

          if (currentSending > energyAvailable - sent) {
            currentSending = energyAvailable - sent;
          }

          ForgeDirection sideFrom = this.availableconnectedDirections.get(tileEntity);

          if (tileEntity instanceof IElectrical) {
            sentToAcceptor =
                ((IElectrical) tileEntity)
                    .receiveElectricity(sideFrom, currentSending, tierProduced, true);
          } else if (isRF2Loaded && tileEntity instanceof IEnergyReceiver) {
            final int currentSendinginRF =
                (currentSending >= Integer.MAX_VALUE / EnergyConfigHandler.TO_RF_RATIO)
                    ? Integer.MAX_VALUE
                    : (int) (currentSending * EnergyConfigHandler.TO_RF_RATIO);
            sentToAcceptor =
                ((IEnergyReceiver) tileEntity).receiveEnergy(sideFrom, currentSendinginRF, false)
                    / EnergyConfigHandler.TO_RF_RATIO;
          } else if (isMekLoaded && tileEntity instanceof IStrictEnergyAcceptor) {
            sentToAcceptor =
                (float)
                        ((IStrictEnergyAcceptor) tileEntity)
                            .transferEnergyToAcceptor(
                                sideFrom, currentSending * EnergyConfigHandler.TO_MEKANISM_RATIO)
                    / EnergyConfigHandler.TO_MEKANISM_RATIO;
          } else if (isRF1Loaded && tileEntity instanceof IEnergyHandler) {
            final int currentSendinginRF =
                (currentSending >= Integer.MAX_VALUE / EnergyConfigHandler.TO_RF_RATIO)
                    ? Integer.MAX_VALUE
                    : (int) (currentSending * EnergyConfigHandler.TO_RF_RATIO);
            sentToAcceptor =
                ((IEnergyHandler) tileEntity).receiveEnergy(sideFrom, currentSendinginRF, false)
                    / EnergyConfigHandler.TO_RF_RATIO;
          } else if (isIC2Loaded && tileEntity instanceof IEnergySink) {
            double energySendingIC2 = currentSending * EnergyConfigHandler.TO_IC2_RATIO;
            if (energySendingIC2 >= 1D) {
              double result = 0;
              try {
                if (EnergyUtil.voltageParameterIC2) {
                  result =
                      (Double)
                          EnergyUtil.injectEnergyIC2.invoke(
                              tileEntity, sideFrom, energySendingIC2, 120D);
                } else {
                  result =
                      (Double)
                          EnergyUtil.injectEnergyIC2.invoke(tileEntity, sideFrom, energySendingIC2);
                }
              } catch (Exception ex) {
                if (ConfigManagerCore.enableDebug) {
                  ex.printStackTrace();
                }
              }
              sentToAcceptor = currentSending - (float) result / EnergyConfigHandler.TO_IC2_RATIO;
              if (sentToAcceptor < 0F) {
                sentToAcceptor = 0F;
              }
            } else {
              sentToAcceptor = 0F;
            }
          } else if (isBCLoaded
              && EnergyConfigHandler.getBuildcraftVersion() == 6
              && MjAPI.getMjBattery(tileEntity, MjAPI.DEFAULT_POWER_FRAMEWORK, sideFrom) != null)
          // New BC API
          {
            sentToAcceptor =
                (float)
                        MjAPI.getMjBattery(tileEntity, MjAPI.DEFAULT_POWER_FRAMEWORK, sideFrom)
                            .addEnergy(currentSending * EnergyConfigHandler.TO_BC_RATIO)
                    / EnergyConfigHandler.TO_BC_RATIO;
          } else if (isBCLoaded && tileEntity instanceof IPowerReceptor)
          // Legacy BC API
          {
            PowerReceiver receiver = ((IPowerReceptor) tileEntity).getPowerReceiver(sideFrom);

            if (receiver != null) {
              double toSendBC =
                  Math.min(
                      currentSending * EnergyConfigHandler.TO_BC_RATIO, receiver.powerRequest());
              sentToAcceptor =
                  (float)
                          receiver.receiveEnergy(
                              buildcraft.api.power.PowerHandler.Type.PIPE, toSendBC, sideFrom)
                      / EnergyConfigHandler.TO_BC_RATIO;
            } else {
              sentToAcceptor = 0F;
            }
          } else {
            sentToAcceptor = 0F;
          }

          if (sentToAcceptor / currentSending > 1.002F && sentToAcceptor > 0.01F) {
            if (!this.spamstop) {
              FMLLog.info(
                  "Energy network: acceptor took too much energy, offered "
                      + currentSending
                      + ", took "
                      + sentToAcceptor
                      + ". "
                      + tileEntity.toString());
              this.spamstop = true;
            }
            sentToAcceptor = currentSending;
          }

          sent += sentToAcceptor;
        }
      } catch (Exception e) {
        GCLog.severe("DEBUG Energy network loop issue, please report this");
        if (debugTE != null)
          GCLog.severe(
              "Problem was likely caused by tile in dim "
                  + debugTE.getWorldObj().provider.dimensionId
                  + " at "
                  + debugTE.xCoord
                  + ","
                  + debugTE.yCoord
                  + ","
                  + debugTE.zCoord
                  + " Type:"
                  + debugTE.getClass().getSimpleName());
      }
    }

    if (EnergyNetwork.tickCount % 200 == 0) {
      this.spamstop = false;
    }

    float returnvalue = sent;
    if (returnvalue > this.totalEnergy) {
      returnvalue = this.totalEnergy;
    }
    if (returnvalue < 0F) {
      returnvalue = 0F;
    }
    return returnvalue;
  }
  /**
   * Refreshes all tiles in network, and updates requested energy
   *
   * @param ignoreTiles TileEntities to ignore for energy calculations.
   */
  private void doTickStartCalc() {
    this.tickDone = EnergyNetwork.tickCount;
    this.totalSent = 0F;
    this.refreshAcceptors();

    if (!EnergyUtil.initialisedIC2Methods) {
      EnergyUtil.initialiseIC2Methods();
    }

    if (this.conductors.size() == 0) {
      return;
    }

    this.loopPrevention = true;

    this.availableAcceptors.clear();
    this.availableconnectedDirections.clear();
    this.energyRequests.clear();
    this.totalRequested = 0.0F;
    this.totalStorageExcess = 0F;

    if (!this.connectedAcceptors.isEmpty()) {
      float e;
      final Iterator<ForgeDirection> acceptorDirection = this.connectedDirections.iterator();
      for (TileEntity acceptor : this.connectedAcceptors) {
        // This tries all sides of the acceptor which are connected (see refreshAcceptors())
        ForgeDirection sideFrom = acceptorDirection.next();

        // But the grid will only put energy into the acceptor from one side - once it's in
        // availableAcceptors
        if (!this.ignoreAcceptors.contains(acceptor)
            && !this.availableAcceptors.contains(acceptor)) {
          e = 0.0F;

          if (acceptor instanceof IElectrical) {
            e = ((IElectrical) acceptor).getRequest(sideFrom);
          } else if (isRF2Loaded && acceptor instanceof IEnergyReceiver) {
            e =
                ((IEnergyReceiver) acceptor).receiveEnergy(sideFrom, Integer.MAX_VALUE, true)
                    / EnergyConfigHandler.TO_RF_RATIO;
          } else if (isMekLoaded && acceptor instanceof IStrictEnergyAcceptor) {
            e =
                (float)
                    ((((IStrictEnergyAcceptor) acceptor).getMaxEnergy()
                            - ((IStrictEnergyAcceptor) acceptor).getEnergy())
                        / EnergyConfigHandler.TO_MEKANISM_RATIO);
          } else if (isRF1Loaded && acceptor instanceof IEnergyHandler) {
            e =
                ((IEnergyHandler) acceptor).receiveEnergy(sideFrom, Integer.MAX_VALUE, true)
                    / EnergyConfigHandler.TO_RF_RATIO;
          } else if (isIC2Loaded && acceptor instanceof IEnergySink) {
            double result = 0;
            try {
              result = (Double) EnergyUtil.demandedEnergyIC2.invoke(acceptor);
            } catch (Exception ex) {
              if (ConfigManagerCore.enableDebug) {
                ex.printStackTrace();
              }
            }
            // Cap IC2 power transfer at 128EU/t for standard Alu wire, 256EU/t for heavy Alu wire
            result = Math.max(result, (this.networkTierGC == 2) ? 256D : 128D);
            e = (float) result / EnergyConfigHandler.TO_IC2_RATIO;
          } else if (isBCLoaded
              && EnergyConfigHandler.getBuildcraftVersion() == 6
              && MjAPI.getMjBattery(acceptor, MjAPI.DEFAULT_POWER_FRAMEWORK, sideFrom) != null)
          // New BC API
          {
            e =
                (float)
                        MjAPI.getMjBattery(acceptor, MjAPI.DEFAULT_POWER_FRAMEWORK, sideFrom)
                            .getEnergyRequested()
                    / EnergyConfigHandler.TO_BC_RATIO;
          } else if (isBCLoaded && acceptor instanceof IPowerReceptor)
          // Legacy BC API
          {
            PowerReceiver BCreceiver = ((IPowerReceptor) acceptor).getPowerReceiver(sideFrom);
            if (BCreceiver != null) {
              e = (float) BCreceiver.powerRequest() / EnergyConfigHandler.TO_BC_RATIO;
            }
          }

          if (e > 0.0F) {
            this.availableAcceptors.add(acceptor);
            this.availableconnectedDirections.put(acceptor, sideFrom);
            this.energyRequests.put(acceptor, Float.valueOf(e));
            this.totalRequested += e;
            if (e > EnergyNetwork.ENERGY_STORAGE_LEVEL) {
              this.totalStorageExcess += e - EnergyNetwork.ENERGY_STORAGE_LEVEL;
            }
          }
        }
      }
    }

    this.loopPrevention = false;
  }