package crazypants.enderio.machine.capbank.network; import java.util.Collection; import java.util.HashMap; import java.util.Map; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.MathHelper; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import com.enderio.core.common.util.BlockCoord; import crazypants.enderio.EnderIO; import crazypants.enderio.machine.RedstoneControlMode; import crazypants.enderio.machine.capbank.CapBankType; import crazypants.enderio.machine.capbank.InfoDisplayType; import crazypants.enderio.machine.capbank.TileCapBank; import crazypants.enderio.machine.capbank.packet.PacketNetworkEnergyRequest; import crazypants.enderio.machine.capbank.packet.PacketNetworkStateRequest; import crazypants.enderio.network.PacketHandler; import crazypants.enderio.power.IPowerStorage; public class CapBankClientNetwork implements ICapBankNetwork { private final int id; private final Map<BlockCoord, TileCapBank> members = new HashMap<BlockCoord, TileCapBank>(); private int maxEnergySent; private int maxEnergyRecieved; private int stateUpdateCount; private int maxIO; private long maxEnergyStored; private long energyStored; private RedstoneControlMode inputControlMode = RedstoneControlMode.IGNORE; private RedstoneControlMode outputControlMode = RedstoneControlMode.IGNORE; private final InventoryImpl inventory = new InventoryImpl(); private float avgInput; private float avgOutput; private long lastPowerRequestTick = -1; private Map<DisplayInfoKey, IOInfo> ioDisplayInfoCache; public CapBankClientNetwork(int id) { this.id = id; } @Override public int getId() { return id; } public void requestPowerUpdate(TileCapBank capBank, int interval) { long curTick = EnderIO.proxy.getTickCount(); if(lastPowerRequestTick == -1 || curTick - lastPowerRequestTick >= interval) { if(stateUpdateCount == 0) { PacketHandler.INSTANCE.sendToServer(new PacketNetworkStateRequest(capBank)); // the network state also contains the energy data } else { PacketHandler.INSTANCE.sendToServer(new PacketNetworkEnergyRequest(capBank)); } lastPowerRequestTick = curTick; } } public void setState(World world, NetworkState state) { maxEnergyRecieved = state.getMaxInput(); maxEnergySent = state.getMaxOutput(); maxIO = state.getMaxIO(); maxEnergyStored = state.getMaxEnergyStored(); energyStored = state.getEnergyStored(); inputControlMode = state.getInputMode(); outputControlMode = state.getOutputMode(); BlockCoord bc = state.getInventoryImplLocation(); if(bc == null) { inventory.setCapBank(null); } else if(world != null) { TileEntity te = world.getTileEntity(bc.x, bc.y, bc.z); if(te instanceof TileCapBank) { inventory.setCapBank((TileCapBank) te); } } avgInput = state.getAverageInput(); avgOutput = state.getAverageOutput(); stateUpdateCount++; } public int getStateUpdateCount() { return stateUpdateCount; } public void setStateUpdateCount(int stateUpdateCount) { this.stateUpdateCount = stateUpdateCount; } @Override public void addMember(TileCapBank capBank) { members.put(capBank.getLocation(), capBank); invalidateDisplayInfoCache(); } @Override public Collection<TileCapBank> getMembers() { return members.values(); } @Override public void destroyNetwork() { for (TileCapBank cb : members.values()) { cb.setNetworkId(-1); cb.setNetwork(null); } invalidateDisplayInfoCache(); } @Override public int getMaxIO() { return maxIO; } @Override public long getMaxEnergyStoredL() { return maxEnergyStored; } public void setMaxEnergyStoredL(long maxEnergyStored) { this.maxEnergyStored = maxEnergyStored; } public void setEnergyStored(long energyStored) { this.energyStored = energyStored; } @Override public long getEnergyStoredL() { return energyStored; } @Override public int getMaxOutput() { return maxEnergySent; } @Override public void setMaxOutput(int max) { maxEnergySent = MathHelper.clamp_int(max, 0, maxIO); } @Override public int getMaxInput() { return maxEnergyRecieved; } @Override public void setMaxInput(int max) { maxEnergyRecieved = MathHelper.clamp_int(max, 0, maxIO); } public double getEnergyStoredRatio() { if(getMaxEnergyStoredL() <= 0) { return 0; } return (double) getEnergyStoredL() / getMaxEnergyStoredL(); } @Override public RedstoneControlMode getInputControlMode() { return inputControlMode; } @Override public void setInputControlMode(RedstoneControlMode inputControlMode) { this.inputControlMode = inputControlMode; } @Override public RedstoneControlMode getOutputControlMode() { return outputControlMode; } @Override public void setOutputControlMode(RedstoneControlMode outputControlMode) { this.outputControlMode = outputControlMode; } @Override public InventoryImpl getInventory() { return inventory; } @Override public float getAverageChangePerTick() { return avgInput - avgOutput; } @Override public float getAverageInputPerTick() { return avgInput; } @Override public float getAverageOutputPerTick() { return avgOutput; } public void setAverageIOPerTick(float input, float output) { this.avgInput = input; this.avgOutput = output; } @Override public NetworkState getState() { return new NetworkState(this); } @Override public void onUpdateEntity(TileCapBank tileCapBank) { } @Override public void addEnergy(int energy) { } @Override public int receiveEnergy(int maxReceive, boolean simulate) { return 0; } @Override public void removeReceptors(Collection<EnergyReceptor> receptors) { } @Override public void addReceptors(Collection<EnergyReceptor> receptors) { } @Override public void updateRedstoneSignal(TileCapBank tileCapBank, boolean recievingSignal) { } @Override public boolean isOutputEnabled() { return true; } @Override public boolean isInputEnabled() { return true; } @Override public IPowerStorage getController() { return this; } @Override public boolean isOutputEnabled(ForgeDirection direction) { return isOutputEnabled(); } @Override public boolean isInputEnabled(ForgeDirection direction) { return isInputEnabled(); } @Override public boolean isCreative() { return false; } @Override public boolean isNetworkControlledIo(ForgeDirection direction) { return true; } @Override public void invalidateDisplayInfoCache() { ioDisplayInfoCache = null; } public IOInfo getIODisplayInfo(int x, int y, int z, ForgeDirection face) { DisplayInfoKey key = new DisplayInfoKey(x, y, z, face); if(ioDisplayInfoCache == null) { ioDisplayInfoCache = new HashMap<DisplayInfoKey, IOInfo>(); } IOInfo value = ioDisplayInfoCache.get(key); if(value == null) { value = computeIODisplayInfo(x, y, z, face); ioDisplayInfoCache.put(key, value); } return value; } private IOInfo computeIODisplayInfo(int xOrg, int yOrg, int zOrg, ForgeDirection dir) { if(dir.offsetY != 0) { return IOInfo.SINGLE; } TileCapBank cb = getCapBankAt(xOrg, yOrg, zOrg); if(cb == null) { return IOInfo.SINGLE; } CapBankType type = cb.getType(); ForgeDirection left = dir.getRotation(ForgeDirection.DOWN); ForgeDirection right = left.getOpposite(); int hOff = 0; int vOff = 0; // step 1: find top left while(isIOType(xOrg+left.offsetX, yOrg, zOrg+left.offsetZ, dir, type)) { xOrg += left.offsetX; zOrg += left.offsetZ; hOff++; } while(isIOType(xOrg, yOrg+1, zOrg, dir, type)) { yOrg++; vOff++; } if(isIOType(xOrg+left.offsetX, yOrg, zOrg+left.offsetZ, dir, type)) { // not a rectangle return IOInfo.SINGLE; } // step 2: find width int width = 1; int height = 1; int xTmp = xOrg; int yTmp = yOrg; int zTmp = zOrg; while(isIOType(xTmp+right.offsetX, yTmp, zTmp+right.offsetZ, dir, type)) { if(isIOType(xTmp+right.offsetX, yTmp+1, zTmp+right.offsetZ, dir, type)) { // not a rectangle return IOInfo.SINGLE; } xTmp += right.offsetX; zTmp += right.offsetZ; width++; } // step 3: find height while(isIOType(xOrg, yTmp-1, zOrg, dir, type)) { xTmp = xOrg; yTmp--; zTmp = zOrg; if(isIOType(xTmp+left.offsetX, yTmp, zTmp+left.offsetZ, dir, type)) { // not a rectangle return IOInfo.SINGLE; } for(int i=1 ; i<width ; i++) { xTmp += right.offsetX; zTmp += right.offsetZ; if(!isIOType(xTmp, yTmp, zTmp, dir, type)) { // not a rectangle return IOInfo.SINGLE; } } if(isIOType(xTmp+right.offsetX, yTmp, zTmp+right.offsetZ, dir, type)) { // not a rectangle return IOInfo.SINGLE; } height++; } xTmp = xOrg; yTmp--; zTmp = zOrg; for(int i=0 ; i<width ; i++) { if(isIOType(xTmp, yTmp, zTmp, dir, type)) { // not a rectangle return IOInfo.SINGLE; } xTmp += right.offsetX; zTmp += right.offsetZ; } if(width == 1 && height == 1) { return IOInfo.SINGLE; } if(hOff > 0 || vOff > 0) { return IOInfo.INSIDE; } return new IOInfo(width, height); } private boolean isIOType(int x, int y, int z, ForgeDirection face, CapBankType type) { TileCapBank cb = getCapBankAt(x, y, z); return cb != null && type == cb.getType() && cb.getDisplayType(face) == InfoDisplayType.IO; } private TileCapBank getCapBankAt(int x, int y, int z) { return members.get(new BlockCoord(x, y, z)); } public static final class DisplayInfoKey { final int x; final int y; final int z; final ForgeDirection face; public DisplayInfoKey(int x, int y, int z, ForgeDirection face) { this.x = x; this.y = y; this.z = z; this.face = face; } @Override public int hashCode() { int hash = 7; hash = 97 * hash + this.x; hash = 97 * hash + this.y; hash = 97 * hash + this.z; hash = 97 * hash + this.face.hashCode(); return hash; } @Override public boolean equals(Object obj) { if(!(obj instanceof DisplayInfoKey)) { return false; } final DisplayInfoKey other = (DisplayInfoKey) obj; return (this.x == other.x) && (this.y == other.y) && (this.z == other.z) && (this.face == other.face); } } public static class IOInfo { public final int width; public final int height; static final IOInfo SINGLE = new IOInfo(1, 1); static final IOInfo INSIDE = new IOInfo(0, 0); IOInfo(int width, int height) { this.width = width; this.height = height; } public boolean isInside() { return width == 0; } } }