diff --git a/build.gradle b/build.gradle index 66ada5aca..2dc7ef269 100644 --- a/build.gradle +++ b/build.gradle @@ -57,7 +57,7 @@ tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } -version = aeversion + "-" + aechannel + "-" + aebuild +version = aeversion + "-" + aechannel + "-" + aebuild + " " + trousers group = aegroup archivesBaseName = aebasename diff --git a/gradle.properties b/gradle.properties index 57e7aa133..19fe95814 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,6 +3,7 @@ aechannel=stable aebuild=7 aegroup=appeng aebasename=appliedenergistics2 +trousers=omni-fixes-v21b ######################################################### # Versions # diff --git a/gradle/scripts/dependencies.gradle b/gradle/scripts/dependencies.gradle index 5c442bb53..b4b2529dc 100644 --- a/gradle/scripts/dependencies.gradle +++ b/gradle/scripts/dependencies.gradle @@ -19,6 +19,11 @@ repositories { mavenLocal() + maven { + name = "storagedrawers" + url = "https://dl.bintray.com/jaquadro/dev/" + } + maven { name "Mobius" url "http://mobiusstrip.eu/maven" @@ -80,6 +85,8 @@ configurations { } dependencies { + deobfCompile "com.jaquadro.minecraft.storagedrawers:StorageDrawers:1.12.2-5.4.2:api" + // installable runtime dependencies mods "mcp.mobius.waila:Hwyla:${hwyla_version}" mods "net.industrial-craft:industrialcraft-2:${ic2_version}:dev" @@ -98,6 +105,8 @@ dependencies { // at runtime, use the full JEI jar runtime "mezz.jei:jei_${minecraft_version}:${jei_version}" + runtime "com.jaquadro.minecraft.storagedrawers:StorageDrawers:1.12.2-5.4.2" + runtime "com.jaquadro.minecraft.chameleon:Chameleon:1.12-4.1.3" // unit test dependencies testCompile "junit:junit:4.12" diff --git a/src/main/java/appeng/capabilities/Capabilities.java b/src/main/java/appeng/capabilities/Capabilities.java index e0786555a..c4889e36a 100644 --- a/src/main/java/appeng/capabilities/Capabilities.java +++ b/src/main/java/appeng/capabilities/Capabilities.java @@ -19,6 +19,7 @@ package appeng.capabilities; +import com.jaquadro.minecraft.storagedrawers.api.capabilities.IItemRepository; import net.darkhax.tesla.api.ITeslaConsumer; import net.darkhax.tesla.api.ITeslaHolder; import net.minecraft.nbt.NBTBase; @@ -54,6 +55,8 @@ public final class Capabilities public static Capability FORGE_ENERGY; + public static Capability ITEM_REPOSITORY_CAPABILITY; + /** * Register AE2 provided capabilities. */ @@ -99,6 +102,9 @@ public final class Capabilities FORGE_ENERGY = cap; } + @CapabilityInject( IItemRepository.class ) + private static void capIItemRepositoryRegistered( Capability cap ) { ITEM_REPOSITORY_CAPABILITY = cap; } + // Create a storage implementation that does not do anything private static Capability.IStorage createNullStorage() { diff --git a/src/main/java/appeng/parts/misc/ItemRepositoryAdapter.java b/src/main/java/appeng/parts/misc/ItemRepositoryAdapter.java new file mode 100644 index 000000000..d0f4b101a --- /dev/null +++ b/src/main/java/appeng/parts/misc/ItemRepositoryAdapter.java @@ -0,0 +1,329 @@ +package appeng.parts.misc; + +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 java.util.function.Predicate; +import java.util.stream.Collectors; + +import appeng.api.storage.IStorageChannel; + +import appeng.core.AELog; +import com.jaquadro.minecraft.storagedrawers.api.capabilities.IItemRepository; +import net.minecraft.item.ItemStack; +import net.minecraft.util.NonNullList; + +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.channels.IItemStorageChannel; +import appeng.api.storage.data.IAEItemStack; +import appeng.api.storage.data.IItemList; +import appeng.me.GridAccessException; +import appeng.me.helpers.IGridProxyable; +import appeng.me.storage.ITickingMonitor; +import appeng.util.item.AEItemStack; + +import javax.annotation.Nonnull; + +/** + * Wraps an Item Repository in such a way that it can be used as an IMEInventory for items. + * Used by the Storage Bus + */ + +class ItemRepositoryAdapter implements IMEInventory, IBaseMonitor, ITickingMonitor, IItemRepository +{ + private final Map, Object> listeners = new HashMap<>(); + private IActionSource mySource; + private final IItemRepository itemRepository; + private final IGridProxyable proxyable; + private final InventoryCache cache; + + + ItemRepositoryAdapter( IItemRepository itemRepository, IGridProxyable proxy ) + { + this.itemRepository = itemRepository; + this.proxyable = proxy; + this.cache = new InventoryCache( this.itemRepository ); + } + + @Override + public IAEItemStack injectItems( IAEItemStack iox, Actionable type, IActionSource src ) + { + ItemStack orgInput = iox.createItemStack(); + ItemStack remaining = orgInput; + + boolean simulate = ( type == Actionable.SIMULATE ); + + remaining = this.itemRepository.insertItem( remaining, simulate ); + + // At this point, we still have some items left... + if( remaining == orgInput ) + { + // The stack remained unmodified, target inventory is full + return iox; + } + + if( type == Actionable.MODULATE ) + { + try + { + this.proxyable.getProxy().getTick().alertDevice( this.proxyable.getProxy().getNode() ); + } + catch( GridAccessException ex ) + { + // meh + } + } + + return AEItemStack.fromItemStack( remaining ); + + } + + @Override + public IAEItemStack extractItems( IAEItemStack request, Actionable mode, IActionSource src ) + { + ItemStack requestedItemStack = request.createItemStack(); + int remainingSize = requestedItemStack.getCount(); + + final boolean simulate = ( mode == Actionable.SIMULATE ); + + ItemStack extracted; + extracted = this.itemRepository.extractItem( requestedItemStack, remainingSize, simulate ); + + if( extracted.getCount() > remainingSize ) + { + // Something broke. It should never return more than we requested... + // We're going to silently eat the remainder + AELog.warn( "Mod that provided item handler %s is broken. Returned %s items while only requesting %d.", + this.itemRepository.getClass().getName(), extracted.toString(), remainingSize ); + extracted.setCount( remainingSize ); + } + + if( !extracted.isEmpty() ) + { + if( mode == Actionable.MODULATE ) + { + try + { + this.proxyable.getProxy().getTick().alertDevice( this.proxyable.getProxy().getNode() ); + } + catch( GridAccessException ex ) + { + // meh + } + } + + return AEItemStack.fromItemStack( extracted ); + } + + return null; + } + + @Override + public IItemList getAvailableItems( IItemList out ) + { + return this.cache.getAvailableItems( out ); + } + + @Override + public IStorageChannel getChannel() + { + return AEApi.instance().storage().getStorageChannel( IItemStorageChannel.class ); + } + + @Override + public void addListener( IMEMonitorHandlerReceiver l, Object verificationToken ) + { + this.listeners.put( l, verificationToken ); + } + + @Override + public void removeListener( IMEMonitorHandlerReceiver l ) + { + this.listeners.remove( l ); + } + + private void postDifference( Iterable a ) + { + final Iterator, Object>> i = this.listeners.entrySet().iterator(); + while( i.hasNext() ) + { + final Map.Entry, Object> l = i.next(); + final IMEMonitorHandlerReceiver key = l.getKey(); + if( key.isValid( l.getValue() ) ) + { + key.postChange( this, a, this.mySource ); + } + else + { + i.remove(); + } + } + } + + @Override + public TickRateModulation onTick() + { + { + List changes = this.cache.update(); + if( !changes.isEmpty() ) + { + this.postDifference( changes ); + return TickRateModulation.URGENT; + } + else + { + return TickRateModulation.SLOWER; + } + } + } + + @Override + public void setActionSource( final IActionSource mySource ) + { + this.mySource = mySource; + } + + @Nonnull + @Override + public NonNullList getAllItems() + { + //not a provider + return null; + } + + @Nonnull + @Override + public ItemStack insertItem( @Nonnull ItemStack stack, boolean simulate, Predicate predicate ) + { + //not a provider + return null; + } + + @Nonnull + @Override + public ItemStack extractItem( @Nonnull ItemStack stack, int amount, boolean simulate, Predicate predicate ) + { + //not a provider + return null; + } + + private static class InventoryCache + { + private IAEItemStack[] cachedAeStacks = new IAEItemStack[0]; + private final IItemRepository iItemRepository; + + public InventoryCache( IItemRepository iItemRepository ) + { + this.iItemRepository = iItemRepository; + } + + public IItemList getAvailableItems( IItemList out ) + { + Arrays.stream( this.cachedAeStacks ).forEach( out::add ); + return out; + } + + public List update() + { + final List changes = new ArrayList<>(); + + List out = this.iItemRepository.getAllItems().stream().map( s -> AEItemStack.fromItemStack( s.itemPrototype ).setStackSize( s.count ) ).collect(Collectors.toList() ); + + final int size = out.size(); + + // Make room for new slots + if( size > this.cachedAeStacks.length ) + { + this.cachedAeStacks = Arrays.copyOf( this.cachedAeStacks, size ); + } + + for( int x = 0; x < size; x++ ) + { + // Save the old stuff + final IAEItemStack oldAeIS = this.cachedAeStacks[x]; + final IAEItemStack newIS = out.get( x ); + + this.handlePossibleSlotChanges( x, oldAeIS, newIS, changes ); + } + + // Handle cases where the number of slots actually is lower now than before + if( size < this.cachedAeStacks.length ) + { + for( int x = 0; x < this.cachedAeStacks.length; x++ ) + { + final IAEItemStack aeStack = this.cachedAeStacks[x]; + + if( aeStack != null ) + { + final IAEItemStack a = aeStack.copy(); + a.setStackSize( -a.getStackSize() ); + changes.add( a ); + } + } + + // Reduce the cache size + this.cachedAeStacks = Arrays.copyOf( this.cachedAeStacks, size ); + } + + return changes; + } + + private void handlePossibleSlotChanges( int slot, IAEItemStack oldAeIS, IAEItemStack newIS, List changes ) + { + if( oldAeIS != null && oldAeIS.isSameType( newIS ) ) + { + this.handleStackSizeChanged( slot, oldAeIS, newIS, changes ); + } + else + { + this.handleItemChanged( slot, oldAeIS, newIS, changes ); + } + } + + private void handleStackSizeChanged( int slot, IAEItemStack oldAeIS, IAEItemStack newIS, List changes ) + { + // Still the same item, but amount might have changed + final long diff = newIS.getStackSize() - oldAeIS.getStackSize(); + + if( diff != 0 ) + { + final IAEItemStack stack = oldAeIS.copy(); + stack.setStackSize( newIS.getStackSize() ); + + this.cachedAeStacks[slot] = stack; + + final IAEItemStack a = stack.copy(); + a.setStackSize( diff ); + changes.add( a ); + } + } + + private void handleItemChanged( int slot, IAEItemStack oldAeIS, IAEItemStack newIS, List changes ) + { + // Completely different item + this.cachedAeStacks[slot] = newIS ; + + // If we had a stack previously in this slot, notify the network about its disappearance + if( oldAeIS != null ) + { + oldAeIS.setStackSize( -oldAeIS.getStackSize() ); + changes.add( oldAeIS ); + } + + // Notify the network about the new stack. Note that this is null if newIS was null + if( this.cachedAeStacks[slot] != null ) + { + changes.add( this.cachedAeStacks[slot] ); + } + } + } +} diff --git a/src/main/java/appeng/parts/misc/PartStorageBus.java b/src/main/java/appeng/parts/misc/PartStorageBus.java index 9441a9dc8..75feaada1 100644 --- a/src/main/java/appeng/parts/misc/PartStorageBus.java +++ b/src/main/java/appeng/parts/misc/PartStorageBus.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import com.jaquadro.minecraft.storagedrawers.api.capabilities.IItemRepository; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; @@ -33,6 +34,8 @@ import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.world.IBlockAccess; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.CapabilityInject; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; @@ -95,6 +98,8 @@ import appeng.util.prioritylist.PrecisePriorityList; public class PartStorageBus extends PartUpgradeable implements IGridTickable, ICellContainer, IMEMonitorHandlerReceiver, IPriorityHost { + @CapabilityInject(IItemRepository.class) + public static Capability ITEM_REPOSITORY_CAPABILITY = null; public static final ResourceLocation MODEL_BASE = new ResourceLocation( AppEng.MOD_ID, "part/storage_bus_base" ); @@ -395,6 +400,12 @@ public class PartStorageBus extends PartUpgradeable implements IGridTickable, IC return null; } + // Check via cap for IItemRepository + IItemRepository handlerRepo = target.getCapability( ITEM_REPOSITORY_CAPABILITY, targetSide ); + if( handlerRepo != null ) + { + return new ItemRepositoryAdapter( handlerRepo, this ); + } // Check via cap for IItemHandler IItemHandler handlerExt = target.getCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, targetSide ); if( handlerExt != null ) @@ -420,6 +431,13 @@ public class PartStorageBus extends PartUpgradeable implements IGridTickable, IC return Objects.hash( target, target.getCapability( Capabilities.STORAGE_MONITORABLE_ACCESSOR, targetSide ) ); } + final IItemRepository handlerRepo = target.getCapability( ITEM_REPOSITORY_CAPABILITY, targetSide ); + + if( handlerRepo != null ) + { + return Objects.hash( target, handlerRepo, handlerRepo.getAllItems().size() ); + } + final IItemHandler itemHandler = target.getCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, targetSide ); if( itemHandler != null ) diff --git a/src/main/java/appeng/util/InventoryAdaptor.java b/src/main/java/appeng/util/InventoryAdaptor.java index bed48e3ef..9733abd0f 100644 --- a/src/main/java/appeng/util/InventoryAdaptor.java +++ b/src/main/java/appeng/util/InventoryAdaptor.java @@ -19,19 +19,18 @@ package appeng.util; +import appeng.util.inv.*; +import com.jaquadro.minecraft.storagedrawers.api.capabilities.IItemRepository; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.CapabilityInject; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import appeng.api.config.FuzzyMode; -import appeng.util.inv.AdaptorItemHandler; -import appeng.util.inv.AdaptorItemHandlerPlayerInv; -import appeng.util.inv.IInventoryDestination; -import appeng.util.inv.ItemSlot; - /** * Universal Facade for other inventories. Used to conveniently interact with various types of inventories. This is not @@ -41,15 +40,29 @@ import appeng.util.inv.ItemSlot; */ public abstract class InventoryAdaptor implements Iterable { + @CapabilityInject( IItemRepository.class) + public static Capability ITEM_REPOSITORY_CAPABILITY = null; + public static InventoryAdaptor getAdaptor( final TileEntity te, final EnumFacing d ) { - if( te != null && te.hasCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, d ) ) + if( te != null ) { - // Attempt getting an IItemHandler for the given side via caps - IItemHandler itemHandler = te.getCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, d ); - if( itemHandler != null ) + if( te.hasCapability( ITEM_REPOSITORY_CAPABILITY, d ) ) { - return new AdaptorItemHandler( itemHandler ); + IItemRepository itemRepository = te.getCapability( ITEM_REPOSITORY_CAPABILITY, d ); + if (itemRepository != null){ + return new AdaptorItemRepository( itemRepository ); + } + } + else if( te.hasCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, d ) ) + { + + // Attempt getting an IItemHandler for the given side via caps + IItemHandler itemHandler = te.getCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, d ); + if( itemHandler != null ) + { + return new AdaptorItemHandler( itemHandler ); + } } } return null; diff --git a/src/main/java/appeng/util/inv/AdaptorItemRepository.java b/src/main/java/appeng/util/inv/AdaptorItemRepository.java new file mode 100644 index 000000000..19badffae --- /dev/null +++ b/src/main/java/appeng/util/inv/AdaptorItemRepository.java @@ -0,0 +1,154 @@ +package appeng.util.inv; + +import appeng.api.config.FuzzyMode; +import appeng.util.InventoryAdaptor; + +import appeng.util.Platform; +import com.jaquadro.minecraft.storagedrawers.api.capabilities.IItemRepository; +import net.minecraft.item.ItemStack; + +import java.util.Iterator; + + +public class AdaptorItemRepository extends InventoryAdaptor +{ + protected final IItemRepository itemRepository; + + public AdaptorItemRepository( IItemRepository itemRepository ) + { + this.itemRepository = itemRepository; + } + + @Override + public ItemStack removeItems( int amount, ItemStack filter, IInventoryDestination destination ) + { + ItemStack rv = ItemStack.EMPTY; + + ItemStack extracted = this.itemRepository.extractItem( filter, amount, true ); + + if( destination != null ) + { + + if( extracted.isEmpty() || !destination.canInsert( extracted ) ) + { + return rv; + } + + } + + extracted = this.itemRepository.extractItem( filter, amount, false ); + return extracted; + } + + @Override + public ItemStack simulateRemove( int amount, ItemStack filter, IInventoryDestination destination ) + { + ItemStack rv = ItemStack.EMPTY; + + ItemStack extracted = this.itemRepository.extractItem( filter, amount, true ); + + if( destination != null ) + { + if( extracted.isEmpty() || !destination.canInsert( extracted ) ) + { + return rv; + } + } + + return extracted; + } + + @Override + public ItemStack removeSimilarItems( int amount, ItemStack filter, FuzzyMode fuzzyMode, IInventoryDestination destination ) + { + ItemStack extracted = ItemStack.EMPTY; + + ItemStack simulated = this.itemRepository.extractItem( filter, amount, true ); + + if (simulated.isEmpty() && !filter.isEmpty()) { + simulated = this.itemRepository.extractItem( filter, amount, true, s -> Platform.itemComparisons().isFuzzyEqualItem( s, filter, fuzzyMode ) ); + } + + if( destination != null ) + { + if( simulated.isEmpty() || !destination.canInsert( simulated ) ) + { + return extracted; + } + } + + extracted = this.itemRepository.extractItem( simulated, amount, false ); + return extracted; + } + + @Override + public ItemStack simulateSimilarRemove( int amount, ItemStack filter, FuzzyMode fuzzyMode, IInventoryDestination destination ) + { + ItemStack extracted = ItemStack.EMPTY; + + ItemStack simulated = this.itemRepository.extractItem( filter, amount, true ); + + if (simulated.isEmpty() && !filter.isEmpty()) { + simulated = this.itemRepository.extractItem( filter, amount, true, s -> Platform.itemComparisons().isFuzzyEqualItem( s, filter, fuzzyMode ) ); + } + + if( destination != null ) + { + if( simulated.isEmpty() || !destination.canInsert( simulated ) ) + { + return extracted; + } + } + + return simulated; + } + + @Override + public ItemStack addItems( ItemStack toBeAdded ) + { + return this.addItems( toBeAdded, false ); + } + + protected ItemStack addItems( final ItemStack itemsToAdd, final boolean simulate ) + { + if( itemsToAdd.isEmpty() ) + { + return ItemStack.EMPTY; + } + + ItemStack left = itemsToAdd.copy(); + + left = this.itemRepository.insertItem( left, simulate ); + + if( left.isEmpty() ) + { + return ItemStack.EMPTY; + } + + return left; + } + + @Override + public ItemStack simulateAdd( ItemStack toBeSimulated ) + { + return this.addItems( toBeSimulated, true ); + } + + @Override + public boolean containsItems() + { + return !this.itemRepository.getAllItems().isEmpty(); + } + + @Override + public boolean hasSlots() + { + return false; + } + + @Override + public Iterator iterator() + { + return null; + } +}