/* * 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.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import net.minecraft.world.World; import appeng.api.config.AccessRestriction; import appeng.api.config.Actionable; import appeng.api.networking.IGrid; import appeng.api.networking.IGridHost; import appeng.api.networking.IGridNode; import appeng.api.networking.IGridStorage; import appeng.api.networking.crafting.ICraftingCPU; import appeng.api.networking.crafting.ICraftingCallback; import appeng.api.networking.crafting.ICraftingGrid; import appeng.api.networking.crafting.ICraftingJob; import appeng.api.networking.crafting.ICraftingLink; import appeng.api.networking.crafting.ICraftingMedium; import appeng.api.networking.crafting.ICraftingPatternDetails; import appeng.api.networking.crafting.ICraftingProvider; import appeng.api.networking.crafting.ICraftingProviderHelper; import appeng.api.networking.crafting.ICraftingRequester; import appeng.api.networking.crafting.ICraftingWatcher; import appeng.api.networking.crafting.ICraftingWatcherHost; import appeng.api.networking.energy.IEnergyGrid; import appeng.api.networking.events.MENetworkCraftingCpuChange; import appeng.api.networking.events.MENetworkCraftingPatternChange; import appeng.api.networking.events.MENetworkEventSubscribe; import appeng.api.networking.events.MENetworkPostCacheConstruction; import appeng.api.networking.security.BaseActionSource; import appeng.api.networking.storage.IStorageGrid; import appeng.api.storage.ICellProvider; import appeng.api.storage.IMEInventoryHandler; import appeng.api.storage.StorageChannel; import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEStack; import appeng.api.storage.data.IItemList; import appeng.crafting.CraftingJob; import appeng.crafting.CraftingLink; import appeng.crafting.CraftingLinkNexus; import appeng.crafting.CraftingWatcher; import appeng.me.cluster.implementations.CraftingCPUCluster; import appeng.me.helpers.GenericInterestManager; import appeng.tile.crafting.TileCraftingStorageTile; import appeng.tile.crafting.TileCraftingTile; import appeng.util.ItemSorters; public class CraftingGridCache implements ICraftingGrid, ICraftingProviderHelper, ICellProvider, IMEInventoryHandler { private static final ExecutorService CRAFTING_POOL; private static final Comparator COMPARATOR = new Comparator(){ @Override public int compare( final ICraftingPatternDetails firstDetail, final ICraftingPatternDetails nextDetail ) { return nextDetail.getPriority() - firstDetail.getPriority(); } }; static { final ThreadFactory factory = new ThreadFactory(){ @Override public Thread newThread( final Runnable ar ) { return new Thread( ar, "AE Crafting Calculator" ); } }; CRAFTING_POOL = Executors.newCachedThreadPool( factory ); } private final Set craftingCPUClusters = new HashSet(); private final Set craftingProviders = new HashSet(); private final Map craftingWatchers = new HashMap(); private final IGrid grid; private final Map> craftingMethods = new HashMap>(); private final Map> craftableItems = new HashMap>(); private final Set emitableItems = new HashSet(); private final Map craftingLinks = new HashMap(); private final Multimap interests = HashMultimap.create(); private final GenericInterestManager interestManager = new GenericInterestManager( this.interests ); private IStorageGrid storageGrid; private IEnergyGrid energyGrid; private boolean updateList = false; public CraftingGridCache( final IGrid grid ) { this.grid = grid; } @MENetworkEventSubscribe public void afterCacheConstruction( final MENetworkPostCacheConstruction cacheConstruction ) { this.storageGrid = this.grid.getCache( IStorageGrid.class ); this.energyGrid = this.grid.getCache( IEnergyGrid.class ); this.storageGrid.registerCellProvider( this ); } @Override public void onUpdateTick() { if( this.updateList ) { this.updateList = false; this.updateCPUClusters(); } final Iterator craftingLinkIterator = this.craftingLinks.values().iterator(); while( craftingLinkIterator.hasNext() ) { if( craftingLinkIterator.next().isDead( this.grid, this ) ) { craftingLinkIterator.remove(); } } for( final CraftingCPUCluster cpu : this.craftingCPUClusters ) { cpu.updateCraftingLogic( this.grid, this.energyGrid, this ); } } @Override public void removeNode( final IGridNode gridNode, final IGridHost machine ) { if( machine instanceof ICraftingWatcherHost ) { final ICraftingWatcher craftingWatcher = this.craftingWatchers.get( machine ); if( craftingWatcher != null ) { craftingWatcher.reset(); this.craftingWatchers.remove( machine ); } } if( machine instanceof ICraftingRequester ) { for( final CraftingLinkNexus link : this.craftingLinks.values() ) { if( link.isMachine( machine ) ) { link.removeNode(); } } } if( machine instanceof TileCraftingTile ) { this.updateList = true; } if( machine instanceof ICraftingProvider ) { this.craftingProviders.remove( machine ); this.updatePatterns(); } } @Override public void addNode( final IGridNode gridNode, final IGridHost machine ) { if( machine instanceof ICraftingWatcherHost ) { final ICraftingWatcherHost watcherHost = (ICraftingWatcherHost) machine; final CraftingWatcher watcher = new CraftingWatcher( this, watcherHost ); this.craftingWatchers.put( gridNode, watcher ); watcherHost.updateWatcher( watcher ); } if( machine instanceof ICraftingRequester ) { for( final ICraftingLink link : ( (ICraftingRequester) machine ).getRequestedJobs() ) { if( link instanceof CraftingLink ) { this.addLink( (CraftingLink) link ); } } } if( machine instanceof TileCraftingTile ) { this.updateList = true; } if( machine instanceof ICraftingProvider ) { this.craftingProviders.add( (ICraftingProvider) machine ); this.updatePatterns(); } } @Override public void onSplit( final IGridStorage destinationStorage ) { // nothing! } @Override public void onJoin( final IGridStorage sourceStorage ) { // nothing! } @Override public void populateGridStorage( final IGridStorage destinationStorage ) { // nothing! } private void updatePatterns() { final Map> oldItems = this.craftableItems; // erase list. this.craftingMethods.clear(); this.craftableItems.clear(); this.emitableItems.clear(); // update the stuff that was in the list... this.storageGrid.postAlterationOfStoredItems( StorageChannel.ITEMS, oldItems.keySet(), new BaseActionSource() ); // re-create list.. for( final ICraftingProvider provider : this.craftingProviders ) { provider.provideCrafting( this ); } final Map> tmpCraft = new HashMap>(); // new craftables! for( final ICraftingPatternDetails details : this.craftingMethods.keySet() ) { for( IAEItemStack out : details.getOutputs() ) { out = out.copy(); out.reset(); out.setCraftable( true ); Set methods = tmpCraft.get( out ); if( methods == null ) { tmpCraft.put( out, methods = new TreeSet( COMPARATOR ) ); } methods.add( details ); } } // make them immutable for( final Entry> e : tmpCraft.entrySet() ) { this.craftableItems.put( e.getKey(), ImmutableList.copyOf( e.getValue() ) ); } this.storageGrid.postAlterationOfStoredItems( StorageChannel.ITEMS, this.craftableItems.keySet(), new BaseActionSource() ); } private void updateCPUClusters() { this.craftingCPUClusters.clear(); for( final IGridNode cst : this.grid.getMachines( TileCraftingStorageTile.class ) ) { final TileCraftingStorageTile tile = (TileCraftingStorageTile) cst.getMachine(); final CraftingCPUCluster cluster = (CraftingCPUCluster) tile.getCluster(); if( cluster != null ) { this.craftingCPUClusters.add( cluster ); if( cluster.getLastCraftingLink() != null ) { this.addLink( (CraftingLink) cluster.getLastCraftingLink() ); } } } } public void addLink( final CraftingLink link ) { if( link.isStandalone() ) { return; } CraftingLinkNexus nexus = this.craftingLinks.get( link.getCraftingID() ); if( nexus == null ) { this.craftingLinks.put( link.getCraftingID(), nexus = new CraftingLinkNexus( link.getCraftingID() ) ); } link.setNexus( nexus ); } @MENetworkEventSubscribe public void updateCPUClusters( final MENetworkCraftingCpuChange c ) { this.updateList = true; } @MENetworkEventSubscribe public void updateCPUClusters( final MENetworkCraftingPatternChange c ) { this.updatePatterns(); } @Override public void addCraftingOption( final ICraftingMedium medium, final ICraftingPatternDetails api ) { List details = this.craftingMethods.get( api ); if( details == null ) { details = new ArrayList(); details.add( medium ); this.craftingMethods.put( api, details ); } else { details.add( medium ); } } @Override public void setEmitable( final IAEItemStack someItem ) { this.emitableItems.add( someItem.copy() ); } @Override public List getCellArray( final StorageChannel channel ) { final List list = new ArrayList( 1 ); if( channel == StorageChannel.ITEMS ) { list.add( this ); } return list; } @Override public int getPriority() { return Integer.MAX_VALUE; } @Override public AccessRestriction getAccess() { return AccessRestriction.WRITE; } @Override public boolean isPrioritized( final IAEStack input ) { return true; } @Override public boolean canAccept( final IAEStack input ) { for( final CraftingCPUCluster cpu : this.craftingCPUClusters ) { if( cpu.canAccept( input ) ) { return true; } } return false; } @Override public int getSlot() { return 0; } @Override public boolean validForPass( final int i ) { return i == 1; } @Override public IAEStack injectItems( IAEStack input, final Actionable type, final BaseActionSource src ) { for( final CraftingCPUCluster cpu : this.craftingCPUClusters ) { input = cpu.injectItems( input, type, src ); } return input; } @Override public IAEStack extractItems( final IAEStack request, final Actionable mode, final BaseActionSource src ) { return null; } @Override public IItemList getAvailableItems( final IItemList out ) { // add craftable items! for( final IAEItemStack stack : this.craftableItems.keySet() ) { out.addCrafting( stack ); } for( final IAEItemStack st : this.emitableItems ) { out.addCrafting( st ); } return out; } @Override public StorageChannel getChannel() { return StorageChannel.ITEMS; } @Override public ImmutableCollection getCraftingFor( final IAEItemStack whatToCraft, final ICraftingPatternDetails details, final int slotIndex, final World world ) { final ImmutableList res = this.craftableItems.get( whatToCraft ); if( res == null ) { if( details != null && details.isCraftable() ) { for( final IAEItemStack ais : this.craftableItems.keySet() ) { if( ais.getItem() == whatToCraft.getItem() && ( !ais.getItem().getHasSubtypes() || ais.getItemDamage() == whatToCraft.getItemDamage() ) ) { if( details.isValidItemForSlot( slotIndex, ais.getItemStack(), world ) ) { return this.craftableItems.get( ais ); } } } } return ImmutableSet.of(); } return res; } @Override public Future beginCraftingJob( final World world, final IGrid grid, final BaseActionSource actionSrc, final IAEItemStack slotItem, final ICraftingCallback cb ) { if( world == null || grid == null || actionSrc == null || slotItem == null ) { throw new IllegalArgumentException( "Invalid Crafting Job Request" ); } final CraftingJob job = new CraftingJob( world, grid, actionSrc, slotItem, cb ); return CRAFTING_POOL.submit( job, (ICraftingJob) job ); } @Override public ICraftingLink submitJob( final ICraftingJob job, final ICraftingRequester requestingMachine, final ICraftingCPU target, final boolean prioritizePower, final BaseActionSource src ) { if( job.isSimulation() ) { return null; } CraftingCPUCluster cpuCluster = null; if( target instanceof CraftingCPUCluster ) { cpuCluster = (CraftingCPUCluster) target; } if( target == null ) { final List validCpusClusters = new ArrayList(); for( final CraftingCPUCluster cpu : this.craftingCPUClusters ) { if( cpu.isActive() && !cpu.isBusy() && cpu.getAvailableStorage() >= job.getByteTotal() ) { validCpusClusters.add( cpu ); } } Collections.sort( validCpusClusters, new Comparator(){ @Override public int compare( final CraftingCPUCluster firstCluster, final CraftingCPUCluster nextCluster ) { if( prioritizePower ) { final int comparison = ItemSorters.compareLong( nextCluster.getCoProcessors(), firstCluster.getCoProcessors() ); if( comparison != 0 ) { return comparison; } return ItemSorters.compareLong( nextCluster.getAvailableStorage(), firstCluster.getAvailableStorage() ); } final int comparison = ItemSorters.compareLong( firstCluster.getCoProcessors(), nextCluster.getCoProcessors() ); if( comparison != 0 ) { return comparison; } return ItemSorters.compareLong( firstCluster.getAvailableStorage(), nextCluster.getAvailableStorage() ); } } ); if( !validCpusClusters.isEmpty() ) { cpuCluster = validCpusClusters.get( 0 ); } } if( cpuCluster != null ) { return cpuCluster.submitJob( this.grid, job, src, requestingMachine ); } return null; } @Override public ImmutableSet getCpus() { return ImmutableSet.copyOf( new ActiveCpuIterator( this.craftingCPUClusters ) ); } @Override public boolean canEmitFor( final IAEItemStack someItem ) { return this.emitableItems.contains( someItem ); } @Override public boolean isRequesting( final IAEItemStack what ) { for( final CraftingCPUCluster cluster : this.craftingCPUClusters ) { if( cluster.isMaking( what ) ) { return true; } } return false; } public List getMediums( final ICraftingPatternDetails key ) { List mediums = this.craftingMethods.get( key ); if( mediums == null ) { mediums = ImmutableList.of(); } return mediums; } public boolean hasCpu( final ICraftingCPU cpu ) { return this.craftingCPUClusters.contains( cpu ); } public GenericInterestManager getInterestManager() { return this.interestManager; } private static class ActiveCpuIterator implements Iterator { private final Iterator iterator; private CraftingCPUCluster cpuCluster; public ActiveCpuIterator( final Collection o ) { this.iterator = o.iterator(); this.cpuCluster = null; } @Override public boolean hasNext() { this.findNext(); return this.cpuCluster != null; } private void findNext() { while( this.iterator.hasNext() && this.cpuCluster == null ) { this.cpuCluster = this.iterator.next(); if( !this.cpuCluster.isActive() || this.cpuCluster.isDestroyed() ) { this.cpuCluster = null; } } } @Override public ICraftingCPU next() { final ICraftingCPU o = this.cpuCluster; this.cpuCluster = null; return o; } @Override public void remove() { // no.. } } }