/* * 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.HashMap; import java.util.PriorityQueue; import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; import net.minecraft.util.ReportedException; import appeng.api.networking.IGrid; import appeng.api.networking.IGridHost; import appeng.api.networking.IGridNode; import appeng.api.networking.IGridStorage; import appeng.api.networking.ticking.IGridTickable; import appeng.api.networking.ticking.ITickManager; import appeng.api.networking.ticking.TickRateModulation; import appeng.api.networking.ticking.TickingRequest; import appeng.me.cache.helpers.TickTracker; public class TickManagerCache implements ITickManager { private final IGrid myGrid; private final HashMap<IGridNode, TickTracker> alertable = new HashMap<IGridNode, TickTracker>(); private final HashMap<IGridNode, TickTracker> sleeping = new HashMap<IGridNode, TickTracker>(); private final HashMap<IGridNode, TickTracker> awake = new HashMap<IGridNode, TickTracker>(); private final PriorityQueue<TickTracker> upcomingTicks = new PriorityQueue<TickTracker>(); private long currentTick = 0; public TickManagerCache( final IGrid g ) { this.myGrid = g; } public long getCurrentTick() { return this.currentTick; } public long getAvgNanoTime( final IGridNode node ) { TickTracker tt = this.awake.get( node ); if( tt == null ) { tt = this.sleeping.get( node ); } if( tt == null ) { return -1; } return tt.getAvgNanos(); } @Override public void onUpdateTick() { TickTracker tt = null; try { this.currentTick++; while( !this.upcomingTicks.isEmpty() ) { tt = this.upcomingTicks.peek(); final int diff = (int) ( this.currentTick - tt.getLastTick() ); if( diff >= tt.getCurrentRate() ) { // remove tt.. this.upcomingTicks.poll(); final TickRateModulation mod = tt.getGridTickable().tickingRequest( tt.getNode(), diff ); switch( mod ) { case FASTER: tt.setRate( tt.getCurrentRate() - 2 ); break; case IDLE: tt.setRate( tt.getRequest().maxTickRate ); break; case SAME: break; case SLEEP: this.sleepDevice( tt.getNode() ); break; case SLOWER: tt.setRate( tt.getCurrentRate() + 1 ); break; case URGENT: tt.setRate( 0 ); break; default: break; } if( this.awake.containsKey( tt.getNode() ) ) { this.addToQueue( tt ); } } else { return; // done! } } } catch( final Throwable t ) { final CrashReport crashreport = CrashReport.makeCrashReport( t, "Ticking GridNode" ); final CrashReportCategory crashreportcategory = crashreport.makeCategory( tt.getGridTickable().getClass().getSimpleName() + " being ticked." ); tt.addEntityCrashInfo( crashreportcategory ); throw new ReportedException( crashreport ); } } private void addToQueue( final TickTracker tt ) { tt.setLastTick( this.currentTick ); this.upcomingTicks.add( tt ); } @Override public void removeNode( final IGridNode gridNode, final IGridHost machine ) { if( machine instanceof IGridTickable ) { this.alertable.remove( gridNode ); this.sleeping.remove( gridNode ); this.awake.remove( gridNode ); } } @Override public void addNode( final IGridNode gridNode, final IGridHost machine ) { if( machine instanceof IGridTickable ) { final TickingRequest tr = ( (IGridTickable) machine ).getTickingRequest( gridNode ); if( tr != null ) { final TickTracker tt = new TickTracker( tr, gridNode, (IGridTickable) machine, this.currentTick, this ); if( tr.canBeAlerted ) { this.alertable.put( gridNode, tt ); } if( tr.isSleeping ) { this.sleeping.put( gridNode, tt ); } else { this.awake.put( gridNode, tt ); this.addToQueue( tt ); } } } } @Override public void onSplit( final IGridStorage storageB ) { } @Override public void onJoin( final IGridStorage storageB ) { } @Override public void populateGridStorage( final IGridStorage storage ) { } @Override public boolean alertDevice( final IGridNode node ) { final TickTracker tt = this.alertable.get( node ); if( tt == null ) { return false; } // throw new RuntimeException( // "Invalid alerted device, this node is not marked as alertable, or part of this grid." ); // set to awake, this is for sanity. this.sleeping.remove( node ); this.awake.put( node, tt ); // configure sort. tt.setLastTick( tt.getLastTick() - tt.getRequest().maxTickRate ); tt.setCurrentRate( tt.getRequest().minTickRate ); // prevent dupes and tick build up. this.upcomingTicks.remove( tt ); this.upcomingTicks.add( tt ); return true; } @Override public boolean sleepDevice( final IGridNode node ) { if( this.awake.containsKey( node ) ) { final TickTracker gt = this.awake.get( node ); this.awake.remove( node ); this.sleeping.put( node, gt ); return true; } return false; } @Override public boolean wakeDevice( final IGridNode node ) { if( this.sleeping.containsKey( node ) ) { final TickTracker gt = this.sleeping.get( node ); this.sleeping.remove( node ); this.awake.put( node, gt ); this.addToQueue( gt ); return true; } return false; } }