Initial Fluid Integration (#3510)

* Added Fluid Storage Cells

* Added Fluid Import Bus

* Added Fluid Export Bus

* Added Fluid Storage Bus

* Added Fluid Terminal.
While holding a item with the FLUID_HANDLER_ITEM_CAPABILITY, such as a tank
Left click on a fluid to extract the fluid
Right click to insert the fluid
This commit is contained in:
Brock
2018-06-12 03:55:33 +10:00
committed by yueh
parent 89bebc4385
commit d16677224a
126 changed files with 8031 additions and 778 deletions
@@ -0,0 +1,106 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, 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.fluids.contents;
import javax.annotation.Nonnull;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidUtil;
import appeng.core.Api;
import appeng.fluids.items.FluidDummyItem;
import appeng.items.contents.CellConfig;
/**
* @author DrummerMC
* @version rv6 - 2018-01-22
* @since rv6 2018-01-22
*/
public class FluidCellConfig extends CellConfig
{
public FluidCellConfig( ItemStack is )
{
super( is );
}
@Override
@Nonnull
public ItemStack insertItem( int slot, @Nonnull ItemStack stack, boolean simulate )
{
if( stack.isEmpty() || stack.getItem() instanceof FluidDummyItem )
{
super.insertItem( slot, stack, simulate );
}
FluidStack fluid = FluidUtil.getFluidContained( stack );
if( fluid == null || !Api.INSTANCE.definitions().items().dummyFluidItem().maybeStack( 1 ).isPresent() )
{
return stack;
}
fluid.amount = Fluid.BUCKET_VOLUME;
ItemStack is = Api.INSTANCE.definitions().items().dummyFluidItem().maybeStack( 1 ).get();
FluidDummyItem item = (FluidDummyItem) is.getItem();
item.setFluidStack( is, fluid );
return super.insertItem( slot, is, simulate );
}
@Override
public void setStackInSlot( int slot, @Nonnull ItemStack stack )
{
if( stack.isEmpty() || stack.getItem() instanceof FluidDummyItem )
{
super.setStackInSlot( slot, stack );
}
FluidStack fluid = FluidUtil.getFluidContained( stack );
if( fluid == null || !Api.INSTANCE.definitions().items().dummyFluidItem().maybeStack( 1 ).isPresent() )
{
return;
}
fluid.amount = Fluid.BUCKET_VOLUME;
ItemStack is = Api.INSTANCE.definitions().items().dummyFluidItem().maybeStack( 1 ).get();
FluidDummyItem item = (FluidDummyItem) is.getItem();
item.setFluidStack( is, fluid );
super.setStackInSlot( slot, is );
}
@Override
public boolean isItemValidForSlot( int slot, ItemStack stack )
{
if( stack.isEmpty() || stack.getItem() instanceof FluidDummyItem )
{
super.isItemValidForSlot( slot, stack );
}
FluidStack fluid = FluidUtil.getFluidContained( stack );
if( fluid == null || !Api.INSTANCE.definitions().items().dummyFluidItem().maybeStack( 1 ).isPresent() )
{
return false;
}
fluid.amount = Fluid.BUCKET_VOLUME;
ItemStack is = Api.INSTANCE.definitions().items().dummyFluidItem().maybeStack( 1 ).get();
FluidDummyItem item = (FluidDummyItem) is.getItem();
item.setFluidStack( is, fluid );
return super.isItemValidForSlot( slot, is );
}
}
@@ -0,0 +1,116 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, 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.fluids.items;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import appeng.api.AEApi;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.channels.IFluidStorageChannel;
import appeng.api.storage.data.IAEFluidStack;
import appeng.fluids.contents.FluidCellConfig;
import appeng.items.materials.MaterialType;
import appeng.items.storage.AbstractStorageCell;
import appeng.util.InventoryAdaptor;
/**
* @author DrummerMC
* @version rv6 - 2018-01-17
* @since rv6 2018-01-17
*/
public final class BasicFluidStorageCell extends AbstractStorageCell<IAEFluidStack>
{
private final int perType;
private final double idleDrain;
public BasicFluidStorageCell( final MaterialType whichCell, final int kilobytes )
{
super( whichCell, kilobytes );
switch( whichCell )
{
case FLUID_CELL1K_PART:
this.idleDrain = 0.5;
this.perType = 8;
break;
case FLUID_CELL4K_PART:
this.idleDrain = 1.0;
this.perType = 32;
break;
case FLUID_CELL16K_PART:
this.idleDrain = 1.5;
this.perType = 128;
break;
case FLUID_CELL64K_PART:
this.idleDrain = 2.0;
this.perType = 512;
break;
default:
this.idleDrain = 0.0;
this.perType = 8;
}
}
@Override
public int getBytesPerType( ItemStack cellItem )
{
return this.perType;
}
@Override
public double getIdleDrain()
{
return this.idleDrain;
}
@Override
public IStorageChannel<IAEFluidStack> getChannel()
{
return AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class );
}
@Override
public int getTotalTypes( final ItemStack cellItem )
{
return 5;
}
@Override
public IItemHandler getConfigInventory( final ItemStack is )
{
return new FluidCellConfig( is );
}
@Override
protected void dropEmptyStorageCellCase( final InventoryAdaptor ia, final EntityPlayer player )
{
AEApi.instance().definitions().materials().emptyStorageCell().maybeStack( 1 ).ifPresent( is -> {
final ItemStack extraA = ia.addItems( is );
if( !extraA.isEmpty() )
{
player.dropItem( extraA, false );
}
} );
}
}
@@ -0,0 +1,83 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, 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.fluids.items;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.NonNullList;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import appeng.items.AEBaseItem;
/**
* Dummy item to display the fluid Icon
*
* @author DrummerMC
* @version rv6 - 2018-01-22
* @since rv6 2018-01-22
*/
public class FluidDummyItem extends AEBaseItem
{
@Override
public String getItemStackDisplayName( ItemStack stack )
{
FluidStack fluidStack = getFluidStack( stack );
if( fluidStack == null )
{
fluidStack = new FluidStack( FluidRegistry.WATER, Fluid.BUCKET_VOLUME );
}
return fluidStack.getLocalizedName();
}
public FluidStack getFluidStack( ItemStack is )
{
if( is.hasTagCompound() )
{
NBTTagCompound tag = is.getTagCompound();
return FluidStack.loadFluidStackFromNBT( tag );
}
return null;
}
public void setFluidStack( ItemStack is, FluidStack fs )
{
if( fs == null )
{
is.setTagCompound( null );
}
else
{
NBTTagCompound tag = new NBTTagCompound();
fs.writeToNBT( tag );
is.setTagCompound( tag );
}
}
@Override
protected void getCheckedSubItems( final CreativeTabs creativeTab, final NonNullList<ItemStack> itemStacks )
{
//Don't show this item in CreativeTabs
}
}
@@ -0,0 +1,39 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, 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.fluids.items;
import appeng.bootstrap.IItemRendering;
import appeng.bootstrap.ItemRenderingCustomizer;
import appeng.client.render.DummyFluidItemModel;
/**
* @author DrummerMC
* @version rv6 - 2018-01-22
* @since rv6 2018-01-22
*/
public class FluidDummyItemRendering extends ItemRenderingCustomizer
{
@Override
public void customize( IItemRendering rendering )
{
rendering.builtInModel( "models/item/dummy_fluid_item", new DummyFluidItemModel() );
}
}
@@ -0,0 +1,300 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, 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.fluids.parts;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidTankProperties;
import appeng.api.AEApi;
import appeng.api.config.Actionable;
import appeng.api.networking.security.IActionSource;
import appeng.api.networking.storage.IBaseMonitor;
import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.storage.IMEInventory;
import appeng.api.storage.IMEMonitorHandlerReceiver;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.channels.IFluidStorageChannel;
import appeng.api.storage.data.IAEFluidStack;
import appeng.api.storage.data.IItemList;
import appeng.me.GridAccessException;
import appeng.me.helpers.IGridProxyable;
import appeng.me.storage.ITickingMonitor;
import appeng.util.item.AEFluidStack;
/**
* Wraps an Fluid Handler in such a way that it can be used as an IMEInventory for fluids.
*
* @author BrockWS
* @version rv6 - 22/05/2018
* @since rv6 22/05/2018
*/
public class FluidHandlerAdapter implements IMEInventory<IAEFluidStack>, IBaseMonitor<IAEFluidStack>, ITickingMonitor
{
private final Map<IMEMonitorHandlerReceiver<IAEFluidStack>, Object> listeners = new HashMap<>();
private IActionSource source;
private final IFluidHandler fluidHandler;
private final IGridProxyable proxyable;
private final FluidHandlerAdapter.InventoryCache cache;
FluidHandlerAdapter( IFluidHandler fluidHandler, IGridProxyable proxy )
{
this.fluidHandler = fluidHandler;
this.proxyable = proxy;
this.cache = new FluidHandlerAdapter.InventoryCache( this.fluidHandler );
}
@Override
public IAEFluidStack injectItems( IAEFluidStack input, Actionable type, IActionSource src )
{
FluidStack fluidStack = input.getFluidStack();
// Insert
int wasFillled = this.fluidHandler.fill( fluidStack, type != Actionable.SIMULATE );
int remaining = fluidStack.amount - wasFillled;
if( fluidStack.amount == remaining )
{
// The stack was unmodified, target tank is full
return input;
}
if( type == Actionable.MODULATE )
{
try
{
this.proxyable.getProxy().getTick().alertDevice( this.proxyable.getProxy().getNode() );
}
catch( GridAccessException ignore )
{
// meh
}
}
fluidStack.amount = remaining;
return AEFluidStack.fromFluidStack( fluidStack );
}
@Override
public IAEFluidStack extractItems( IAEFluidStack request, Actionable mode, IActionSource src )
{
FluidStack requestedFluidStack = request.getFluidStack();
final boolean doDrain = ( mode == Actionable.MODULATE );
// Drain the fluid from the tank
FluidStack gathered = this.fluidHandler.drain( requestedFluidStack, doDrain );
if( gathered == null )
{
// If nothing was pulled from the tank, return null
return null;
}
if( mode == Actionable.MODULATE )
{
try
{
this.proxyable.getProxy().getTick().alertDevice( this.proxyable.getProxy().getNode() );
}
catch( GridAccessException ignore )
{
// meh
}
}
return AEFluidStack.fromFluidStack( gathered );
}
@Override
public TickRateModulation onTick()
{
List<IAEFluidStack> changes = this.cache.update();
if( !changes.isEmpty() )
{
this.postDifference( changes );
return TickRateModulation.URGENT;
}
else
{
return TickRateModulation.SLOWER;
}
}
@Override
public IItemList<IAEFluidStack> getAvailableItems( IItemList<IAEFluidStack> out )
{
return this.cache.getAvailableItems( out );
}
@Override
public IStorageChannel<IAEFluidStack> getChannel()
{
return AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class );
}
@Override
public void setActionSource( IActionSource source )
{
this.source = source;
}
@Override
public void addListener( final IMEMonitorHandlerReceiver<IAEFluidStack> l, final Object verificationToken )
{
this.listeners.put( l, verificationToken );
}
@Override
public void removeListener( final IMEMonitorHandlerReceiver<IAEFluidStack> l )
{
this.listeners.remove( l );
}
private void postDifference( Iterable<IAEFluidStack> a )
{
final Iterator<Map.Entry<IMEMonitorHandlerReceiver<IAEFluidStack>, Object>> i = this.listeners.entrySet().iterator();
while( i.hasNext() )
{
final Map.Entry<IMEMonitorHandlerReceiver<IAEFluidStack>, Object> l = i.next();
final IMEMonitorHandlerReceiver<IAEFluidStack> key = l.getKey();
if( key.isValid( l.getValue() ) )
{
key.postChange( this, a, this.source );
}
else
{
i.remove();
}
}
}
private static class InventoryCache
{
private IAEFluidStack[] cachedAeStacks = new IAEFluidStack[0];
private final IFluidHandler fluidHandler;
public InventoryCache( IFluidHandler fluidHandler )
{
this.fluidHandler = fluidHandler;
}
public List<IAEFluidStack> update()
{
final List<IAEFluidStack> changes = new ArrayList<>();
final IFluidTankProperties[] tankProperties = this.fluidHandler.getTankProperties();
final int slots = tankProperties.length;
// Make room for new slots
if( slots > this.cachedAeStacks.length )
{
this.cachedAeStacks = Arrays.copyOf( this.cachedAeStacks, slots );
}
for( int slot = 0; slot < slots; slot++ )
{
// Save the old stuff
final IAEFluidStack oldAEFS = this.cachedAeStacks[slot];
final FluidStack newFS = tankProperties[slot].getContents();
this.handlePossibleSlotChanges( slot, oldAEFS, newFS, changes );
}
// Handle cases where the number of slots actually is lower now than before
if( slots < this.cachedAeStacks.length )
{
for( int slot = slots; slot < this.cachedAeStacks.length; slot++ )
{
final IAEFluidStack aeStack = this.cachedAeStacks[slot];
if( aeStack != null )
{
final IAEFluidStack a = aeStack.copy();
a.setStackSize( -a.getStackSize() );
changes.add( a );
}
}
this.cachedAeStacks = Arrays.copyOf( this.cachedAeStacks, slots );
}
return changes;
}
public IItemList<IAEFluidStack> getAvailableItems( IItemList<IAEFluidStack> out )
{
Arrays.stream( this.cachedAeStacks ).forEach( out::add );
return out;
}
private void handlePossibleSlotChanges( int slot, IAEFluidStack oldAeFS, FluidStack newFS, List<IAEFluidStack> changes )
{
if( oldAeFS != null && oldAeFS.getFluidStack().isFluidEqual( newFS ) )
{
this.handleStackSizeChanged( slot, oldAeFS, newFS, changes );
}
else
{
this.handleFluidChanged( slot, oldAeFS, newFS, changes );
}
}
private void handleStackSizeChanged( int slot, IAEFluidStack oldAeFS, FluidStack newFS, List<IAEFluidStack> changes )
{
// Still the same fluid, but amount might have changed
final long diff = newFS.amount - oldAeFS.getStackSize();
if( diff != 0 )
{
final IAEFluidStack stack = oldAeFS.copy();
stack.setStackSize( newFS.amount );
this.cachedAeStacks[slot] = stack;
final IAEFluidStack a = stack.copy();
a.setStackSize( diff );
changes.add( a );
}
}
private void handleFluidChanged( int slot, IAEFluidStack oldAeFS, FluidStack newFS, List<IAEFluidStack> changes )
{
// Completely different fluid
this.cachedAeStacks[slot] = AEFluidStack.fromFluidStack( newFS );
// If we had a stack previously in this slot, notify the network about its disappearance
if( oldAeFS != null )
{
oldAeFS.setStackSize( -oldAeFS.getStackSize() );
changes.add( oldAeFS );
}
// Notify the network about the new stack. Note that this is null if newFS was null
if( this.cachedAeStacks[slot] != null )
{
changes.add( this.cachedAeStacks[slot] );
}
}
}
}
@@ -0,0 +1,185 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, 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.fluids.parts;
import javax.annotation.Nonnull;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
import appeng.api.config.Actionable;
import appeng.api.config.FuzzyMode;
import appeng.api.config.RedstoneMode;
import appeng.api.config.SchedulingMode;
import appeng.api.config.Settings;
import appeng.api.config.YesNo;
import appeng.api.networking.IGridNode;
import appeng.api.networking.security.IActionSource;
import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.networking.ticking.TickingRequest;
import appeng.api.parts.IPartCollisionHelper;
import appeng.api.parts.IPartModel;
import appeng.api.storage.IMEMonitor;
import appeng.api.storage.data.IAEFluidStack;
import appeng.api.storage.data.IAEItemStack;
import appeng.core.AppEng;
import appeng.core.settings.TickRates;
import appeng.items.parts.PartModels;
import appeng.me.GridAccessException;
import appeng.me.helpers.MachineSource;
import appeng.parts.PartModel;
import appeng.util.item.AEFluidStack;
/**
* @author BrockWS
* @version rv6 - 30/04/2018
* @since rv6 30/04/2018
*/
public class PartFluidExportBus extends PartSharedFluidBus
{
public static final ResourceLocation MODEL_BASE = new ResourceLocation( AppEng.MOD_ID, "part/fluid_export_bus_base" );
@PartModels
public static final IPartModel MODELS_OFF = new PartModel( MODEL_BASE, new ResourceLocation( AppEng.MOD_ID, "part/fluid_export_bus_off" ) );
@PartModels
public static final IPartModel MODELS_ON = new PartModel( MODEL_BASE, new ResourceLocation( AppEng.MOD_ID, "part/fluid_export_bus_on" ) );
@PartModels
public static final IPartModel MODELS_HAS_CHANNEL = new PartModel( MODEL_BASE, new ResourceLocation( AppEng.MOD_ID, "part/fluid_export_bus_has_channel" ) );
private final IActionSource source;
public PartFluidExportBus( ItemStack is )
{
super( is );
this.getConfigManager().registerSetting( Settings.REDSTONE_CONTROLLED, RedstoneMode.IGNORE );
this.getConfigManager().registerSetting( Settings.FUZZY_MODE, FuzzyMode.IGNORE_ALL );
this.getConfigManager().registerSetting( Settings.CRAFT_ONLY, YesNo.NO );
this.getConfigManager().registerSetting( Settings.SCHEDULING_MODE, SchedulingMode.DEFAULT );
this.source = new MachineSource( this );
}
@Override
public TickingRequest getTickingRequest( IGridNode node )
{
return new TickingRequest( TickRates.FluidExportBus.getMin(), TickRates.FluidExportBus.getMax(), this.isSleeping(), false );
}
@Override
public TickRateModulation tickingRequest( IGridNode node, int ticksSinceLastCall )
{
return this.canDoBusWork() ? this.doBusWork() : TickRateModulation.IDLE;
}
@Override
protected boolean canDoBusWork()
{
return this.getProxy().isActive();
}
@Override
protected TickRateModulation doBusWork()
{
if( !this.canDoBusWork() )
{
return TickRateModulation.IDLE;
}
final TileEntity te = this.getConnectedTE();
if( te != null && te.hasCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, this.getSide().getFacing() ) )
{
try
{
final IFluidHandler fh = te.getCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, this.getSide().getFacing() );
final IMEMonitor<IAEFluidStack> inv = this.getProxy().getStorage().getInventory( this.getChannel() );
if( fh != null )
{
for( int i = 0; i < this.getConfig().getSlots(); i++ )
{
IAEItemStack stack = this.getConfig().getAEStackInSlot( i );
if( stack != null && stack.getDefinition() != null )
{
IFluidHandlerItem ifh = stack.getDefinition().getCapability( CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null );
if( ifh == null )
{
throw new NullPointerException( "IFluidHandlerItem is null" );
}
AEFluidStack toExtract = AEFluidStack.fromFluidStack( ifh.drain( Integer.MAX_VALUE, false ) );
toExtract.setStackSize( this.calculateAmountToSend() );
IAEFluidStack out = inv.extractItems( toExtract, Actionable.SIMULATE, this.source );
if( out != null )
{
int wasInserted = fh.fill( out.getFluidStack(), true );
if( wasInserted > 0 )
{
inv.extractItems( toExtract, Actionable.MODULATE, this.source );
return TickRateModulation.FASTER;
}
}
}
}
return TickRateModulation.SLOWER;
}
}
catch( GridAccessException e )
{
// Ignore
}
}
return TickRateModulation.SLEEP;
}
@Override
public void getBoxes( final IPartCollisionHelper bch )
{
bch.addBox( 4, 4, 12, 12, 12, 14 );
bch.addBox( 5, 5, 14, 11, 11, 15 );
bch.addBox( 6, 6, 15, 10, 10, 16 );
bch.addBox( 6, 6, 11, 10, 10, 12 );
}
@Override
public RedstoneMode getRSMode()
{
return (RedstoneMode) this.getConfigManager().getSetting( Settings.REDSTONE_CONTROLLED );
}
@Nonnull
@Override
public IPartModel getStaticModels()
{
if( this.isActive() && this.isPowered() )
{
return MODELS_HAS_CHANNEL;
}
else if( this.isPowered() )
{
return MODELS_ON;
}
else
{
return MODELS_OFF;
}
}
}
@@ -0,0 +1,202 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, 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.fluids.parts;
import javax.annotation.Nonnull;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
import appeng.api.config.Actionable;
import appeng.api.config.FuzzyMode;
import appeng.api.config.RedstoneMode;
import appeng.api.config.SchedulingMode;
import appeng.api.config.Settings;
import appeng.api.config.YesNo;
import appeng.api.networking.IGridNode;
import appeng.api.networking.security.IActionSource;
import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.networking.ticking.TickingRequest;
import appeng.api.parts.IPartModel;
import appeng.api.storage.IMEMonitor;
import appeng.api.storage.data.IAEFluidStack;
import appeng.api.storage.data.IAEItemStack;
import appeng.core.AppEng;
import appeng.core.settings.TickRates;
import appeng.items.parts.PartModels;
import appeng.me.GridAccessException;
import appeng.me.helpers.MachineSource;
import appeng.parts.PartModel;
import appeng.util.item.AEFluidStack;
/**
* @author BrockWS
* @version rv6 - 30/04/2018
* @since rv6 30/04/2018
*/
public class PartFluidImportBus extends PartSharedFluidBus
{
public static final ResourceLocation MODEL_BASE = new ResourceLocation( AppEng.MOD_ID, "part/fluid_import_bus_base" );
@PartModels
public static final IPartModel MODELS_OFF = new PartModel( MODEL_BASE, new ResourceLocation( AppEng.MOD_ID, "part/fluid_import_bus_off" ) );
@PartModels
public static final IPartModel MODELS_ON = new PartModel( MODEL_BASE, new ResourceLocation( AppEng.MOD_ID, "part/fluid_import_bus_on" ) );
@PartModels
public static final IPartModel MODELS_HAS_CHANNEL = new PartModel( MODEL_BASE, new ResourceLocation( AppEng.MOD_ID, "part/fluid_import_bus_has_channel" ) );
private final IActionSource source;
public PartFluidImportBus( ItemStack is )
{
super( is );
this.getConfigManager().registerSetting( Settings.REDSTONE_CONTROLLED, RedstoneMode.IGNORE );
this.getConfigManager().registerSetting( Settings.FUZZY_MODE, FuzzyMode.IGNORE_ALL );
this.getConfigManager().registerSetting( Settings.CRAFT_ONLY, YesNo.NO );
this.getConfigManager().registerSetting( Settings.SCHEDULING_MODE, SchedulingMode.DEFAULT );
this.source = new MachineSource( this );
}
@Override
public TickingRequest getTickingRequest( IGridNode node )
{
return new TickingRequest( TickRates.FluidImportBus.getMin(), TickRates.FluidImportBus.getMax(), this.isSleeping(), false );
}
@Override
public TickRateModulation tickingRequest( IGridNode node, int ticksSinceLastCall )
{
return this.canDoBusWork() ? this.doBusWork() : TickRateModulation.IDLE;
}
@Override
protected TickRateModulation doBusWork()
{
if( !this.canDoBusWork() )
{
return TickRateModulation.IDLE;
}
final TileEntity te = this.getConnectedTE();
if( te != null && te.hasCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, this.getSide().getFacing() ) )
{
try
{
final IFluidHandler fh = te.getCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, this.getSide().getFacing() );
final IMEMonitor<IAEFluidStack> inv = this.getProxy().getStorage().getInventory( this.getChannel() );
if( fh != null )
{
FluidStack fluidStack = fh.drain( this.calculateAmountToSend(), false );
if( this.filterEnabled() && !this.isInFilter( fluidStack ) )
{
return TickRateModulation.SLOWER;
}
AEFluidStack aeFluidStack = AEFluidStack.fromFluidStack( fluidStack );
if( aeFluidStack != null )
{
IAEFluidStack notInserted = inv.injectItems( aeFluidStack, Actionable.MODULATE, this.source );
if( notInserted != null && notInserted.getStackSize() > 0 )
{
aeFluidStack.decStackSize( notInserted.getStackSize() );
}
fh.drain( aeFluidStack.getFluidStack(), true );
return TickRateModulation.FASTER;
}
}
}
catch( GridAccessException e )
{
e.printStackTrace();
}
}
return TickRateModulation.SLEEP;
}
@Override
protected boolean canDoBusWork()
{
return this.getProxy().isActive();
}
private boolean isInFilter( FluidStack fluid )
{
for( int i = 0; i < this.getConfig().getSlots(); i++ )
{
IAEItemStack stack = this.getConfig().getAEStackInSlot( i );
if( stack != null && stack.getDefinition() != null )
{
IFluidHandlerItem fh = stack.getDefinition().getCapability( CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null );
if( fh == null )
{
throw new NullPointerException( "IFluidHandlerItem is null" );
}
FluidStack filtered = fh.drain( Integer.MAX_VALUE, false );
if( filtered != null && filtered.isFluidEqual( fluid ) )
{
return true;
}
}
}
return false;
}
private boolean filterEnabled()
{
for( int i = 0; i < this.getConfig().getSlots(); i++ )
{
IAEItemStack stack = this.getConfig().getAEStackInSlot( i );
if( stack != null )
{
return true;
}
}
return false;
}
@Override
public RedstoneMode getRSMode()
{
return (RedstoneMode) this.getConfigManager().getSetting( Settings.REDSTONE_CONTROLLED );
}
@Nonnull
@Override
public IPartModel getStaticModels()
{
if( this.isActive() && this.isPowered() )
{
return MODELS_HAS_CHANNEL;
}
else if( this.isPowered() )
{
return MODELS_ON;
}
else
{
return MODELS_OFF;
}
}
}
@@ -0,0 +1,509 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, 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.fluids.parts;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nonnull;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
import net.minecraftforge.items.IItemHandler;
import appeng.api.AEApi;
import appeng.api.config.AccessRestriction;
import appeng.api.config.FuzzyMode;
import appeng.api.config.IncludeExclude;
import appeng.api.config.Settings;
import appeng.api.config.StorageFilter;
import appeng.api.config.Upgrades;
import appeng.api.networking.IGridNode;
import appeng.api.networking.events.MENetworkCellArrayUpdate;
import appeng.api.networking.security.IActionSource;
import appeng.api.networking.storage.IBaseMonitor;
import appeng.api.networking.ticking.ITickManager;
import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.networking.ticking.TickingRequest;
import appeng.api.parts.IPartHost;
import appeng.api.parts.IPartModel;
import appeng.api.storage.IMEInventory;
import appeng.api.storage.IMEInventoryHandler;
import appeng.api.storage.IMEMonitorHandlerReceiver;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.IStorageMonitorable;
import appeng.api.storage.IStorageMonitorableAccessor;
import appeng.api.storage.channels.IFluidStorageChannel;
import appeng.api.storage.data.IAEFluidStack;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IItemList;
import appeng.api.util.AEPartLocation;
import appeng.capabilities.Capabilities;
import appeng.core.AppEng;
import appeng.core.settings.TickRates;
import appeng.core.sync.GuiBridge;
import appeng.helpers.IInterfaceHost;
import appeng.items.parts.PartModels;
import appeng.me.GridAccessException;
import appeng.me.helpers.MachineSource;
import appeng.me.storage.ITickingMonitor;
import appeng.me.storage.MEInventoryHandler;
import appeng.parts.PartModel;
import appeng.parts.misc.PartSharedStorageBus;
import appeng.tile.inventory.AppEngInternalAEInventory;
import appeng.util.Platform;
import appeng.util.inv.InvOperation;
import appeng.util.item.AEFluidStack;
import appeng.util.prioritylist.FuzzyPriorityList;
import appeng.util.prioritylist.PrecisePriorityList;
/**
* @author BrockWS
* @version rv6 - 22/05/2018
* @since rv6 22/05/2018
*/
public class PartFluidStorageBus extends PartSharedStorageBus implements IMEMonitorHandlerReceiver<IAEFluidStack>
{
public static final ResourceLocation MODEL_BASE = new ResourceLocation( AppEng.MOD_ID, "part/fluid_storage_bus_base" );
@PartModels
public static final IPartModel MODELS_OFF = new PartModel( MODEL_BASE, new ResourceLocation( AppEng.MOD_ID, "part/fluid_storage_bus_off" ) );
@PartModels
public static final IPartModel MODELS_ON = new PartModel( MODEL_BASE, new ResourceLocation( AppEng.MOD_ID, "part/fluid_storage_bus_on" ) );
@PartModels
public static final IPartModel MODELS_HAS_CHANNEL = new PartModel( MODEL_BASE, new ResourceLocation( AppEng.MOD_ID, "part/fluid_storage_bus_has_channel" ) );
private final IActionSource source;
private final AppEngInternalAEInventory config = new AppEngInternalAEInventory( this, 63 );
private boolean cached = false;
private ITickingMonitor monitor = null;
private MEInventoryHandler<IAEFluidStack> handler = null;
private int handlerHash = 0;
private byte resetCacheLogic = 0;
public PartFluidStorageBus( ItemStack is )
{
super( is );
this.getConfigManager().registerSetting( Settings.ACCESS, AccessRestriction.READ_WRITE );
this.getConfigManager().registerSetting( Settings.FUZZY_MODE, FuzzyMode.IGNORE_ALL );
this.getConfigManager().registerSetting( Settings.STORAGE_FILTER, StorageFilter.EXTRACTABLE_ONLY );
this.source = new MachineSource( this );
}
private IMEInventory<IAEFluidStack> getInventoryWrapper( TileEntity target )
{
EnumFacing targetSide = this.getSide().getFacing().getOpposite();
// Prioritize a handler to directly link to another ME network
IStorageMonitorableAccessor accessor = target.getCapability( Capabilities.STORAGE_MONITORABLE_ACCESSOR, targetSide );
if( accessor != null )
{
IStorageMonitorable inventory = accessor.getInventory( this.source );
if( inventory != null )
{
return inventory.getInventory( AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class ) );
}
// So this could / can be a design decision. If the tile does support our custom capability,
// but it does not return an inventory for the action source, we do NOT fall back to using
// IItemHandler's, as that might circumvent the security setings, and might also cause
// performance issues.
return null;
}
// Check via cap for IItemHandler
IFluidHandler handlerExt = target.getCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, targetSide );
if( handlerExt != null )
{
return new FluidHandlerAdapter( handlerExt, this );
}
return null;
}
@Override
public TickRateModulation tickingRequest( IGridNode node, int ticksSinceLastCall )
{
if( this.resetCacheLogic != 0 )
{
this.resetCache();
}
if( this.monitor != null )
{
return this.monitor.onTick();
}
return TickRateModulation.SLEEP;
}
protected void resetCache()
{
final boolean fullReset = this.resetCacheLogic == 2;
this.resetCacheLogic = 0;
final IMEInventory<IAEFluidStack> in = this.getInternalHandler();
IItemList<IAEFluidStack> before = AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class ).createList();
if( in != null )
{
before = in.getAvailableItems( before );
}
this.cached = false;
if( fullReset )
{
this.handlerHash = 0;
}
final IMEInventory<IAEFluidStack> out = this.getInternalHandler();
if( in != out )
{
IItemList<IAEFluidStack> after = AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class ).createList();
if( out != null )
{
after = out.getAvailableItems( after );
}
Platform.postListChanges( before, after, this, this.source );
}
}
protected void resetCache( final boolean fullReset )
{
if( this.getHost() == null || this.getHost().getTile() == null || this.getHost().getTile().getWorld() == null || this.getHost().getTile().getWorld().isRemote )
{
return;
}
if( fullReset )
{
this.resetCacheLogic = 2;
}
else
{
this.resetCacheLogic = 1;
}
try
{
this.getProxy().getTick().alertDevice( this.getProxy().getNode() );
}
catch( final GridAccessException e )
{
// :P
}
}
@Override
public boolean onPartActivate( final EntityPlayer player, final EnumHand hand, final Vec3d pos )
{
if( !player.isSneaking() )
{
if( Platform.isClient() )
{
return true;
}
Platform.openGUI( player, this.getHost().getTile(), this.getSide(), GuiBridge.GUI_STORAGEBUS_FLUID );
return true;
}
return false;
}
@Override
public void onChangeInventory( final IItemHandler inv, final int slot, final InvOperation mc, final ItemStack removedStack, final ItemStack newStack )
{
super.onChangeInventory( inv, slot, mc, removedStack, newStack );
if( inv == this.config )
{
this.resetCache( true );
}
}
@Override
public IItemHandler getInventoryByName( final String name )
{
if( name.equals( "config" ) )
{
return this.config;
}
return super.getInventoryByName( name );
}
@Override
public void readFromNBT( final NBTTagCompound data )
{
super.readFromNBT( data );
this.config.readFromNBT( data, "config" );
}
@Override
public void writeToNBT( final NBTTagCompound data )
{
super.writeToNBT( data );
this.config.writeToNBT( data, "config" );
}
@Override
public boolean isValid( final Object verificationToken )
{
return this.handler == verificationToken;
}
@Override
public void postChange( final IBaseMonitor<IAEFluidStack> monitor, final Iterable<IAEFluidStack> change, final IActionSource source )
{
try
{
if( this.getProxy().isActive() )
{
this.getProxy().getStorage().postAlterationOfStoredItems( AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class ), change, this.source );
}
}
catch( final GridAccessException e )
{
// :(
}
}
public MEInventoryHandler<IAEFluidStack> getInternalHandler()
{
if( this.cached )
{
return this.handler;
}
final boolean wasSleeping = this.monitor == null;
this.cached = true;
final TileEntity self = this.getHost().getTile();
final TileEntity target = self.getWorld().getTileEntity( self.getPos().offset( this.getSide().getFacing() ) );
final int newHandlerHash = this.createHandlerHash( target );
if( newHandlerHash != 0 && newHandlerHash == this.handlerHash )
{
return this.handler;
}
this.handlerHash = newHandlerHash;
this.handler = null;
this.monitor = null;
if( target != null )
{
IMEInventory<IAEFluidStack> inv = this.getInventoryWrapper( target );
if( inv instanceof ITickingMonitor )
{
this.monitor = (ITickingMonitor) inv;
this.monitor.setActionSource( new MachineSource( this ) );
}
if( inv != null )
{
this.checkInterfaceVsStorageBus( target, this.getSide().getOpposite() );
this.handler = new MEInventoryHandler<>( inv, AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class ) );
this.handler.setBaseAccess( (AccessRestriction) this.getConfigManager().getSetting( Settings.ACCESS ) );
this.handler.setWhitelist( this.getInstalledUpgrades( Upgrades.INVERTER ) > 0 ? IncludeExclude.BLACKLIST : IncludeExclude.WHITELIST );
this.handler.setPriority( this.getPriority() );
final IItemList<IAEFluidStack> priorityList = AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class ).createList();
final int slotsToUse = 18 + this.getInstalledUpgrades( Upgrades.CAPACITY ) * 9;
for( int x = 0; x < this.config.getSlots() && x < slotsToUse; x++ )
{
final IAEItemStack is = this.config.getAEStackInSlot( x );
if( is != null )
{
// Because we store filtered fluid as buckets, we need to grab the fluid from the stack
IFluidHandlerItem fh = FluidUtil.getFluidHandler( is.createItemStack() );
if( fh == null )
{
continue;
}
FluidStack fluid = fh.drain( Integer.MAX_VALUE, false );
priorityList.add( AEFluidStack.fromFluidStack( fluid ) );
}
}
if( this.getInstalledUpgrades( Upgrades.FUZZY ) > 0 )
{
this.handler.setPartitionList( new FuzzyPriorityList<IAEFluidStack>( priorityList, (FuzzyMode) this.getConfigManager().getSetting( Settings.FUZZY_MODE ) ) );
}
else
{
this.handler.setPartitionList( new PrecisePriorityList<IAEFluidStack>( priorityList ) );
}
if( inv instanceof IBaseMonitor )
{
( (IBaseMonitor<IAEFluidStack>) inv ).addListener( this, this.handler );
}
}
}
// update sleep state...
if( wasSleeping != ( this.monitor == null ) )
{
try
{
final ITickManager tm = this.getProxy().getTick();
if( this.monitor == null )
{
tm.sleepDevice( this.getProxy().getNode() );
}
else
{
tm.wakeDevice( this.getProxy().getNode() );
}
}
catch( final GridAccessException ignore )
{
// :(
}
}
try
{
// force grid to update handlers...
this.getProxy().getGrid().postEvent( new MENetworkCellArrayUpdate() );
}
catch( final GridAccessException ignore )
{
// :3
}
return this.handler;
}
private void checkInterfaceVsStorageBus( final TileEntity target, final AEPartLocation side )
{
IInterfaceHost achievement = null;
if( target instanceof IInterfaceHost )
{
achievement = (IInterfaceHost) target;
}
if( target instanceof IPartHost )
{
final Object part = ( (IPartHost) target ).getPart( side );
if( part instanceof IInterfaceHost )
{
achievement = (IInterfaceHost) part;
}
}
if( achievement != null && achievement.getActionableNode() != null )
{
// Platform.addStat( achievement.getActionableNode().getPlayerID(), Achievements.Recursive.getAchievement()
// );
// Platform.addStat( getActionableNode().getPlayerID(), Achievements.Recursive.getAchievement() );
}
}
@Override
public List<IMEInventoryHandler> getCellArray( final IStorageChannel channel )
{
if( channel == this.getStorageChannel() )
{
final IMEInventoryHandler<IAEFluidStack> out = this.getProxy().isActive() ? this.getInternalHandler() : null;
if( out != null )
{
return Collections.singletonList( out );
}
}
return super.getCellArray( channel );
}
private int createHandlerHash( TileEntity target )
{
if( target == null )
{
return 0;
}
final EnumFacing targetSide = this.getSide().getFacing().getOpposite();
if( target.hasCapability( Capabilities.STORAGE_MONITORABLE_ACCESSOR, targetSide ) )
{
return Objects.hash( target, target.getCapability( Capabilities.STORAGE_MONITORABLE_ACCESSOR, targetSide ) );
}
final IFluidHandler fluidHandler = target.getCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, targetSide );
if( fluidHandler != null )
{
return Objects.hash( target, fluidHandler, fluidHandler.getTankProperties().length );
}
return 0;
}
@Override
public TickingRequest getTickingRequest( IGridNode node )
{
return new TickingRequest( TickRates.FluidStorageBus.getMin(), TickRates.FluidStorageBus.getMax(), this.isSleeping(), true );
}
@Override
public void onListUpdate()
{
// not used here.
}
@Override
public IStorageChannel getStorageChannel()
{
return AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class );
}
@SuppressWarnings( "Duplicates" )
@Nonnull
@Override
public IPartModel getStaticModels()
{
if( this.isActive() && this.isPowered() )
{
return MODELS_HAS_CHANNEL;
}
else if( this.isPowered() )
{
return MODELS_ON;
}
else
{
return MODELS_OFF;
}
}
}
@@ -0,0 +1,209 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, 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.fluids.parts;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.items.IItemHandler;
import appeng.api.AEApi;
import appeng.api.config.RedstoneMode;
import appeng.api.config.Upgrades;
import appeng.api.networking.ticking.IGridTickable;
import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.parts.IPartCollisionHelper;
import appeng.api.storage.channels.IFluidStorageChannel;
import appeng.api.util.AECableType;
import appeng.core.sync.GuiBridge;
import appeng.me.GridAccessException;
import appeng.parts.automation.PartUpgradeable;
import appeng.tile.inventory.AppEngInternalAEInventory;
import appeng.util.Platform;
/**
* @author BrockWS
* @version rv6 - 30/04/2018
* @since rv6 30/04/2018
*/
public abstract class PartSharedFluidBus extends PartUpgradeable implements IGridTickable
{
private final AppEngInternalAEInventory config = new AppEngInternalAEInventory( this, 9 );
private boolean lastRedstone;
public PartSharedFluidBus( ItemStack is )
{
super( is );
}
@Override
public void upgradesChanged()
{
this.updateState();
}
@Override
public void onNeighborChanged( IBlockAccess w, BlockPos pos, BlockPos neighbor )
{
this.updateState();
if( this.lastRedstone != this.getHost().hasRedstone( this.getSide() ) )
{
this.lastRedstone = !this.lastRedstone;
if( this.lastRedstone && this.getRSMode() == RedstoneMode.SIGNAL_PULSE )
{
this.doBusWork();
}
}
}
private void updateState()
{
try
{
if( !this.isSleeping() )
{
this.getProxy().getTick().wakeDevice( this.getProxy().getNode() );
}
else
{
this.getProxy().getTick().sleepDevice( this.getProxy().getNode() );
}
}
catch( final GridAccessException e )
{
// :P
}
}
@Override
public boolean onPartActivate( final EntityPlayer player, final EnumHand hand, final Vec3d pos )
{
if( !player.isSneaking() )
{
if( Platform.isClient() )
{
return true;
}
Platform.openGUI( player, this.getHost().getTile(), this.getSide(), GuiBridge.GUI_BUS_FLUID );
return true;
}
return false;
}
@Override
public void getBoxes( IPartCollisionHelper bch )
{
bch.addBox( 6, 6, 11, 10, 10, 13 );
bch.addBox( 5, 5, 13, 11, 11, 14 );
bch.addBox( 4, 4, 14, 12, 12, 16 );
}
protected TileEntity getConnectedTE()
{
TileEntity self = this.getHost().getTile();
return this.getTileEntity( self, self.getPos().offset( this.getSide().getFacing() ) );
}
private TileEntity getTileEntity( final TileEntity self, final BlockPos pos )
{
final World w = self.getWorld();
if( w.getChunkProvider().getLoadedChunk( pos.getX() >> 4, pos.getZ() >> 4 ) != null )
{
return w.getTileEntity( pos );
}
return null;
}
@Override
public IItemHandler getInventoryByName( final String name )
{
if( name.equals( "config" ) )
{
return this.getConfig();
}
return super.getInventoryByName( name );
}
protected int calculateAmountToSend()
{
double amount = this.getChannel().transferFactor();
switch( this.getInstalledUpgrades( Upgrades.SPEED ) )
{
case 4:
amount = amount * 1.5;
case 3:
amount = amount * 2;
case 2:
amount = amount * 4;
case 1:
amount = amount * 8;
case 0:
default:
return MathHelper.floor( amount );
}
}
@Override
public void readFromNBT( NBTTagCompound extra )
{
super.readFromNBT( extra );
this.getConfig().readFromNBT( extra, "config" );
}
@Override
public void writeToNBT( NBTTagCompound extra )
{
super.writeToNBT( extra );
this.getConfig().writeToNBT( extra, "config" );
}
public AppEngInternalAEInventory getConfig()
{
return this.config;
}
protected IFluidStorageChannel getChannel(){
return AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class );
}
@Override
public float getCableConnectionLength( AECableType cable )
{
return 5;
}
protected abstract TickRateModulation doBusWork();
protected abstract boolean canDoBusWork();
}
@@ -0,0 +1,87 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, 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.fluids.registries;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import appeng.api.AEApi;
import appeng.api.implementations.tiles.IChestOrDrive;
import appeng.api.storage.ICellHandler;
import appeng.api.storage.ICellInventory;
import appeng.api.storage.ICellInventoryHandler;
import appeng.api.storage.IMEInventory;
import appeng.api.storage.IMEInventoryHandler;
import appeng.api.storage.ISaveProvider;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.channels.IFluidStorageChannel;
import appeng.api.storage.data.IAEStack;
import appeng.fluids.storage.FluidCellInventory;
import appeng.fluids.storage.FluidCellInventoryHandler;
public class BasicFluidCellHandler implements ICellHandler
{
@Override
public boolean isCell( final ItemStack is )
{
return FluidCellInventory.isCell( is );
}
@Override
public <T extends IAEStack<T>> IMEInventoryHandler<T> getCellInventory( final ItemStack is, final ISaveProvider container, final IStorageChannel<T> channel )
{
if( channel == AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class ) )
{
return FluidCellInventory.getCell( is, container );
}
return null;
}
@Override
public void openChestGui( final EntityPlayer player, final IChestOrDrive chest, final ICellHandler cellHandler, final IMEInventoryHandler inv, final ItemStack is, final IStorageChannel chan )
{
if( chan == AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class ) )
{
//TODO: Open Gui
//Platform.openGUI( player, (TileEntity) chest, AEPartLocation.fromFacing( chest.getUp() ), GuiBridge.GUI_ME );
}
}
@Override
public int getStatusForCell( final ItemStack is, final IMEInventory handler )
{
if( handler instanceof FluidCellInventoryHandler )
{
final FluidCellInventoryHandler ci = (FluidCellInventoryHandler) handler;
return ci.getStatusForCell();
}
return 0;
}
@Override
public double cellIdleDrain( final ItemStack is, final IMEInventory handler )
{
final ICellInventory inv = ( (ICellInventoryHandler) handler ).getCellInv();
return inv.getIdleDrain();
}
}
@@ -0,0 +1,270 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, 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.fluids.storage;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.fluids.FluidStack;
import appeng.api.AEApi;
import appeng.api.config.Actionable;
import appeng.api.exceptions.AppEngException;
import appeng.api.implementations.items.IStorageCell;
import appeng.api.networking.security.IActionSource;
import appeng.api.storage.IMEInventoryHandler;
import appeng.api.storage.ISaveProvider;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.channels.IFluidStorageChannel;
import appeng.api.storage.data.IAEFluidStack;
import appeng.core.AEConfig;
import appeng.core.AELog;
import appeng.me.storage.AbstractCellInventory;
import appeng.util.item.AEFluidStack;
/**
* @author DrummerMC
* @version rv6 - 2018-01-16
* @since rv6 2018-01-16
*/
public class FluidCellInventory extends AbstractCellInventory<IAEFluidStack>
{
protected FluidCellInventory( final NBTTagCompound data, final ISaveProvider container )
{
super( data, container, 8000 );
}
private FluidCellInventory( final ItemStack o, final ISaveProvider container ) throws AppEngException
{
super( o, container, 8000 );
}
public static IMEInventoryHandler getCell( final ItemStack o, final ISaveProvider container2 )
{
try
{
return new FluidCellInventoryHandler( new FluidCellInventory( o, container2 ) );
}
catch( final AppEngException e )
{
return null;
}
}
public static boolean isCell( final ItemStack i )
{
if( i == null )
{
return false;
}
final Item type = i.getItem();
if( type instanceof IStorageCell )
{
if( ( (IStorageCell) type ).getChannel() == AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class ) )
{
return ( (IStorageCell) type ).isStorageCell( i );
}
}
return false;
}
@Override
public IAEFluidStack injectItems( final IAEFluidStack input, final Actionable mode, final IActionSource src )
{
if( input == null )
{
return null;
}
if( input.getStackSize() == 0 )
{
return null;
}
if( this.cellType.isBlackListed( this.i, input ) )
{
return input;
}
final FluidStack sharedFluidStack = input.getFluidStack();
final IAEFluidStack l = this.getCellItems().findPrecise( input );
if( l != null )
{
final long remainingItemSlots = this.getRemainingItemCount();
if( remainingItemSlots < 0 )
{
return input;
}
if( input.getStackSize() > remainingItemSlots )
{
final IAEFluidStack r = input.copy();
r.setStackSize( r.getStackSize() - remainingItemSlots );
if( mode == Actionable.MODULATE )
{
l.setStackSize( l.getStackSize() + remainingItemSlots );
this.updateItemCount( remainingItemSlots );
this.saveChanges();
}
return r;
}
else
{
if( mode == Actionable.MODULATE )
{
l.setStackSize( l.getStackSize() + input.getStackSize() );
this.updateItemCount( input.getStackSize() );
this.saveChanges();
}
return null;
}
}
if( this.canHoldNewItem() ) // room for new type, and for at least one item!
{
final int remainingItemCount = (int) this.getRemainingItemCount() - this.getBytesPerType() * itemsPerByte;
if( remainingItemCount > 0 )
{
if( input.getStackSize() > remainingItemCount )
{
final FluidStack toReturn = sharedFluidStack.copy();
toReturn.amount = sharedFluidStack.amount - remainingItemCount;
if( mode == Actionable.MODULATE )
{
final FluidStack toWrite = sharedFluidStack.copy();
toWrite.amount = remainingItemCount;
this.cellItems.add( AEFluidStack.fromFluidStack( toWrite ) );
this.updateItemCount( toWrite.amount );
this.saveChanges();
}
return AEFluidStack.fromFluidStack( toReturn );
}
if( mode == Actionable.MODULATE )
{
this.updateItemCount( input.getStackSize() );
this.cellItems.add( input );
this.saveChanges();
}
return null;
}
}
return input;
}
@Override
public IAEFluidStack extractItems( final IAEFluidStack request, final Actionable mode, final IActionSource src )
{
if( request == null )
{
return null;
}
final long size = Math.min( Integer.MAX_VALUE, request.getStackSize() );
IAEFluidStack results = null;
final IAEFluidStack l = this.getCellItems().findPrecise( request );
if( l != null )
{
results = l.copy();
if( l.getStackSize() <= size )
{
results.setStackSize( l.getStackSize() );
if( mode == Actionable.MODULATE )
{
this.updateItemCount( -l.getStackSize() );
l.setStackSize( 0 );
this.saveChanges();
}
}
else
{
results.setStackSize( size );
if( mode == Actionable.MODULATE )
{
l.setStackSize( l.getStackSize() - size );
this.updateItemCount( -size );
this.saveChanges();
}
}
}
return results;
}
@Override
public IStorageChannel getChannel()
{
return AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class );
}
protected void loadCellItem( NBTTagCompound compoundTag, int stackSize )
{
// Now load the fluid stack
final FluidStack t;
try
{
t = FluidStack.loadFluidStackFromNBT( compoundTag );
if( t == null )
{
AELog.warn( "Removing item " + compoundTag + " from storage cell because the associated item type couldn't be found." );
return;
}
}
catch( Throwable ex )
{
if( AEConfig.instance().isRemoveCrashingItemsOnLoad() )
{
AELog.warn( ex, "Removing item " + compoundTag + " from storage cell because loading the ItemStack crashed." );
return;
}
throw ex;
}
t.amount = stackSize;
if( t.amount > 0 )
{
try
{
this.cellItems.add( AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class ).createStack( t ) );
}
catch( Throwable ex )
{
if( AEConfig.instance().isRemoveCrashingItemsOnLoad() )
{
AELog.warn( ex, "Removing item " + t + " from storage cell because processing the loaded item crashed." );
return;
}
throw ex;
}
}
}
}
@@ -0,0 +1,52 @@
/*
* 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.fluids.storage;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.FluidStack;
import appeng.api.AEApi;
import appeng.api.storage.IMEInventory;
import appeng.api.storage.channels.IFluidStorageChannel;
import appeng.api.storage.data.IAEFluidStack;
import appeng.fluids.items.FluidDummyItem;
import appeng.me.storage.AbstractCellInventoryHandler;
import appeng.util.item.AEFluidStack;
public class FluidCellInventoryHandler extends AbstractCellInventoryHandler<IAEFluidStack>
{
public FluidCellInventoryHandler( IMEInventory c )
{
super( c, AEApi.instance().storage().getStorageChannel( IFluidStorageChannel.class ) );
}
@Override
protected IAEFluidStack createConfigStackFromItem( ItemStack is )
{
if( is.getItem() instanceof FluidDummyItem )
{
FluidStack fs = ( (FluidDummyItem) is.getItem() ).getFluidStack( is );
return fs != null ? AEFluidStack.fromFluidStack( fs ) : null;
}
return null;
}
}