diff --git a/src/main/java/appeng/fluids/parts/PartFluidStorageBus.java b/src/main/java/appeng/fluids/parts/PartFluidStorageBus.java index a32c9b95a..62f5a422e 100644 --- a/src/main/java/appeng/fluids/parts/PartFluidStorageBus.java +++ b/src/main/java/appeng/fluids/parts/PartFluidStorageBus.java @@ -207,7 +207,7 @@ public class PartFluidStorageBus extends PartSharedStorageBus implements IMEMoni { this.resetCacheLogic = 2; } - else + else if( resetCacheLogic < 2 ) { this.resetCacheLogic = 1; } diff --git a/src/main/java/appeng/me/cache/EnergyGridCache.java b/src/main/java/appeng/me/cache/EnergyGridCache.java index d8c945824..f012cf59b 100644 --- a/src/main/java/appeng/me/cache/EnergyGridCache.java +++ b/src/main/java/appeng/me/cache/EnergyGridCache.java @@ -30,6 +30,7 @@ import java.util.PriorityQueue; import java.util.Queue; import java.util.Set; +import com.google.common.base.Preconditions; import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset; import com.google.common.collect.Sets; @@ -63,11 +64,10 @@ import appeng.me.energy.EnergyWatcher; public class EnergyGridCache implements IEnergyGrid { - private static final double MAX_BUFFER_STORAGE = 200; + private static final double MAX_BUFFER_STORAGE = 800; private static final Comparator COMPARATOR_HIGHEST_AMOUNT_STORED_FIRST = ( o1, o2 ) -> Double.compare( o2.getProviderStoredEnergy(), o1.getProviderStoredEnergy() ); - private static final Comparator COMPARATOR_LOWEST_PERCENTAGE_FIRST = ( o1, o2 ) -> - { + private static final Comparator COMPARATOR_LOWEST_PERCENTAGE_FIRST = ( o1, o2 ) -> { final double percent1 = ( o1.getProviderStoredEnergy() + 1 ) / ( o1.getProviderMaxEnergy() + 1 ); final double percent2 = ( o2.getProviderStoredEnergy() + 1 ) / ( o2.getProviderMaxEnergy() + 1 ); @@ -75,9 +75,24 @@ public class EnergyGridCache implements IEnergyGrid }; private final NavigableSet interests = Sets.newTreeSet(); + + // Should only be modified from the add/remove methods below to guard against + // concurrent modifications private final double averageLength = 40.0; private final Set providers = new LinkedHashSet<>(); + // Used to track whether an extraction is currently in progress, to fail fast + // when something externally + // modifies the energy grid. + private boolean ongoingExtractOperation = false; + + // Should only be modified from the add/remove methods below to guard against + // concurrent modifications private final Set requesters = new LinkedHashSet<>(); + // Used to track whether an injection is currently in progress, to fail fast + // when something externally + // modifies the energy grid. + private boolean ongoingInjectOperation = false; + private final Multiset energyGridProviders = HashMultiset.create(); private final IGrid myGrid; private final HashMap watchers = new HashMap<>(); @@ -109,6 +124,10 @@ public class EnergyGridCache implements IEnergyGrid private double lastStoredPower = -1; private final GridPowerStorage localStorage = new GridPowerStorage(); + private Set providerToRemove = new HashSet<>(); + private Set requesterToRemove = new HashSet<>(); + private Set providersToAdd = new HashSet<>(); + private Set requesterToAdd = new HashSet<>(); public EnergyGridCache( final IGrid g ) { @@ -147,13 +166,27 @@ public class EnergyGridCache implements IEnergyGrid case PROVIDE_POWER: if( ev.storage.getPowerFlow() != AccessRestriction.WRITE ) { - this.providers.add( ev.storage ); + if( !ongoingExtractOperation ) + { + addProvider( ev.storage ); + } + else + { + this.providersToAdd.add( ev.storage ); + } } break; case REQUEST_POWER: if( ev.storage.getPowerFlow() != AccessRestriction.READ ) { - this.requesters.add( ev.storage ); + if( !ongoingInjectOperation ) + { + addRequester( ev.storage ); + } + else + { + this.requesterToAdd.add( ev.storage ); + } } break; } @@ -300,22 +333,54 @@ public class EnergyGridCache implements IEnergyGrid { double extractedPower = 0; + this.providers.addAll( providersToAdd ); + providersToAdd.clear(); + final Iterator it = this.providers.iterator(); - while ( extractedPower < amt && it.hasNext() ) + ongoingExtractOperation = true; + boolean ls = false; + try { - final IAEPowerStorage node = it.next(); - - final double req = amt - extractedPower; - final double newPower = node.extractAEPower( req, mode, PowerMultiplier.ONE ); - extractedPower += newPower; - - if( newPower < req && mode == Actionable.MODULATE ) + while ( extractedPower < amt && it.hasNext() ) { - it.remove(); + final IAEPowerStorage node = it.next(); + + if( node == localStorage && mode == Actionable.MODULATE ) + { + ls = true; + continue; + } + + final double req = amt - extractedPower; + final double newPower = node.extractAEPower( req, mode, PowerMultiplier.ONE ); + extractedPower += newPower; + + if( newPower < req && mode == Actionable.MODULATE ) + { + it.remove(); + } + } + } finally + { + ongoingExtractOperation = false; + if( ls && extractedPower < amt ) + { + final double req = amt - extractedPower; + final double newPower = localStorage.extractAEPower( req, mode, PowerMultiplier.ONE ); + + extractedPower += newPower; + + if( newPower < req ) + { + providers.remove( localStorage ); + } } } + providers.removeIf( p -> providerToRemove.contains( p ) ); + this.providerToRemove.clear(); + final double result = Math.min( extractedPower, amt ); if( mode == Actionable.MODULATE ) @@ -337,19 +402,33 @@ public class EnergyGridCache implements IEnergyGrid { final double originalAmount = amt; + this.requesters.addAll( requesterToAdd ); + requesterToAdd.clear(); + final Iterator it = this.requesters.iterator(); - while ( amt > 0 && it.hasNext() ) + ongoingInjectOperation = true; + try { - final IAEPowerStorage node = it.next(); - amt = node.injectAEPower( amt, mode ); - - if( amt > 0 && mode == Actionable.MODULATE ) + while ( amt > 0 && it.hasNext() ) { - it.remove(); + final IAEPowerStorage node = it.next(); + + amt = node.injectAEPower( amt, mode ); + + if( amt > 0 && mode == Actionable.MODULATE ) + { + it.remove(); + } } + } finally + { + ongoingInjectOperation = false; } + requesters.removeIf( r -> requesterToRemove.contains( r ) ); + this.requesterToRemove.clear(); + final double overflow = Math.max( 0.0, amt ); if( mode == Actionable.MODULATE ) @@ -366,7 +445,7 @@ public class EnergyGridCache implements IEnergyGrid double required = 0; final Iterator it = this.requesters.iterator(); - while( required < maxRequired && it.hasNext() ) + while ( required < maxRequired && it.hasNext() ) { final IAEPowerStorage node = it.next(); if( node.getPowerFlow() != AccessRestriction.READ ) @@ -427,11 +506,7 @@ public class EnergyGridCache implements IEnergyGrid @Override public double getStoredPower() { - if( this.availableTicksSinceUpdate > 90 ) - { - this.refreshPower(); - } - + this.refreshPower(); return Math.max( 0.0, this.globalAvailablePower ); } @@ -504,9 +579,22 @@ public class EnergyGridCache implements IEnergyGrid this.globalMaxPower -= ps.getAEMaxPower(); this.globalAvailablePower -= ps.getAECurrentPower(); } - - this.providers.remove( ps ); - this.requesters.remove( ps ); + if( !ongoingExtractOperation ) + { + removeProvider( ps ); + } + else + { + this.providerToRemove.add( ps ); + } + if( !ongoingInjectOperation ) + { + removeRequester( ps ); + } + else + { + this.requesterToRemove.add( ps ); + } } } @@ -522,6 +610,31 @@ public class EnergyGridCache implements IEnergyGrid } } + private void addRequester( IAEPowerStorage requester ) + { + Preconditions.checkState( !ongoingInjectOperation, "Cannot modify energy requesters while energy is being injected." ); + this.requesters.add( requester ); + } + + private void removeRequester( IAEPowerStorage requester ) + { + Preconditions.checkState( !ongoingInjectOperation, "Cannot modify energy requesters while energy is being injected." ); + this.requesters.remove( requester ); + } + + private void addProvider( IAEPowerStorage provider ) + { + Preconditions.checkState( !ongoingExtractOperation, "Cannot modify energy providers while energy is being extracted." ); + this.providers.add( provider ); + } + + private void removeProvider( IAEPowerStorage provider ) + { + Preconditions.checkState( !ongoingExtractOperation, "Cannot modify energy providers while energy is being extracted." ); + this.providers.remove( provider ); + } + + @Override public void addNode( final IGridNode node, final IGridHost machine ) { @@ -553,12 +666,26 @@ public class EnergyGridCache implements IEnergyGrid if( current > 0 && ps.getPowerFlow() != AccessRestriction.WRITE ) { this.globalAvailablePower += current; - this.providers.add( ps ); + if( !ongoingExtractOperation ) + { + addProvider( ps ); + } + else + { + this.providersToAdd.add( ps ); + } } if( current < max && ps.getPowerFlow() != AccessRestriction.READ ) { - this.requesters.add( ps ); + if( !ongoingInjectOperation ) + { + addRequester( ps ); + } + else + { + this.requesterToAdd.add( ps ); + } } } } diff --git a/src/main/java/appeng/parts/misc/PartSharedStorageBus.java b/src/main/java/appeng/parts/misc/PartSharedStorageBus.java index 18a3fb1bd..2a473031c 100644 --- a/src/main/java/appeng/parts/misc/PartSharedStorageBus.java +++ b/src/main/java/appeng/parts/misc/PartSharedStorageBus.java @@ -22,8 +22,12 @@ package appeng.parts.misc; import java.util.Collections; import java.util.List; +import appeng.fluids.parts.PartFluidInterface; +import appeng.fluids.tile.TileFluidInterface; +import appeng.tile.networking.TileCableBus; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockAccess; @@ -153,7 +157,24 @@ public abstract class PartSharedStorageBus extends PartUpgradeable implements IG @Override public void onNeighborChanged( IBlockAccess w, BlockPos pos, BlockPos neighbor ) { - if( pos.offset( this.getSide().getFacing() ).equals( neighbor ) ) + + final TileEntity te = w.getTileEntity( neighbor ); + + // In case the TE was destroyed, we have to do a full reset immediately. + if( te instanceof TileCableBus ) + { + if( ( (TileCableBus) te ).getPart( this.getSide().getOpposite() ) instanceof PartFluidInterface ) + { + this.resetCache( true ); + this.resetCache(); + } + } + if( te == null || te instanceof TileFluidInterface ) + { + this.resetCache( true ); + this.resetCache(); + } + else { this.resetCache( false ); } diff --git a/src/main/java/appeng/parts/misc/PartStorageBus.java b/src/main/java/appeng/parts/misc/PartStorageBus.java index 20d7d2972..500544fed 100644 --- a/src/main/java/appeng/parts/misc/PartStorageBus.java +++ b/src/main/java/appeng/parts/misc/PartStorageBus.java @@ -23,6 +23,8 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import appeng.tile.misc.TileInterface; +import appeng.tile.networking.TileCableBus; import com.jaquadro.minecraft.storagedrawers.api.capabilities.IItemRepository; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; @@ -234,7 +236,7 @@ public class PartStorageBus extends PartUpgradeable implements IGridTickable, IC { this.resetCacheLogic = 2; } - else + else if( resetCacheLogic < 2 ) { this.resetCacheLogic = 1; } @@ -296,7 +298,15 @@ public class PartStorageBus extends PartUpgradeable implements IGridTickable, IC final TileEntity te = w.getTileEntity( neighbor ); // In case the TE was destroyed, we have to do a full reset immediately. - if( te == null ) + if( te instanceof TileCableBus ) + { + if( ( (TileCableBus) te ).getPart( this.getSide().getOpposite() ) instanceof PartInterface ) + { + this.resetCache( true ); + this.resetCache(); + } + } + if( te == null || te instanceof TileInterface ) { this.resetCache( true ); this.resetCache(); diff --git a/src/main/java/appeng/tile/networking/TileEnergyCell.java b/src/main/java/appeng/tile/networking/TileEnergyCell.java index 525ef0cce..ac18962a4 100644 --- a/src/main/java/appeng/tile/networking/TileEnergyCell.java +++ b/src/main/java/appeng/tile/networking/TileEnergyCell.java @@ -155,7 +155,7 @@ public class TileEnergyCell extends AENetworkTile implements IAEPowerStorage return 0; } - if( this.internalCurrentPower < 0.01 && amt > 0.01 ) + if( this.internalCurrentPower < 0.01 && amt > 0 ) { this.getProxy().getNode().getGrid().postEvent( new MENetworkPowerStorage( this, PowerEventType.PROVIDE_POWER ) ); } @@ -217,7 +217,7 @@ public class TileEnergyCell extends AENetworkTile implements IAEPowerStorage final boolean wasFull = this.internalCurrentPower >= this.getInternalMaxPower() - 0.001; - if( wasFull && amt > 0.001 ) + if( wasFull && amt > 0 ) { try {