| package mekanism.api.transmitters; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.NoSuchElementException; |
| import java.util.Set; |
| |
| import mekanism.api.Coord4D; |
| import mekanism.api.IClientTicker; |
| import mekanism.api.Range4D; |
| |
| import net.minecraft.entity.player.EntityPlayer; |
| import net.minecraft.tileentity.TileEntity; |
| import net.minecraft.world.World; |
| import net.minecraftforge.common.MinecraftForge; |
| import net.minecraftforge.common.util.ForgeDirection; |
| import cpw.mods.fml.common.FMLCommonHandler; |
| import cpw.mods.fml.common.eventhandler.Event; |
| |
| public abstract class DynamicNetwork<A, N extends DynamicNetwork<A, N>> implements ITransmitterNetwork<A, N>, IClientTicker, INetworkDataHandler |
| { |
| public LinkedHashSet<IGridTransmitter<N>> transmitters = new LinkedHashSet<IGridTransmitter<N>>(); |
| |
| public HashMap<Coord4D, A> possibleAcceptors = new HashMap<Coord4D, A>(); |
| public HashMap<A, ForgeDirection> acceptorDirections = new HashMap<A, ForgeDirection>(); |
| |
| private List<DelayQueue> updateQueue = new ArrayList<DelayQueue>(); |
| |
| protected Range4D packetRange = null; |
| |
| protected int ticksSinceCreate = 0; |
| |
| protected int capacity = 0; |
| protected double meanCapacity = 0; |
| |
| protected boolean fixed = false; |
| |
| protected boolean needsUpdate = false; |
| |
| protected abstract ITransmitterNetwork<A, N> create(IGridTransmitter<N>... varTransmitters); |
| |
| protected abstract ITransmitterNetwork<A, N> create(Collection<IGridTransmitter<N>> collection); |
| |
| protected abstract ITransmitterNetwork<A, N> create(Set<N> networks); |
| |
| protected void clearAround(IGridTransmitter<N> transmitter) |
| { |
| for(ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) |
| { |
| Coord4D coord = Coord4D.get(transmitter.getTile()).getFromSide(side); |
| |
| if(possibleAcceptors.containsKey(coord)) |
| { |
| clearIfNecessary(coord, transmitter, side.getOpposite()); |
| } |
| } |
| } |
| |
| protected void clearIfNecessary(Coord4D acceptor, IGridTransmitter<N> transmitter, ForgeDirection side) |
| { |
| if(getWorld() == null) |
| { |
| return; |
| } |
| |
| if(acceptor.getTileEntity(getWorld()) == null || acceptor.getTileEntity(getWorld()).isInvalid() || transmitter.canConnectToAcceptor(side, true)) |
| { |
| possibleAcceptors.remove(acceptor); |
| acceptorDirections.remove(acceptor.getTileEntity(getWorld())); |
| } |
| } |
| |
| public void addAllTransmitters(Set<IGridTransmitter<N>> newTransmitters) |
| { |
| transmitters.addAll(newTransmitters); |
| updateCapacity(); |
| } |
| |
| public boolean isFirst(IGridTransmitter<N> transmitter) |
| { |
| return transmitters.iterator().next().equals(transmitter); |
| } |
| |
| @Override |
| public void fullRefresh() |
| { |
| possibleAcceptors.clear(); |
| acceptorDirections.clear(); |
| |
| for(IGridTransmitter<N> transmitter : transmitters) |
| { |
| refresh(transmitter); |
| } |
| |
| refresh(); |
| } |
| |
| public Range4D getPacketRange() |
| { |
| if(packetRange == null) |
| { |
| return genPacketRange(); |
| } |
| |
| return packetRange; |
| } |
| |
| public World getWorld() |
| { |
| if(getSize() == 0) |
| { |
| return null; |
| } |
| |
| return transmitters.iterator().next().getTile().getWorldObj(); |
| } |
| |
| protected Range4D genPacketRange() |
| { |
| if(getSize() == 0) |
| { |
| deregister(); |
| return null; |
| } |
| |
| Coord4D initCoord = Coord4D.get(transmitters.iterator().next().getTile()); |
| |
| int minX = initCoord.xCoord; |
| int minY = initCoord.yCoord; |
| int minZ = initCoord.zCoord; |
| int maxX = initCoord.xCoord; |
| int maxY = initCoord.yCoord; |
| int maxZ = initCoord.zCoord; |
| |
| for(IGridTransmitter transmitter : transmitters) |
| { |
| Coord4D coord = Coord4D.get(transmitter.getTile()); |
| |
| if(coord.xCoord < minX) minX = coord.xCoord; |
| if(coord.yCoord < minY) minY = coord.yCoord; |
| if(coord.zCoord < minZ) minZ = coord.zCoord; |
| if(coord.xCoord > maxX) maxX = coord.xCoord; |
| if(coord.yCoord > maxY) maxY = coord.yCoord; |
| if(coord.zCoord > maxZ) maxZ = coord.zCoord; |
| } |
| |
| return new Range4D(minX, minY, minZ, maxX, maxY, maxZ, getWorld().provider.dimensionId); |
| } |
| |
| @Override |
| public void removeTransmitter(IGridTransmitter<N> transmitter) |
| { |
| transmitters.remove(transmitter); |
| updateCapacity(); |
| |
| if(transmitters.size() == 0) |
| { |
| deregister(); |
| } |
| } |
| |
| @Override |
| public void register() |
| { |
| try { |
| IGridTransmitter<N> aTransmitter = transmitters.iterator().next(); |
| |
| if(aTransmitter instanceof TileEntity) |
| { |
| if(!((TileEntity)aTransmitter).getWorldObj().isRemote) |
| { |
| TransmitterNetworkRegistry.getInstance().registerNetwork(this); |
| } |
| else { |
| MinecraftForge.EVENT_BUS.post(new ClientTickUpdate(this, (byte)1)); |
| } |
| } |
| } catch(NoSuchElementException e) {} |
| } |
| |
| @Override |
| public void deregister() |
| { |
| transmitters.clear(); |
| |
| if(FMLCommonHandler.instance().getEffectiveSide().isServer()) |
| { |
| TransmitterNetworkRegistry.getInstance().removeNetwork(this); |
| } |
| else { |
| MinecraftForge.EVENT_BUS.post(new ClientTickUpdate(this, (byte)0)); |
| } |
| } |
| |
| @Override |
| public int getSize() |
| { |
| return transmitters.size(); |
| } |
| |
| @Override |
| public int getAcceptorSize() |
| { |
| return possibleAcceptors.size(); |
| } |
| |
| public synchronized void updateCapacity() |
| { |
| updateMeanCapacity(); |
| capacity = (int)meanCapacity * transmitters.size(); |
| } |
| |
| /** |
| * Override this if things can have variable capacity along the network. |
| * @return An 'average' value of capacity. Calculate it how you will. |
| */ |
| protected synchronized void updateMeanCapacity() |
| { |
| if(transmitters.size() > 0) |
| { |
| meanCapacity = transmitters.iterator().next().getCapacity(); |
| } |
| else { |
| meanCapacity = 0; |
| } |
| } |
| |
| public int getCapacity() |
| { |
| return capacity; |
| } |
| |
| public double getMeanCapacity() |
| { |
| return meanCapacity; |
| } |
| |
| @Override |
| public void tick() |
| { |
| boolean didFix = false; |
| |
| if(!fixed) |
| { |
| ticksSinceCreate++; |
| |
| if(transmitters.size() == 0) |
| { |
| deregister(); |
| return; |
| } |
| |
| if(ticksSinceCreate > 1200) |
| { |
| ticksSinceCreate = 0; |
| fixMessedUpNetwork(transmitters.iterator().next()); |
| didFix = true; |
| } |
| } |
| |
| if(!didFix) |
| { |
| onUpdate(); |
| } |
| } |
| |
| public void onUpdate() |
| { |
| if(FMLCommonHandler.instance().getEffectiveSide().isServer()) |
| { |
| Iterator<DelayQueue> i = updateQueue.iterator(); |
| |
| try { |
| while(i.hasNext()) |
| { |
| DelayQueue q = i.next(); |
| |
| if(q.delay > 0) |
| { |
| q.delay--; |
| } |
| else { |
| needsUpdate = true; |
| i.remove(); |
| } |
| } |
| } catch(Exception e) {} |
| } |
| } |
| |
| @Override |
| public synchronized void fixMessedUpNetwork(IGridTransmitter<N> transmitter) |
| { |
| if(transmitter instanceof TileEntity) |
| { |
| NetworkFinder finder = new NetworkFinder(((TileEntity)transmitter).getWorldObj(), getTransmissionType(), Coord4D.get((TileEntity)transmitter)); |
| List<Coord4D> partNetwork = finder.exploreNetwork(); |
| Set<IGridTransmitter<N>> newTransporters = new HashSet<IGridTransmitter<N>>(); |
| |
| for(Coord4D node : partNetwork) |
| { |
| TileEntity nodeTile = node.getTileEntity(((TileEntity)transmitter).getWorldObj()); |
| |
| if(TransmissionType.checkTransmissionType(nodeTile, getTransmissionType(), (TileEntity)transmitter)) |
| { |
| ((IGridTransmitter<N>)nodeTile).removeFromTransmitterNetwork(); |
| newTransporters.add((IGridTransmitter<N>)nodeTile); |
| } |
| } |
| |
| ITransmitterNetwork<A, N> newNetwork = create(newTransporters); |
| newNetwork.fullRefresh(); |
| newNetwork.setFixed(true); |
| deregister(); |
| } |
| } |
| |
| @Override |
| public synchronized void split(IGridTransmitter<N> splitPoint) |
| { |
| if(splitPoint instanceof TileEntity) |
| { |
| removeTransmitter(splitPoint); |
| |
| TileEntity[] connectedBlocks = new TileEntity[6]; |
| boolean[] dealtWith = {false, false, false, false, false, false}; |
| List<ITransmitterNetwork<A, N>> newNetworks = new ArrayList<ITransmitterNetwork<A, N>>(); |
| |
| for(ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) |
| { |
| TileEntity sideTile = Coord4D.get((TileEntity)splitPoint).getFromSide(side).getTileEntity(((TileEntity)splitPoint).getWorldObj()); |
| |
| if(sideTile != null) |
| { |
| connectedBlocks[side.ordinal()] = sideTile; |
| } |
| } |
| |
| for(int count = 0; count < connectedBlocks.length; count++) |
| { |
| TileEntity connectedBlockA = connectedBlocks[count]; |
| |
| if(TransmissionType.checkTransmissionType(connectedBlockA, getTransmissionType()) && !dealtWith[count]) |
| { |
| NetworkFinder finder = new NetworkFinder(((TileEntity)splitPoint).getWorldObj(), getTransmissionType(), Coord4D.get(connectedBlockA), Coord4D.get((TileEntity)splitPoint)); |
| List<Coord4D> partNetwork = finder.exploreNetwork(); |
| |
| for(int check = count; check < connectedBlocks.length; check++) |
| { |
| if(check == count) |
| { |
| continue; |
| } |
| |
| TileEntity connectedBlockB = connectedBlocks[check]; |
| |
| if(TransmissionType.checkTransmissionType(connectedBlockB, getTransmissionType()) && !dealtWith[check]) |
| { |
| if(partNetwork.contains(Coord4D.get(connectedBlockB))) |
| { |
| dealtWith[check] = true; |
| } |
| } |
| } |
| |
| Set<IGridTransmitter<N>> newNetCables = new HashSet<IGridTransmitter<N>>(); |
| |
| for(Coord4D node : finder.iterated) |
| { |
| TileEntity nodeTile = node.getTileEntity(((TileEntity)splitPoint).getWorldObj()); |
| |
| if(TransmissionType.checkTransmissionType(nodeTile, getTransmissionType())) |
| { |
| if(nodeTile != splitPoint) |
| { |
| newNetCables.add((IGridTransmitter<N>)nodeTile); |
| } |
| } |
| } |
| |
| newNetworks.add(create(newNetCables)); |
| } |
| } |
| |
| if(newNetworks.size() > 0) |
| { |
| onNetworksCreated((List)newNetworks); |
| |
| for(ITransmitterNetwork<A, N> network : newNetworks) |
| { |
| network.fullRefresh(); |
| } |
| } |
| |
| deregister(); |
| } |
| } |
| |
| @Override |
| public void onNetworksCreated(List<N> networks) {} |
| |
| @Override |
| public void setFixed(boolean value) |
| { |
| fixed = value; |
| } |
| |
| @Override |
| public boolean needsTicks() |
| { |
| return getSize() > 0; |
| } |
| |
| @Override |
| public void clientTick() |
| { |
| ticksSinceCreate++; |
| |
| if(ticksSinceCreate == 5 && getSize() > 0) |
| { |
| TileEntity tile = (TileEntity)transmitters.iterator().next(); |
| MinecraftForge.EVENT_BUS.post(new NetworkClientRequest(tile)); |
| } |
| } |
| |
| @Override |
| public boolean canMerge(List<ITransmitterNetwork<?, ?>> networks) |
| { |
| return true; |
| } |
| |
| public static class ClientTickUpdate extends Event |
| { |
| public DynamicNetwork network; |
| public byte operation; /*0 remove, 1 add*/ |
| |
| public ClientTickUpdate(DynamicNetwork net, byte b) |
| { |
| network = net; |
| operation = b; |
| } |
| } |
| |
| public static class NetworkClientRequest extends Event |
| { |
| public TileEntity tileEntity; |
| |
| public NetworkClientRequest(TileEntity tile) |
| { |
| tileEntity = tile; |
| } |
| } |
| |
| public void addUpdate(EntityPlayer player) |
| { |
| updateQueue.add(new DelayQueue(player)); |
| } |
| |
| public static class NetworkFinder |
| { |
| public TransmissionType transmissionType; |
| |
| public World worldObj; |
| public Coord4D start; |
| |
| public List<Coord4D> iterated = new ArrayList<Coord4D>(); |
| public List<Coord4D> toIgnore = new ArrayList<Coord4D>(); |
| |
| public NetworkFinder(World world, TransmissionType type, Coord4D location, Coord4D... ignore) |
| { |
| worldObj = world; |
| start = location; |
| |
| transmissionType = type; |
| |
| if(ignore != null) |
| { |
| for(int i = 0; i < ignore.length; i++) |
| { |
| toIgnore.add(ignore[i]); |
| } |
| } |
| } |
| |
| public void loopAll(Coord4D location) |
| { |
| if(TransmissionType.checkTransmissionType(location.getTileEntity(worldObj), transmissionType)) |
| { |
| iterated.add(location); |
| } |
| else { |
| toIgnore.add(location); |
| } |
| |
| for(ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) |
| { |
| Coord4D obj = location.getFromSide(direction); |
| |
| if(!iterated.contains(obj) && !toIgnore.contains(obj)) |
| { |
| TileEntity tileEntity = obj.getTileEntity(worldObj); |
| |
| if(!(tileEntity instanceof IBlockableConnection) || ((IBlockableConnection)tileEntity).canConnectMutual(direction.getOpposite())) |
| { |
| if(TransmissionType.checkTransmissionType(tileEntity, transmissionType, location.getTileEntity(worldObj))) |
| { |
| loopAll(obj); |
| } |
| } |
| } |
| } |
| } |
| |
| public List<Coord4D> exploreNetwork() |
| { |
| loopAll(start); |
| |
| return iterated; |
| } |
| } |
| |
| public static class DelayQueue |
| { |
| public EntityPlayer player; |
| public int delay; |
| |
| public DelayQueue(EntityPlayer p) |
| { |
| player = p; |
| delay = 5; |
| } |
| } |
| } |