/* * 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 . */ package appeng.me.cache; import java.util.HashMap; import java.util.PriorityQueue; 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; import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; import net.minecraft.util.ReportedException; public class TickManagerCache implements ITickManager { private final IGrid myGrid; private final HashMap alertable = new HashMap(); private final HashMap sleeping = new HashMap(); private final HashMap awake = new HashMap(); private final PriorityQueue upcomingTicks = new PriorityQueue(); 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; } }