/* * This file is part of Industrial Wires. * Copyright (C) 2016-2017 malte0811 * * Industrial Wires is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Industrial Wires is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Industrial Wires. If not, see . */ package malte0811.industrialWires.blocks.wire; import blusunrize.immersiveengineering.api.ApiUtils; import blusunrize.immersiveengineering.api.TargetingInfo; 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.blocks.IEBlockInterfaces.IDirectionalTile; import ic2.api.energy.event.EnergyTileLoadEvent; import ic2.api.energy.event.EnergyTileUnloadEvent; import ic2.api.energy.tile.IEnergyAcceptor; import ic2.api.energy.tile.IEnergyEmitter; import ic2.api.energy.tile.IEnergySink; import ic2.api.energy.tile.IEnergySource; import malte0811.industrialWires.IIC2Connector; import malte0811.industrialWires.blocks.IBlockBoundsIW; import malte0811.industrialWires.wires.IC2Wiretype; import net.minecraft.entity.EntityLivingBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.Vec3d; import net.minecraftforge.common.MinecraftForge; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nonnull; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; public class TileEntityIC2ConnectorTin extends TileEntityImmersiveConnectable implements IEnergySource, IEnergySink, IDirectionalTile, ITickable, IIC2Connector, IBlockBoundsIW { EnumFacing f = EnumFacing.NORTH; boolean relay; private boolean first = true; //IC2 net to IE net buffer private double inBuffer = 0; private double maxToNet = 0; //IE net to IC2 net buffer private double outBuffer = 0; private double maxToMachine = 0; protected double maxStored = IC2Wiretype.IC2_TYPES[0].getTransferRate()/8; int tier = 1; public TileEntityIC2ConnectorTin(boolean rel) { relay = rel; } public TileEntityIC2ConnectorTin() {} @Override public void update() { if (first) { if (!world.isRemote) MinecraftForge.EVENT_BUS.post(new EnergyTileLoadEvent(this)); first = false; } if (!world.isRemote && inBuffer > .1) transferPower(); } public void transferPower() { Set conns = new HashSet<>(ImmersiveNetHandler.INSTANCE.getIndirectEnergyConnections(pos, world)); Map> maxOutputs = new HashMap<>(); double outputMax = Math.min(inBuffer, maxToNet); double sum = 0; for (AbstractConnection c:conns) { IImmersiveConnectable iic = ApiUtils.toIIC(c.end, world); if (iic instanceof IIC2Connector) { double tmp = inBuffer-((IIC2Connector)iic).insertEnergy(outputMax, true); if (tmp>.00000001) { maxOutputs.put(c, new ImmutablePair<>((IIC2Connector)iic, tmp)); sum+=tmp; } } } if (sum<.0001) { return; } final double oldInBuf = outputMax; HashMap transferedPerConn = ImmersiveNetHandler.INSTANCE.getTransferedRates(world.provider.getDimension()); for (AbstractConnection c:maxOutputs.keySet()) { Pair p = maxOutputs.get(c); double out = oldInBuf*p.getRight()/sum; double loss = getAverageLossRate(c); double inserted = out-p.getLeft().insertEnergy(out-loss, false); inBuffer-=inserted; float intermediaryLoss = 0; HashSet passedConnectors = new HashSet<>(); double energyAtConn = inserted+loss; for(Connection sub : c.subConnections) { int transferredPerCon = transferedPerConn.getOrDefault(sub, 0); energyAtConn-=sub.cableType.getLossRatio()*sub.length; ImmersiveNetHandler.INSTANCE.getTransferedRates(world.provider.getDimension()).put(sub, (int) (transferredPerCon + energyAtConn)); IImmersiveConnectable subStart = ApiUtils.toIIC(sub.start, world); IImmersiveConnectable subEnd = ApiUtils.toIIC(sub.end, world); if(subStart!=null && passedConnectors.add(subStart)) subStart.onEnergyPassthrough((int)(inserted-inserted*intermediaryLoss)); if(subEnd!=null && passedConnectors.add(subEnd)) subEnd.onEnergyPassthrough((int)(inserted-inserted*intermediaryLoss)); } } } public double getAverageLossRate(AbstractConnection conn) { double f = 0; for(Connection c : conn.subConnections) { f += c.length*c.cableType.getLossRatio(); } return f; } //Input through the net @Override public double insertEnergy(double eu, boolean simulate) { final double insert = Math.min(maxStored-outBuffer, eu); if (insert>0) { if (outBuffermaxToMachine) { maxToMachine = eu; } } if (!simulate) { outBuffer+=insert; } return eu-insert; } @Override public void invalidate() { if (!world.isRemote && !first) MinecraftForge.EVENT_BUS.post(new EnergyTileUnloadEvent(this)); first = true; super.invalidate(); } @Override public void onChunkUnload() { super.onChunkUnload(); if (!world.isRemote && !first) MinecraftForge.EVENT_BUS.post(new EnergyTileUnloadEvent(this)); first = true; } @Override public Vec3d getRaytraceOffset(IImmersiveConnectable link) { EnumFacing side = f.getOpposite(); return new Vec3d(.5+side.getFrontOffsetX()*.0625, .5+side.getFrontOffsetY()*.0625, .5+side.getFrontOffsetZ()*.0625); } @Override public Vec3d getConnectionOffset(Connection con) { EnumFacing side = f.getOpposite(); double conRadius = con.cableType.getRenderDiameter()/2; return new Vec3d(.5-conRadius*side.getFrontOffsetX(), .5-conRadius*side.getFrontOffsetY(), .5-conRadius*side.getFrontOffsetZ()); } @Override public boolean canConnect() { return true; } @Override public boolean isEnergyOutput() { return !relay; } @Override public boolean canConnectCable(WireType cableType, TargetingInfo target) { return (limitType==null||(this.isRelay() && limitType==cableType))&&canConnect(cableType); } public boolean canConnect(WireType t) { return t==IC2Wiretype.IC2_TYPES[0]; } @Override protected boolean isRelay() { return relay; } @Override public boolean emitsEnergyTo(IEnergyAcceptor receiver, EnumFacing side) { return !relay&&side==f; } @Override public boolean acceptsEnergyFrom(IEnergyEmitter emitter, EnumFacing side) { return !relay&&side==f; } @Override public double getDemandedEnergy() { double ret = maxStored-inBuffer; if (ret<.1) ret = 0; return ret; } @Override public int getSinkTier() { return tier; } @Override public double injectEnergy(EnumFacing directionFrom, double amount, double voltage) { if (inBuffermaxToNet) { maxToNet = amount; } markDirty(); return 0; } return amount; } @Override public double getOfferedEnergy() { return Math.min(maxToMachine, outBuffer); } @Override public void drawEnergy(double amount) { outBuffer -= amount; markDirty(); } @Override public int getSourceTier() { return tier; } @Override public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) { super.readCustomNBT(nbt, descPacket); f = EnumFacing.getFront(nbt.getInteger("facing")); relay = nbt.getBoolean("relay"); inBuffer = nbt.getDouble("inBuffer"); outBuffer = nbt.getDouble("outBuffer"); if (nbt.hasKey("maxToNet")) { maxToNet = nbt.getDouble("maxToNet"); } else { maxToNet = inBuffer; } if (nbt.hasKey("maxToMachine")) { maxToMachine = nbt.getDouble("maxToMachine"); } else { maxToMachine = outBuffer; } } @Override public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) { super.writeCustomNBT(nbt, descPacket); nbt.setInteger("facing", f.getIndex()); nbt.setBoolean("relay", relay); nbt.setDouble("inBuffer", inBuffer); nbt.setDouble("outBuffer", outBuffer); nbt.setDouble("maxToNet", maxToNet); nbt.setDouble("maxToMachine", maxToMachine); } @Nonnull @Override public EnumFacing getFacing() { return f; } @Override public void setFacing(@Nonnull EnumFacing facing) { f = facing; } @Override public int getFacingLimitation() { return 0; } @Override public boolean mirrorFacingOnPlacement(@Nonnull EntityLivingBase placer) { return true; } @Override public boolean canHammerRotate(@Nonnull EnumFacing side, float hitX, float hitY, float hitZ, @Nonnull EntityLivingBase entity) { return false; } @Override public AxisAlignedBB getBoundingBox() { float length = this instanceof TileEntityIC2ConnectorHV?(relay?.875f:.75f): this instanceof TileEntityIC2ConnectorGold?.5625f: .5f; float wMin = .3125f; float wMax = .6875f; switch(f.getOpposite() ) { case UP: return new AxisAlignedBB(wMin,0,wMin, wMax,length,wMax); case DOWN: return new AxisAlignedBB(wMin,1-length,wMin, wMax,1,wMax); case SOUTH: return new AxisAlignedBB(wMin,wMin,0, wMax,wMax,length); case NORTH: return new AxisAlignedBB(wMin,wMin,1-length, wMax,wMax,1); case EAST: return new AxisAlignedBB(0,wMin,wMin, length,wMax,wMax); case WEST: return new AxisAlignedBB(1-length,wMin,wMin, 1,wMax,wMax); } return new AxisAlignedBB(0,0,0,1,1,1); } /* * regarding equals+hashCode * TE's are considered equal if they have the same pos+dimension id * This is necessary to work around a weird bug causing a lot of log spam (100GB and above are well possible). * For further information see #1 (https://github.com/malte0811/IndustrialWires/issues/1) */ @Override public int hashCode() { int ret = world.provider.getDimension(); ret = 31*ret+pos.hashCode(); return ret; } @Override public boolean equals(Object obj) { if (obj==this) { return true; } if (!(obj instanceof TileEntityIC2ConnectorTin)) { return false; } if (obj.getClass()!=getClass()) { return false; } TileEntityIC2ConnectorTin te = (TileEntityIC2ConnectorTin) obj; if (!te.pos.equals(pos)) { return false; } if (te.world.provider.getDimension() != world.provider.getDimension()) { return false; } return true; } @Override public boolean canRotate(@Nonnull EnumFacing axis) { return false; } }