package mekanism.api; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; import net.minecraftforge.common.ForgeDirection; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.Event; import net.minecraftforge.event.ForgeSubscribe; import net.minecraftforge.event.world.ChunkEvent; import cpw.mods.fml.common.FMLCommonHandler; public class GasNetwork implements ITransmitterNetwork { public HashSet tubes = new HashSet(); public Set possibleAcceptors = new HashSet(); public Map acceptorDirections = new HashMap(); private int ticksSinceCreate = 0; private boolean fixed = false; public GasNetwork(IPressurizedTube... varPipes) { tubes.addAll(Arrays.asList(varPipes)); register(); } public GasNetwork(Set networks) { for(GasNetwork net : networks) { if(net != null) { addAllTubes(net.tubes); net.deregister(); } } refresh(); register(); } public int emit(int gasToSend, EnumGas transferType, TileEntity emitter) { List availableAcceptors = Arrays.asList(getGasAcceptors(transferType).toArray()); Collections.shuffle(availableAcceptors); int prevSending = gasToSend; if(!availableAcceptors.isEmpty()) { int divider = availableAcceptors.size(); int remaining = gasToSend % divider; int sending = (gasToSend-remaining)/divider; for(Object obj : availableAcceptors) { if(obj instanceof IGasAcceptor && obj != emitter) { IGasAcceptor acceptor = (IGasAcceptor)obj; int currentSending = sending; if(remaining > 0) { currentSending++; remaining--; } gasToSend -= (currentSending - acceptor.transferGasToAcceptor(currentSending, transferType)); } } } if(prevSending > gasToSend && FMLCommonHandler.instance().getEffectiveSide().isServer()) { MinecraftForge.EVENT_BUS.post(new GasTransferEvent(this, transferType)); } return gasToSend; } public Set getGasAcceptors(EnumGas transferType) { Set toReturn = new HashSet(); for(IGasAcceptor acceptor : possibleAcceptors) { if(acceptor.canReceiveGas(acceptorDirections.get(acceptor).getOpposite(), transferType)) { if(!(acceptor instanceof IGasStorage) || (acceptor instanceof IGasStorage && (((IGasStorage)acceptor).getMaxGas(transferType) - ((IGasStorage)acceptor).getGas(transferType)) > 0)) { toReturn.add(acceptor); } } } return toReturn; } public void refresh() { Set iterTubes = (Set) tubes.clone(); Iterator it = iterTubes.iterator(); possibleAcceptors.clear(); acceptorDirections.clear(); while(it.hasNext()) { IPressurizedTube conductor = (IPressurizedTube)it.next(); if(conductor == null || ((TileEntity)conductor).isInvalid()) { it.remove(); tubes.remove(conductor); } else { conductor.setNetwork(this); } } for(IPressurizedTube pipe : tubes) { IGasAcceptor[] acceptors = GasTransmission.getConnectedAcceptors((TileEntity)pipe); for(IGasAcceptor acceptor : acceptors) { if(acceptor != null && !(acceptor instanceof IPressurizedTube)) { possibleAcceptors.add(acceptor); acceptorDirections.put(acceptor, ForgeDirection.getOrientation(Arrays.asList(acceptors).indexOf(acceptor))); } } } } public void merge(GasNetwork network) { if(network != null && network != this) { Set networks = new HashSet(); networks.add(this); networks.add(network); GasNetwork newNetwork = new GasNetwork(networks); newNetwork.refresh(); } } public void addAllTubes(Set newTubes) { tubes.addAll(newTubes); } public void split(IPressurizedTube splitPoint) { if(splitPoint instanceof TileEntity) { removeTube(splitPoint); TileEntity[] connectedBlocks = new TileEntity[6]; boolean[] dealtWith = {false, false, false, false, false, false}; for(ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) { TileEntity sideTile = Object3D.get((TileEntity)splitPoint).getFromSide(direction).getTileEntity(((TileEntity)splitPoint).worldObj); if(sideTile != null) { connectedBlocks[Arrays.asList(ForgeDirection.values()).indexOf(direction)] = sideTile; } } for(int countOne = 0; countOne < connectedBlocks.length; countOne++) { TileEntity connectedBlockA = connectedBlocks[countOne]; if(connectedBlockA instanceof IPressurizedTube && !dealtWith[countOne]) { NetworkFinder finder = new NetworkFinder(((TileEntity)splitPoint).worldObj, Object3D.get(connectedBlockA), Object3D.get((TileEntity)splitPoint)); List partNetwork = finder.exploreNetwork(); for(int countTwo = countOne + 1; countTwo < connectedBlocks.length; countTwo++) { TileEntity connectedBlockB = connectedBlocks[countTwo]; if(connectedBlockB instanceof IPressurizedTube && !dealtWith[countTwo]) { if(partNetwork.contains(Object3D.get(connectedBlockB))) { dealtWith[countTwo] = true; } } } GasNetwork newNetwork = new GasNetwork(); for(Object3D node : finder.iterated) { TileEntity nodeTile = node.getTileEntity(((TileEntity)splitPoint).worldObj); if(nodeTile instanceof IPressurizedTube) { if(nodeTile != splitPoint) { newNetwork.tubes.add((IPressurizedTube)nodeTile); } } } newNetwork.refresh(); } } deregister(); } } public void fixMessedUpNetwork(IPressurizedTube tube) { if(tube instanceof TileEntity) { NetworkFinder finder = new NetworkFinder(((TileEntity)tube).getWorldObj(), Object3D.get((TileEntity)tube), null); List partNetwork = finder.exploreNetwork(); Set newTubes = new HashSet(); for(Object3D node : partNetwork) { TileEntity nodeTile = node.getTileEntity(((TileEntity)tube).worldObj); if(nodeTile instanceof IPressurizedTube) { ((IPressurizedTube) nodeTile).removeFromNetwork(); newTubes.add((IPressurizedTube)nodeTile); } } GasNetwork newNetwork = new GasNetwork(newTubes.toArray(new IPressurizedTube[0])); newNetwork.refresh(); newNetwork.fixed = true; deregister(); } } public void removeTube(IPressurizedTube tube) { tubes.remove(tube); if(tubes.size() == 0) { deregister(); } } public void register() { try { IPressurizedTube aTube = tubes.iterator().next(); if(aTube instanceof TileEntity && !((TileEntity)aTube).worldObj.isRemote) { TransmitterNetworkRegistry.getInstance().registerNetwork(this); } } catch(NoSuchElementException e) {} } public void deregister() { tubes.clear(); TransmitterNetworkRegistry.getInstance().removeNetwork(this); } public static class NetworkFinder { public World worldObj; public Object3D start; public List iterated = new ArrayList(); public List toIgnore = new ArrayList(); public NetworkFinder(World world, Object3D location, Object3D... ignore) { worldObj = world; start = location; if(ignore != null) { toIgnore = Arrays.asList(ignore); } } public void loopAll(Object3D location) { if(location.getTileEntity(worldObj) instanceof IPressurizedTube) { iterated.add(location); } for(ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) { Object3D obj = location.getFromSide(direction); if(!iterated.contains(obj) && !toIgnore.contains(obj)) { TileEntity tileEntity = obj.getTileEntity(worldObj); if(tileEntity instanceof IPressurizedTube) { loopAll(obj); } } } } public List exploreNetwork() { loopAll(start); return iterated; } } public static class GasTransferEvent extends Event { public final GasNetwork gasNetwork; public final EnumGas transferType; public GasTransferEvent(GasNetwork network, EnumGas type) { gasNetwork = network; transferType = type; } } public static class NetworkLoader { @ForgeSubscribe public void onChunkLoad(ChunkEvent.Load event) { if(event.getChunk() != null) { for(Object obj : event.getChunk().chunkTileEntityMap.values()) { if(obj instanceof TileEntity) { TileEntity tileEntity = (TileEntity)obj; if(tileEntity instanceof IPressurizedTube) { ((IPressurizedTube)tileEntity).refreshNetwork(); } } } } } } @Override public String toString() { return "[GasNetwork] " + tubes.size() + " pipes, " + possibleAcceptors.size() + " acceptors."; } public void tick() { //Fix weird behaviour periodically. if(!fixed) { ++ticksSinceCreate; if(ticksSinceCreate > 1200) { ticksSinceCreate = 0; fixMessedUpNetwork(tubes.iterator().next()); } } } @Override public int getSize() { return tubes.size(); } }