Package logisticspipes.routing.pathfinder

Source Code of logisticspipes.routing.pathfinder.PathFinder

/**
* Copyright (c) Krapht, 2011
*
* "LogisticsPipes" is distributed under the terms of the Minecraft Mod Public
* License 1.0, or MMPL. Please check the contents of the license located in
* http://www.mod-buildcraft.com/MMPL-1.0.txt
*/

package logisticspipes.routing.pathfinder;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;

import logisticspipes.api.ILogisticsPowerProvider;
import logisticspipes.interfaces.ISubSystemPowerProvider;
import logisticspipes.interfaces.routing.IDirectRoutingConnection;
import logisticspipes.interfaces.routing.IFilter;
import logisticspipes.pipes.basic.CoreRoutedPipe;
import logisticspipes.pipes.basic.LogisticsTileGenericPipe;
import logisticspipes.proxy.SimpleServiceLocator;
import logisticspipes.routing.ExitRoute;
import logisticspipes.routing.IPaintPath;
import logisticspipes.routing.LaserData;
import logisticspipes.routing.PipeRoutingConnectionType;
import logisticspipes.utils.OneList;
import logisticspipes.utils.OrientationsUtil;
import logisticspipes.utils.tuples.LPPosition;
import logisticspipes.utils.tuples.Pair;
import net.minecraft.inventory.IInventory;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;


/**
* Examines all pipe connections and their forks to locate all connected routers
*/
public class PathFinder {
  /**
   * Recurse through all exists of a pipe to find instances of PipeItemsRouting. maxVisited and maxLength are safeguards for
   * recursion runaways.
   *
   * @param startPipe - The TileEntity to start the search from
   * @param maxVisited - The maximum number of pipes to visit, regardless of recursion level
   * @param maxLength - The maximum recurse depth, i.e. the maximum length pipe that is supported
   * @return
   */
 
  public static HashMap<CoreRoutedPipe, ExitRoute> paintAndgetConnectedRoutingPipes(TileEntity startPipe, ForgeDirection startOrientation, int maxVisited, int maxLength, IPaintPath pathPainter, EnumSet<PipeRoutingConnectionType> connectionType) {
    IPipeInformationProvider startProvider = SimpleServiceLocator.pipeInformaitonManager.getInformationProviderFor(startPipe);
    if(startProvider == null) {
      return new HashMap<CoreRoutedPipe, ExitRoute>();
    }
    PathFinder newSearch = new PathFinder(maxVisited, maxLength, pathPainter);
    LPPosition p = new LPPosition(startProvider);
    newSearch.setVisited.add(p);
    p.moveForward(startOrientation);
    TileEntity entity = p.getTileEntity(startProvider.getWorld());
    IPipeInformationProvider provider = SimpleServiceLocator.pipeInformaitonManager.getInformationProviderFor(entity);
    if (provider == null) {
      return new HashMap<CoreRoutedPipe, ExitRoute>();
    }
    return newSearch.getConnectedRoutingPipes(provider, connectionType, startOrientation);
  }
 
  public PathFinder(IPipeInformationProvider startPipe, int maxVisited, int maxLength) {
    this(maxVisited, maxLength, null);
    if(startPipe == null) {
      result = new HashMap<CoreRoutedPipe, ExitRoute>();
      return;
    }
    result = this.getConnectedRoutingPipes(startPipe, EnumSet.allOf(PipeRoutingConnectionType.class), ForgeDirection.UNKNOWN);
  }
 
  public PathFinder(LogisticsTileGenericPipe startPipe, int maxVisited, int maxLength) {
    this(SimpleServiceLocator.pipeInformaitonManager.getInformationProviderFor(startPipe), maxVisited, maxLength);
  }
 
  public PathFinder(IPipeInformationProvider startPipe, int maxVisited, int maxLength, ForgeDirection side) {
    this(maxVisited, maxLength, null);
    result=this.getConnectedRoutingPipes(startPipe, EnumSet.allOf(PipeRoutingConnectionType.class), side);
  }
 
 
  private PathFinder(int maxVisited, int maxLength, IPaintPath pathPainter) {
    this.maxVisited = maxVisited;
    this.maxLength = maxLength;
    this.setVisited = new HashSet<LPPosition>();
    this.distances = new HashMap<LPPosition, Integer>();
    this.pathPainter = pathPainter;
  }

  private final int maxVisited;
  private final int maxLength;
  private final HashSet<LPPosition> setVisited;
  private final HashMap<LPPosition, Integer> distances;
  private final IPaintPath pathPainter;
  private int pipesVisited;

  public List<Pair<ILogisticsPowerProvider,List<IFilter>>> powerNodes;
  public List<Pair<ISubSystemPowerProvider,List<IFilter>>> subPowerProvider;
  public HashMap<CoreRoutedPipe, ExitRoute> result;
 
  private HashMap<CoreRoutedPipe, ExitRoute> getConnectedRoutingPipes(IPipeInformationProvider startPipe, EnumSet<PipeRoutingConnectionType> connectionFlags, ForgeDirection side) {
    HashMap<CoreRoutedPipe, ExitRoute> foundPipes = new HashMap<CoreRoutedPipe, ExitRoute>();
   
    boolean root = setVisited.size() == 0;
   
    //Reset visited count at top level
    if (setVisited.size() == 1) {
      pipesVisited = 0;
    }
   
    //Break recursion if we have visited a set number of pipes, to prevent client hang if pipes are weirdly configured
    if (++pipesVisited > maxVisited) {
      return foundPipes;
    }
   
    //Break recursion after certain amount of nodes visited
    if (setVisited.size() > maxLength) {
      return foundPipes;
    }
   
    if (!startPipe.isInitialised()) {
      return foundPipes;
    }
   
    //Break recursion if we end up on a routing pipe, unless its the first one. Will break if matches the first call
    if (startPipe.isRoutingPipe() && setVisited.size() != 0) {
      CoreRoutedPipe rp = startPipe.getRoutingPipe();
      if(rp.stillNeedReplace()) {
        return foundPipes;
      }
      int size = 0;
      for(Integer dis:distances.values()) {
        size += dis;
      }
     
      if(!rp.getUpgradeManager().hasPowerPassUpgrade()) {
        connectionFlags.remove(PipeRoutingConnectionType.canPowerSubSystemFrom);
      }
     
      foundPipes.put(rp, new ExitRoute(null,rp.getRouter(), ForgeDirection.UNKNOWN, side.getOpposite(), Math.max(1, size), connectionFlags, distances.size()));
     
      return foundPipes;
    }
   
    //Visited is checked after, so we can reach the same target twice to allow to keep the shortest path
    setVisited.add(new LPPosition(startPipe));
    distances.put(new LPPosition(startPipe), startPipe.getDistance());
   
    // first check specialPipeConnections (tesseracts, teleports, other connectors)
    List<IPipeInformationProvider> pipez = SimpleServiceLocator.specialpipeconnection.getConnectedPipes(startPipe);
    for (IPipeInformationProvider specialpipe : pipez){
      if (setVisited.contains(new LPPosition(specialpipe))) {
        //Don't go where we have been before
        continue;
      }
      HashMap<CoreRoutedPipe, ExitRoute> result = getConnectedRoutingPipes(specialpipe, connectionFlags, side);
      for (Entry<CoreRoutedPipe, ExitRoute> pipe : result.entrySet()) {
        pipe.getValue().exitOrientation = ForgeDirection.UNKNOWN;
        ExitRoute foundPipe=foundPipes.get(pipe.getKey());
        if (foundPipe == null || (pipe.getValue().distanceToDestination < foundPipe.distanceToDestination)) {
          // New path OR   If new path is better, replace old path
          foundPipes.put(pipe.getKey(), pipe.getValue());
        }
      }
    }
   
    ArrayDeque<Pair<TileEntity,ForgeDirection>> connections = new ArrayDeque<Pair<TileEntity,ForgeDirection>>();
   
    //Recurse in all directions
    for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) {
      if(root && !ForgeDirection.UNKNOWN.equals(side) && !direction.equals(side)) continue;

      // tile may be up to 1 second old, but any neighbour pipe change will cause an immidiate update here, so we know that if it has changed, it isn't a pipe that has done so.
      TileEntity tile = startPipe.getTile(direction);
     
      if (tile == null) continue;
      if(OrientationsUtil.isSide(direction)) {
        if (root && tile instanceof ILogisticsPowerProvider) {
          if(this.powerNodes==null) {
            powerNodes = new ArrayList<Pair<ILogisticsPowerProvider,List<IFilter>>>();
          }
          //If we are a FireWall pipe add our filter to the pipes
          if(startPipe.isFirewallPipe()) {
            powerNodes.add(new Pair<ILogisticsPowerProvider,List<IFilter>>((ILogisticsPowerProvider) tile, new OneList<IFilter>(startPipe.getFirewallFilter())));
          } else {
            powerNodes.add(new Pair<ILogisticsPowerProvider,List<IFilter>>((ILogisticsPowerProvider) tile, Collections.unmodifiableList(new ArrayList<IFilter>(0))));
          }
        }
        if(root && tile instanceof ISubSystemPowerProvider) {
          if(this.subPowerProvider==null) {
            subPowerProvider = new ArrayList<Pair<ISubSystemPowerProvider,List<IFilter>>>();
          }
          //If we are a FireWall pipe add our filter to the pipes
          if(startPipe.isFirewallPipe()) {
            subPowerProvider.add(new Pair<ISubSystemPowerProvider,List<IFilter>>((ISubSystemPowerProvider) tile, new OneList<IFilter>(startPipe.getFirewallFilter())));
          } else {
            subPowerProvider.add(new Pair<ISubSystemPowerProvider,List<IFilter>>((ISubSystemPowerProvider) tile, Collections.unmodifiableList(new ArrayList<IFilter>(0))));
          }
        }
      }
      connections.add(new Pair<TileEntity, ForgeDirection>(tile, direction));
    }
   
    while(!connections.isEmpty()) {
      Pair<TileEntity,ForgeDirection> pair = connections.pollFirst();
      TileEntity tile = pair.getValue1();
      ForgeDirection direction = pair.getValue2();
      EnumSet<PipeRoutingConnectionType> nextConnectionFlags = EnumSet.copyOf(connectionFlags);
      boolean isDirectConnection = false;
      int resistance = 0;
     
      if(root) {
        List<TileEntity> list = SimpleServiceLocator.specialtileconnection.getConnectedPipes(tile);
        if(!list.isEmpty()) {
          for(TileEntity pipe:list) {
            connections.add(new Pair<TileEntity, ForgeDirection>(pipe, direction));
          }
          continue;
        }
        if(!startPipe.getRoutingPipe().getUpgradeManager().hasPowerPassUpgrade()) {
          nextConnectionFlags.remove(PipeRoutingConnectionType.canPowerSubSystemFrom);
        }
      }
     
      if(tile instanceof IInventory && startPipe.isRoutingPipe() && startPipe.getRoutingPipe() instanceof IDirectRoutingConnection) {
        if(SimpleServiceLocator.connectionManager.hasDirectConnection(startPipe.getRoutingPipe().getRouter())) {
          CoreRoutedPipe CRP = SimpleServiceLocator.connectionManager.getConnectedPipe(startPipe.getRoutingPipe().getRouter());
          if(CRP != null) {
            tile = CRP.container;
            isDirectConnection = true;
            resistance = ((IDirectRoutingConnection)startPipe.getRoutingPipe()).getConnectionResistance();
          }
        }
      }
     
      if (tile == null) continue;
     
      IPipeInformationProvider currentPipe = SimpleServiceLocator.pipeInformaitonManager.getInformationProviderFor(tile);
     
      if (currentPipe != null && currentPipe.isInitialised() && (isDirectConnection || SimpleServiceLocator.pipeInformaitonManager.canConnect(startPipe, currentPipe, direction, true))) {
        //TileGenericPipe currentPipe = (TileGenericPipe) tile;
        if (setVisited.contains(new LPPosition(tile))) {
          //Don't go where we have been before
          continue;
        }
        if(side != pair.getValue2() && !root) { //Only straight connections for subsystem power
          nextConnectionFlags.remove(PipeRoutingConnectionType.canPowerSubSystemFrom);
        }
        if(isDirectConnection) {  //ISC doesn't pass power
          nextConnectionFlags.remove(PipeRoutingConnectionType.canPowerFrom);
          nextConnectionFlags.remove(PipeRoutingConnectionType.canPowerSubSystemFrom);
        }
        //Iron, obsidean and liquid pipes will separate networks
        if(currentPipe.divideNetwork()) {
          continue;
        }
        if(currentPipe.powerOnly()) {
          nextConnectionFlags.remove(PipeRoutingConnectionType.canRouteTo);
          nextConnectionFlags.remove(PipeRoutingConnectionType.canRequestFrom);
        }
        if(startPipe.isOnewayPipe()) {
          if(!startPipe.isOutputOpen(direction)) {
            nextConnectionFlags.remove(PipeRoutingConnectionType.canRouteTo);
          }
        }
        if(currentPipe.isOnewayPipe()) {
          nextConnectionFlags.remove(PipeRoutingConnectionType.canPowerSubSystemFrom);
          if(!currentPipe.isOutputOpen(direction.getOpposite())) {
            nextConnectionFlags.remove(PipeRoutingConnectionType.canRequestFrom);
            nextConnectionFlags.remove(PipeRoutingConnectionType.canPowerFrom);
          }
        }

        if(nextConnectionFlags.isEmpty()) {  //don't bother going somewhere we can't do anything with
          continue;
        }

        int beforeRecurseCount = foundPipes.size();
        HashMap<CoreRoutedPipe, ExitRoute> result = getConnectedRoutingPipes(currentPipe, nextConnectionFlags, direction);
        for(Entry<CoreRoutedPipe, ExitRoute> pipeEntry : result.entrySet()) {
          //Update Result with the direction we took
          pipeEntry.getValue().exitOrientation = direction;
          ExitRoute foundPipe = foundPipes.get(pipeEntry.getKey());
          if (foundPipe==null) {
            // New path
            foundPipes.put(pipeEntry.getKey(), pipeEntry.getValue());
            //Add resistance
            pipeEntry.getValue().distanceToDestination += resistance;
          }
          else if (pipeEntry.getValue().distanceToDestination + resistance < foundPipe.distanceToDestination) {
            //If new path is better, replace old path, otherwise do nothing
            foundPipes.put(pipeEntry.getKey(), pipeEntry.getValue());
            //Add resistance
            pipeEntry.getValue().distanceToDestination += resistance;
          }
        }
        if (foundPipes.size() > beforeRecurseCount && pathPainter != null) {
          pathPainter.addLaser(startPipe.getWorld(), new LaserData(startPipe.getX(), startPipe.getY(), startPipe.getZ(), direction, connectionFlags));
        }
      }
    }
    setVisited.remove(new LPPosition(startPipe));
    distances.remove(new LPPosition(startPipe));
    if(startPipe.isRoutingPipe()) { // ie, has the recursion returned to the pipe it started from?
      for(ExitRoute e:foundPipes.values()) {
        e.root = (startPipe.getRoutingPipe()).getRouter();
      }
    }
    //If we are a FireWall pipe add our filter to the pipes
    if(startPipe.isFirewallPipe() && root) {
      for(ExitRoute e:foundPipes.values()) {
        e.filters = new OneList<IFilter>(startPipe.getFirewallFilter());
      }
    }
    return foundPipes;
  }

  public static int messureDistanceToNextRoutedPipe(LPPosition lpPosition, ForgeDirection exitOrientation, World world) {
    int dis = 1;
    TileEntity tile = lpPosition.getTileEntity(world);
    if(tile instanceof LogisticsTileGenericPipe) {
      tile = ((LogisticsTileGenericPipe)tile).getTile(exitOrientation);
    }
    if(tile == null) return 0;
    IPipeInformationProvider info = SimpleServiceLocator.pipeInformaitonManager.getInformationProviderFor(tile);
    while(info != null && !info.isRoutingPipe()) {
      tile = info.getTile(exitOrientation);
      if(tile == null) {
        info = null;
        continue;
      }
      info = SimpleServiceLocator.pipeInformaitonManager.getInformationProviderFor(tile);
      dis++;
    }
    return dis;
  }
}
TOP

Related Classes of logisticspipes.routing.pathfinder.PathFinder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.