Applied-Energistics-2-tiler.../src/main/java/appeng/me/cache/PathGridCache.java
2014-11-14 12:03:00 +01:00

397 lines
10 KiB
Java

/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.cache;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import net.minecraftforge.common.util.ForgeDirection;
import appeng.api.networking.GridFlags;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridBlock;
import appeng.api.networking.IGridConnection;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridMultiblock;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IGridStorage;
import appeng.api.networking.events.MENetworkBootingStatusChange;
import appeng.api.networking.events.MENetworkChannelChanged;
import appeng.api.networking.events.MENetworkControllerChange;
import appeng.api.networking.events.MENetworkEventSubscribe;
import appeng.api.networking.pathing.ControllerState;
import appeng.api.networking.pathing.IPathingGrid;
import appeng.api.util.DimensionalCoord;
import appeng.core.AEConfig;
import appeng.core.features.AEFeature;
import appeng.core.stats.Achievements;
import appeng.me.GridConnection;
import appeng.me.GridNode;
import appeng.me.pathfinding.AdHocChannelUpdater;
import appeng.me.pathfinding.ControllerChannelUpdater;
import appeng.me.pathfinding.ControllerValidator;
import appeng.me.pathfinding.IPathItem;
import appeng.me.pathfinding.PathSegment;
import appeng.tile.networking.TileController;
import appeng.util.Platform;
public class PathGridCache implements IPathingGrid
{
boolean recalculateControllerNextTick = true;
boolean updateNetwork = true;
boolean booting = false;
final LinkedList<PathSegment> active = new LinkedList<PathSegment>();
ControllerState controllerState = ControllerState.NO_CONTROLLER;
int instance = Integer.MIN_VALUE;
int ticksUntilReady = 20;
public int channelsInUse = 0;
int lastChannels = 0;
final Set<TileController> controllers = new HashSet<TileController>();
final Set<IGridNode> requireChannels = new HashSet<IGridNode>();
final Set<IGridNode> blockDense = new HashSet<IGridNode>();
final IGrid myGrid;
private HashSet<IPathItem> semiOpen = new HashSet<IPathItem>();
public int channelsByBlocks = 0;
public double channelPowerUsage = 0.0;
public PathGridCache(IGrid g)
{
myGrid = g;
}
@Override
public void onUpdateTick()
{
if ( recalculateControllerNextTick )
{
recalcController();
}
if ( updateNetwork )
{
if ( !booting )
myGrid.postEvent( new MENetworkBootingStatusChange() );
booting = true;
updateNetwork = false;
instance++;
channelsInUse = 0;
if ( !AEConfig.instance.isFeatureEnabled( AEFeature.Channels ) )
{
int used = calculateRequiredChannels();
int nodes = myGrid.getNodes().size();
ticksUntilReady = 20 + Math.max( 0, nodes / 100 - 20 );
channelsByBlocks = nodes * used;
channelPowerUsage = channelsByBlocks / 128.0;
myGrid.getPivot().beginVisit( new AdHocChannelUpdater( used ) );
}
else if ( controllerState == ControllerState.NO_CONTROLLER )
{
int requiredChannels = calculateRequiredChannels();
int used = requiredChannels;
if ( requiredChannels > 8 )
used = 0;
int nodes = myGrid.getNodes().size();
channelsInUse = used;
ticksUntilReady = 20 + Math.max( 0, nodes / 100 - 20 );
channelsByBlocks = nodes * used;
channelPowerUsage = channelsByBlocks / 128.0;
myGrid.getPivot().beginVisit( new AdHocChannelUpdater( used ) );
}
else if ( controllerState == ControllerState.CONTROLLER_CONFLICT )
{
ticksUntilReady = 20;
myGrid.getPivot().beginVisit( new AdHocChannelUpdater( 0 ) );
}
else
{
int nodes = myGrid.getNodes().size();
ticksUntilReady = 20 + Math.max( 0, nodes / 100 - 20 );
HashSet<IPathItem> closedList = new HashSet<IPathItem>();
semiOpen = new HashSet<IPathItem>();
// myGrid.getPivot().beginVisit( new AdHocChannelUpdater( 0 )
// );
for (IGridNode node : myGrid.getMachines( TileController.class ))
{
closedList.add( (IPathItem) node );
for (IGridConnection gcc : node.getConnections())
{
GridConnection gc = (GridConnection) gcc;
if ( !(gc.getOtherSide( node ).getMachine() instanceof TileController) )
{
List<IPathItem> open = new LinkedList<IPathItem>();
closedList.add( gc );
open.add( gc );
gc.setControllerRoute( (GridNode) node, true );
active.add( new PathSegment( this, open, semiOpen, closedList ) );
}
}
}
}
}
if ( !active.isEmpty() || ticksUntilReady > 0 )
{
Iterator<PathSegment> i = active.iterator();
while (i.hasNext())
{
PathSegment pat = i.next();
if ( pat.step() )
{
pat.isDead = true;
i.remove();
}
}
ticksUntilReady--;
if ( active.isEmpty() && ticksUntilReady <= 0 )
{
if ( controllerState == ControllerState.CONTROLLER_ONLINE )
{
final Iterator<TileController> controllerIterator = this.controllers.iterator();
if (controllerIterator.hasNext())
{
final TileController controller = controllerIterator.next();
controller.getGridNode( ForgeDirection.UNKNOWN ).beginVisit( new ControllerChannelUpdater() );
}
}
// check for achievements
achievementPost();
booting = false;
channelPowerUsage = channelsByBlocks / 128.0;
myGrid.postEvent( new MENetworkBootingStatusChange() );
}
}
}
private void achievementPost()
{
if ( lastChannels != channelsInUse && AEConfig.instance.isFeatureEnabled( AEFeature.Channels ) )
{
Achievements currentBracket = getAchievementBracket( channelsInUse );
Achievements lastBracket = getAchievementBracket( lastChannels );
if ( currentBracket != lastBracket && currentBracket != null )
{
Set<Integer> players = new HashSet<Integer>();
for (IGridNode n : requireChannels)
players.add( n.getPlayerID() );
for (int id : players)
{
Platform.addStat( id, currentBracket.getAchievement() );
}
}
}
lastChannels = channelsInUse;
}
private Achievements getAchievementBracket(int ch)
{
if ( ch < 8 )
return null;
if ( ch < 128 )
return Achievements.Networking1;
if ( ch < 2048 )
return Achievements.Networking2;
return Achievements.Networking3;
}
private int calculateRequiredChannels()
{
int depth = 0;
semiOpen.clear();
for (IGridNode nodes : requireChannels)
{
if ( !semiOpen.contains( nodes ) )
{
IGridBlock gb = nodes.getGridBlock();
EnumSet<GridFlags> flags = gb.getFlags();
if ( flags.contains( GridFlags.COMPRESSED_CHANNEL ) && !blockDense.isEmpty() )
return 9;
depth++;
if ( flags.contains( GridFlags.MULTIBLOCK ) )
{
IGridMultiblock gmb = (IGridMultiblock) gb;
Iterator<IGridNode> i = gmb.getMultiblockNodes();
while (i.hasNext())
semiOpen.add( (IPathItem) i.next() );
}
}
}
return depth;
}
@Override
public void repath()
{
// clean up...
active.clear();
channelsByBlocks = 0;
updateNetwork = true;
}
@Override
public void removeNode(IGridNode gridNode, IGridHost machine)
{
if ( machine instanceof TileController )
{
controllers.remove( machine );
recalculateControllerNextTick = true;
}
EnumSet<GridFlags> flags = gridNode.getGridBlock().getFlags();
if ( flags.contains( GridFlags.REQUIRE_CHANNEL ) )
requireChannels.remove( gridNode );
if ( flags.contains( GridFlags.CANNOT_CARRY_COMPRESSED ) )
blockDense.remove( gridNode );
repath();
}
@Override
public void addNode(IGridNode gridNode, IGridHost machine)
{
if ( machine instanceof TileController )
{
controllers.add( (TileController) machine );
recalculateControllerNextTick = true;
}
EnumSet<GridFlags> flags = gridNode.getGridBlock().getFlags();
if ( flags.contains( GridFlags.REQUIRE_CHANNEL ) )
requireChannels.add( gridNode );
if ( flags.contains( GridFlags.CANNOT_CARRY_COMPRESSED ) )
blockDense.add( gridNode );
repath();
}
@MENetworkEventSubscribe
void updateNodReq(MENetworkChannelChanged ev)
{
IGridNode gridNode = ev.node;
if ( gridNode.getGridBlock().getFlags().contains( GridFlags.REQUIRE_CHANNEL ) )
requireChannels.add( gridNode );
else
requireChannels.remove( gridNode );
repath();
}
private void recalcController()
{
recalculateControllerNextTick = false;
ControllerState old = controllerState;
if ( controllers.isEmpty() )
{
controllerState = ControllerState.NO_CONTROLLER;
}
else
{
IGridNode startingNode = controllers.iterator().next().getGridNode( ForgeDirection.UNKNOWN );
if ( startingNode == null )
{
controllerState = ControllerState.CONTROLLER_CONFLICT;
return;
}
DimensionalCoord dc = startingNode.getGridBlock().getLocation();
ControllerValidator cv = new ControllerValidator( dc.x, dc.y, dc.z );
startingNode.beginVisit( cv );
if ( cv.isValid && cv.found == controllers.size() )
controllerState = ControllerState.CONTROLLER_ONLINE;
else
controllerState = ControllerState.CONTROLLER_CONFLICT;
}
if ( old != controllerState )
{
myGrid.postEvent( new MENetworkControllerChange() );
}
}
@Override
public ControllerState getControllerState()
{
return this.controllerState;
}
@Override
public boolean isNetworkBooting()
{
return !this.active.isEmpty() && !this.booting;
}
@Override
public void onSplit(IGridStorage storageB)
{
}
@Override
public void onJoin(IGridStorage storageB)
{
}
@Override
public void populateGridStorage(IGridStorage storage)
{
}
}