Java tutorial
/* * BluSunrize * Copyright (c) 2017 * * This code is licensed under "Blu's License of Common Sense" * Details can be found in the license file in the root folder of this project */ package blusunrize.immersiveengineering.common.blocks.metal; import blusunrize.immersiveengineering.api.ApiUtils; import blusunrize.immersiveengineering.api.IEEnums.SideConfig; import blusunrize.immersiveengineering.api.energy.immersiveflux.FluxStorage; import blusunrize.immersiveengineering.api.energy.wires.IImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler; import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler.AbstractConnection; import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler.Connection; import blusunrize.immersiveengineering.api.energy.wires.TileEntityImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.WireType; import blusunrize.immersiveengineering.common.Config; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IBlockBounds; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IDirectionalTile; import blusunrize.immersiveengineering.common.util.EnergyHelper; import blusunrize.immersiveengineering.common.util.EnergyHelper.IEForgeEnergyWrapper; import blusunrize.immersiveengineering.common.util.EnergyHelper.IIEInternalFluxHandler; import blusunrize.immersiveengineering.common.util.Utils; import net.minecraft.entity.EntityLivingBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nullable; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.function.Consumer; //@Optional.Interface(iface = "ic2.api.energy.tile.IEnergySink", modid = "IC2") public class TileEntityConnectorLV extends TileEntityImmersiveConnectable implements ITickable, IDirectionalTile, IIEInternalFluxHandler, IBlockBounds//, ic2.api.energy.tile.IEnergySink { boolean inICNet = false; public EnumFacing facing = EnumFacing.DOWN; public int currentTickToMachine = 0; public int currentTickToNet = 0; public static int[] connectorInputValues = Config.IEConfig.Machines.wireConnectorInput; private FluxStorage energyStorage = new FluxStorage(getMaxInput(), getMaxInput(), 0); boolean firstTick = true; @Override public void update() { if (!world.isRemote) { // if(Lib.IC2 && !this.inICNet) // { // IC2Helper.loadIC2Tile(this); // this.inICNet = true; // } if (energyStorage.getEnergyStored() > 0) { int temp = this.transferEnergy(energyStorage.getEnergyStored(), true, 0); if (temp > 0) { energyStorage.modifyEnergyStored(-this.transferEnergy(temp, false, 0)); markDirty(); } addAvailableEnergy(-1F, null); notifyAvailableEnergy(energyStorage.getEnergyStored(), null); } currentTickToMachine = 0; currentTickToNet = 0; } else if (firstTick) { Set<Connection> conns = ImmersiveNetHandler.INSTANCE.getConnections(world, pos); if (conns != null) for (Connection conn : conns) if (pos.compareTo(conn.end) < 0 && world.isBlockLoaded(conn.end)) this.markContainingBlockForUpdate(null); firstTick = false; } } // @Override // public void invalidate() // { // super.invalidate(); // unload(); // } // void unload() // { // if(Lib.IC2 && this.inICNet) // { // IC2Helper.unloadIC2Tile(this); // this.inICNet = false; // } // } // @Override // public void onChunkUnload() // { // unload(); // } @Override public EnumFacing getFacing() { return this.facing; } @Override public void setFacing(EnumFacing facing) { this.facing = facing; } @Override public int getFacingLimitation() { return 0; } @Override public boolean mirrorFacingOnPlacement(EntityLivingBase placer) { return true; } @Override public boolean canHammerRotate(EnumFacing side, float hitX, float hitY, float hitZ, EntityLivingBase entity) { return false; } @Override public boolean canRotate(EnumFacing axis) { return false; } @Override protected boolean canTakeLV() { return true; } @Override public boolean isEnergyOutput() { BlockPos outPos = getPos().offset(facing); if (isRelay()) return false; TileEntity tile = Utils.getExistingTileEntity(world, outPos); return EnergyHelper.isFluxReceiver(tile, facing.getOpposite()); } @Override public int outputEnergy(int amount, boolean simulate, int energyType) { if (isRelay()) return 0; int acceptanceLeft = getMaxOutput() - currentTickToMachine; if (acceptanceLeft <= 0) return 0; int toAccept = Math.min(acceptanceLeft, amount); TileEntity capacitor = Utils.getExistingTileEntity(world, getPos().offset(facing)); int ret = EnergyHelper.insertFlux(capacitor, facing.getOpposite(), toAccept, simulate); // if(capacitor instanceof IFluxReceiver && ((IFluxReceiver)capacitor).canConnectEnergy(facing.getOpposite())) // { // ret = ((IFluxReceiver)capacitor).receiveEnergy(facing.getOpposite(), toAccept, simulate); // } // else if(capacitor instanceof IEnergyReceiver && ((IEnergyReceiver)capacitor).canConnectEnergy(facing.getOpposite())) // { // ret = ((IEnergyReceiver)capacitor).receiveEnergy(facing.getOpposite(), toAccept, simulate); // } // else if(Lib.IC2 && IC2Helper.isAcceptingEnergySink(capacitor, this, fd.getOpposite())) // { // double left = IC2Helper.injectEnergy(capacitor, fd.getOpposite(), ModCompatability.convertRFtoEU(toAccept, getIC2Tier()), canTakeHV()?(256*256): canTakeMV()?(128*128) : (32*32), simulate); // ret = amount-ModCompatability.convertEUtoRF(left); // } // else if(Lib.GREG && GregTechHelper.gregtech_isValidEnergyOutput(capacitor)) // { // long translAmount = (long)ModCompatability.convertRFtoEU(toAccept, getIC2Tier()); // long accepted = GregTechHelper.gregtech_outputGTPower(capacitor, (byte)fd.getOpposite().ordinal(), translAmount, 1L, simulate); // int reConv = ModCompatability.convertEUtoRF(accepted); // ret = reConv; // } if (!simulate) currentTickToMachine += ret; return ret; } @Override public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) { super.writeCustomNBT(nbt, descPacket); nbt.setInteger("facing", facing.ordinal()); energyStorage.writeToNBT(nbt); } @Override public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) { super.readCustomNBT(nbt, descPacket); facing = EnumFacing.byIndex(nbt.getInteger("facing")); energyStorage.readFromNBT(nbt); } @Override public Vec3d getConnectionOffset(Connection con) { EnumFacing side = facing.getOpposite(); double conRadius = con.cableType.getRenderDiameter() / 2; return new Vec3d(.5 - conRadius * side.getXOffset(), .5 - conRadius * side.getYOffset(), .5 - conRadius * side.getZOffset()); } @SideOnly(Side.CLIENT) private AxisAlignedBB renderAABB; @SideOnly(Side.CLIENT) @Override public AxisAlignedBB getRenderBoundingBox() { // if(renderAABB==null) // { // if(Config.getBoolean("increasedRenderboxes")) // { int inc = getRenderRadiusIncrease(); return new AxisAlignedBB(this.pos.getX() - inc, this.pos.getY() - inc, this.pos.getZ() - inc, this.pos.getX() + inc + 1, this.pos.getY() + inc + 1, this.pos.getZ() + inc + 1); // renderAABB = new AxisAlignedBB(this.pos.getX()-inc,this.pos.getY()-inc,this.pos.getZ()-inc, this.pos.getX()+inc+1,this.pos.getY()+inc+1,this.pos.getZ()+inc+1); // } // else // renderAABB = super.getRenderBoundingBox(); // } // return renderAABB; } int getRenderRadiusIncrease() { return WireType.COPPER.getMaxLength(); } IEForgeEnergyWrapper energyWrapper; @Override public IEForgeEnergyWrapper getCapabilityWrapper(EnumFacing facing) { if (facing != this.facing || isRelay()) return null; if (energyWrapper == null || energyWrapper.side != this.facing) energyWrapper = new IEForgeEnergyWrapper(this, this.facing); return energyWrapper; } @Override public FluxStorage getFluxStorage() { return energyStorage; } @Override public SideConfig getEnergySideConfig(EnumFacing facing) { return (!isRelay() && facing == this.facing) ? SideConfig.INPUT : SideConfig.NONE; } @Override public boolean canConnectEnergy(EnumFacing from) { if (isRelay()) return false; return from == facing; } @Override public int receiveEnergy(EnumFacing from, int energy, boolean simulate) { if (world.isRemote || isRelay()) return 0; energy = Math.min(getMaxInput() - currentTickToNet, energy); if (energy <= 0) return 0; int accepted = Math.min(Math.min(getMaxOutput(), getMaxInput()), energy); accepted = Math.min(getMaxOutput() - energyStorage.getEnergyStored(), accepted); if (accepted <= 0) return 0; if (!simulate) { energyStorage.modifyEnergyStored(accepted); notifyAvailableEnergy(accepted, null); currentTickToNet += accepted; markDirty(); } return accepted; } @Override public int getEnergyStored(EnumFacing from) { if (isRelay()) return 0; return energyStorage.getEnergyStored(); } @Override public int getMaxEnergyStored(EnumFacing from) { if (isRelay()) return 0; return getMaxInput(); } @Override public int extractEnergy(EnumFacing from, int energy, boolean simulate) { return 0; } public int transferEnergy(int energy, boolean simulate, final int energyType) { int received = 0; if (!world.isRemote) { Set<AbstractConnection> outputs = ImmersiveNetHandler.INSTANCE .getIndirectEnergyConnections(Utils.toCC(this), world, true); int powerLeft = Math.min(Math.min(getMaxOutput(), getMaxInput()), energy); final int powerForSort = powerLeft; if (outputs.isEmpty()) return 0; int sum = 0; //TreeMap to prioritize outputs close to this connector if more energy is requested than available //(energy will be provided to the nearby outputs rather than some random ones) Map<AbstractConnection, Integer> powerSorting = new TreeMap<>(); for (AbstractConnection con : outputs) if (con.isEnergyOutput) { IImmersiveConnectable end = ApiUtils.toIIC(con.end, world); if (con.cableType != null && end != null) { int atmOut = Math.min(powerForSort, con.cableType.getTransferRate()); int tempR = end.outputEnergy(atmOut, true, energyType); if (tempR > 0) { powerSorting.put(con, tempR); sum += tempR; } } } if (sum > 0) for (AbstractConnection con : powerSorting.keySet()) { IImmersiveConnectable end = ApiUtils.toIIC(con.end, world); if (con.cableType != null && end != null) { float prio = powerSorting.get(con) / (float) sum; int output = Math.min(MathHelper.ceil(powerForSort * prio), powerLeft); int tempR = end.outputEnergy(Math.min(output, con.cableType.getTransferRate()), true, energyType); int r = tempR; int maxInput = getMaxInput(); tempR -= (int) Math.max(0, Math.floor(tempR * con.getPreciseLossRate(tempR, maxInput))); end.outputEnergy(tempR, simulate, energyType); HashSet<IImmersiveConnectable> passedConnectors = new HashSet<IImmersiveConnectable>(); float intermediaryLoss = 0; //<editor-fold desc="Transfer rate and passed energy"> for (Connection sub : con.subConnections) { float length = sub.length / (float) sub.cableType.getMaxLength(); float baseLoss = (float) sub.cableType.getLossRatio(); float mod = (((maxInput - tempR) / (float) maxInput) / .25f) * .1f; intermediaryLoss = MathHelper .clamp(intermediaryLoss + length * (baseLoss + baseLoss * mod), 0, 1); int transferredPerCon = ImmersiveNetHandler.INSTANCE .getTransferedRates(world.provider.getDimension()).getOrDefault(sub, 0); transferredPerCon += r; if (!simulate) { ImmersiveNetHandler.INSTANCE.getTransferedRates(world.provider.getDimension()) .put(sub, transferredPerCon); IImmersiveConnectable subStart = ApiUtils.toIIC(sub.start, world); IImmersiveConnectable subEnd = ApiUtils.toIIC(sub.end, world); if (subStart != null && passedConnectors.add(subStart)) subStart.onEnergyPassthrough(r - r * intermediaryLoss); if (subEnd != null && passedConnectors.add(subEnd)) subEnd.onEnergyPassthrough(r - r * intermediaryLoss); } } //</editor-fold> received += r; powerLeft -= r; if (powerLeft <= 0) break; } } } return received; } private void notifyAvailableEnergy(int energyStored, @Nullable Set<AbstractConnection> outputs) { if (outputs == null) outputs = ImmersiveNetHandler.INSTANCE.getIndirectEnergyConnections(pos, world, true); for (AbstractConnection con : outputs) { IImmersiveConnectable end = ApiUtils.toIIC(con.end, world); if (con.cableType != null && end != null && end.allowEnergyToPass(null)) { Pair<Float, Consumer<Float>> e = getEnergyForConnection(con); end.addAvailableEnergy(e.getKey(), e.getValue()); } } } private Pair<Float, Consumer<Float>> getEnergyForConnection(@Nullable AbstractConnection c) { float loss = c != null ? c.getAverageLossRate() : 0; float max = (1 - loss) * energyStorage.getEnergyStored(); Consumer<Float> extract = (energy) -> { energyStorage.modifyEnergyStored((int) (-energy / (1 - loss))); }; return new ImmutablePair<>(max, extract); } public int getMaxInput() { return connectorInputValues[0]; } public int getMaxOutput() { return connectorInputValues[0]; } @Nullable @Override protected Pair<Float, Consumer<Float>> getOwnEnergy() { return getEnergyForConnection(null); } @Override public float[] getBlockBounds() { float length = this instanceof TileEntityRelayHV ? .875f : this instanceof TileEntityConnectorHV ? .75f : this instanceof TileEntityConnectorMV ? .5625f : .5f; float wMin = this instanceof TileEntityConnectorStructural ? .25f : .3125f; float wMax = this instanceof TileEntityConnectorStructural ? .75f : .6875f; switch (facing.getOpposite()) { case UP: return new float[] { wMin, 0, wMin, wMax, length, wMax }; case DOWN: return new float[] { wMin, 1 - length, wMin, wMax, 1, wMax }; case SOUTH: return new float[] { wMin, wMin, 0, wMax, wMax, length }; case NORTH: return new float[] { wMin, wMin, 1 - length, wMax, wMax, 1 }; case EAST: return new float[] { 0, wMin, wMin, length, wMax, wMax }; case WEST: return new float[] { 1 - length, wMin, wMin, 1, wMax, wMax }; } return new float[] { 0, 0, 0, 1, 1, 1 }; } @Override public boolean moveConnectionTo(Connection c, BlockPos newEnd) { return true; } }