attempt #1 to backport 1.16 to 1.15

This commit is contained in:
Bohdan Shchepanskyy 2020-11-10 18:57:21 +01:00
parent 36f74ee44d
commit aa32faf302
93 changed files with 2568 additions and 604 deletions

1
.gitignore vendored
View file

@ -12,6 +12,7 @@
/classes
/bin/
/run/
/logs
*.classpath
*.project

2
Jenkinsfile vendored
View file

@ -22,7 +22,7 @@ pipeline {
stage('Publish') {
when {
branch 'master'
branch 'main'
}
steps {
sh './gradlew publish --no-daemon'

View file

@ -13,7 +13,7 @@ apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'eclipse'
apply plugin: 'maven-publish'
version = '1.5.5'
version = '1.8.2'
group = 'de.ellpeck.prettypipes' // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = 'PrettyPipes'
@ -23,7 +23,7 @@ if (System.getenv('BUILD_NUMBER') != null) {
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
minecraft {
mappings channel: 'snapshot', version: '20200128-1.15.1'
mappings channel: 'snapshot', version: '20200514-1.15.1'
runs {
client {
@ -87,6 +87,9 @@ repositories {
maven {
url = "https://dvs1.progwml6.com/files/maven"
}
maven {
url = "https://www.cursemaven.com"
}
}
configurations {
@ -95,11 +98,15 @@ configurations {
}
dependencies {
minecraft 'net.minecraftforge:forge:1.15.2-31.1.19'
minecraft 'net.minecraftforge:forge:1.15.2-31.2.45'
embed "org.jgrapht:jgrapht-core:1.4.0"
compileOnly fg.deobf("mezz.jei:jei-1.15.2:6.0.0.2:api")
runtimeOnly fg.deobf("mezz.jei:jei-1.15.2:6.0.0.2")
// to test the rf requiring stuff
runtimeOnly fg.deobf("curse.maven:powah:3057732")
runtimeOnly fg.deobf("curse.maven:lollipop:3057731")
}
// Example for how to get properties into the manifest for reading by the runtime..

0
gradlew vendored Normal file → Executable file
View file

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 KiB

View file

@ -4,17 +4,25 @@ import de.ellpeck.prettypipes.entities.PipeFrameEntity;
import de.ellpeck.prettypipes.entities.PipeFrameRenderer;
import de.ellpeck.prettypipes.items.*;
import de.ellpeck.prettypipes.misc.ItemEqualityType;
import de.ellpeck.prettypipes.pipe.modules.FilterModifierModule;
import de.ellpeck.prettypipes.pipe.modules.LowPriorityModuleItem;
import de.ellpeck.prettypipes.pipe.modules.RedstoneModuleItem;
import de.ellpeck.prettypipes.pipe.modules.SpeedModuleItem;
import de.ellpeck.prettypipes.network.PipeNetwork;
import de.ellpeck.prettypipes.packets.PacketHandler;
import de.ellpeck.prettypipes.pipe.IPipeConnectable;
import de.ellpeck.prettypipes.pipe.PipeBlock;
import de.ellpeck.prettypipes.pipe.PipeRenderer;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import de.ellpeck.prettypipes.pipe.containers.AbstractPipeContainer;
import de.ellpeck.prettypipes.pipe.containers.MainPipeContainer;
import de.ellpeck.prettypipes.pipe.containers.MainPipeGui;
import de.ellpeck.prettypipes.pipe.modules.*;
import de.ellpeck.prettypipes.pipe.modules.craft.CraftingModuleContainer;
import de.ellpeck.prettypipes.pipe.modules.craft.CraftingModuleGui;
import de.ellpeck.prettypipes.pipe.modules.craft.CraftingModuleItem;
import de.ellpeck.prettypipes.pipe.modules.extraction.ExtractionModuleContainer;
import de.ellpeck.prettypipes.pipe.modules.extraction.ExtractionModuleGui;
import de.ellpeck.prettypipes.pipe.modules.extraction.ExtractionModuleItem;
import de.ellpeck.prettypipes.network.PipeNetwork;
import de.ellpeck.prettypipes.packets.PacketHandler;
import de.ellpeck.prettypipes.pipe.*;
import de.ellpeck.prettypipes.pipe.containers.*;
import de.ellpeck.prettypipes.pipe.modules.filter.FilterIncreaseModuleContainer;
import de.ellpeck.prettypipes.pipe.modules.filter.FilterIncreaseModuleGui;
import de.ellpeck.prettypipes.pipe.modules.filter.FilterIncreaseModuleItem;
import de.ellpeck.prettypipes.pipe.modules.insertion.FilterModuleContainer;
import de.ellpeck.prettypipes.pipe.modules.insertion.FilterModuleGui;
import de.ellpeck.prettypipes.pipe.modules.insertion.FilterModuleItem;
@ -24,6 +32,10 @@ import de.ellpeck.prettypipes.pipe.modules.retrieval.RetrievalModuleItem;
import de.ellpeck.prettypipes.pipe.modules.stacksize.StackSizeModuleContainer;
import de.ellpeck.prettypipes.pipe.modules.stacksize.StackSizeModuleGui;
import de.ellpeck.prettypipes.pipe.modules.stacksize.StackSizeModuleItem;
import de.ellpeck.prettypipes.pressurizer.PressurizerBlock;
import de.ellpeck.prettypipes.pressurizer.PressurizerContainer;
import de.ellpeck.prettypipes.pressurizer.PressurizerGui;
import de.ellpeck.prettypipes.pressurizer.PressurizerTileEntity;
import de.ellpeck.prettypipes.terminal.CraftingTerminalBlock;
import de.ellpeck.prettypipes.terminal.CraftingTerminalTileEntity;
import de.ellpeck.prettypipes.terminal.ItemTerminalBlock;
@ -80,6 +92,8 @@ public final class Registry {
@CapabilityInject(PipeNetwork.class)
public static Capability<PipeNetwork> pipeNetworkCapability;
@CapabilityInject(IPipeConnectable.class)
public static Capability<IPipeConnectable> pipeConnectableCapability;
public static Item wrenchItem;
public static Item pipeFrameItem;
@ -98,17 +112,24 @@ public final class Registry {
public static EntityType<PipeFrameEntity> pipeFrameEntity;
public static Block pressurizerBlock;
public static TileEntityType<PressurizerTileEntity> pressurizerTileEntity;
public static ContainerType<PressurizerContainer> pressurizerContainer;
public static ContainerType<ExtractionModuleContainer> extractionModuleContainer;
public static ContainerType<FilterModuleContainer> filterModuleContainer;
public static ContainerType<RetrievalModuleContainer> retrievalModuleContainer;
public static ContainerType<StackSizeModuleContainer> stackSizeModuleContainer;
public static ContainerType<FilterIncreaseModuleContainer> filterIncreaseModuleContainer;
public static ContainerType<CraftingModuleContainer> craftingModuleContainer;
@SubscribeEvent
public static void registerBlocks(RegistryEvent.Register<Block> event) {
event.getRegistry().registerAll(
pipeBlock = new PipeBlock().setRegistryName("pipe"),
itemTerminalBlock = new ItemTerminalBlock().setRegistryName("item_terminal"),
craftingTerminalBlock = new CraftingTerminalBlock().setRegistryName("crafting_terminal")
craftingTerminalBlock = new CraftingTerminalBlock().setRegistryName("crafting_terminal"),
pressurizerBlock = new PressurizerBlock().setRegistryName("pressurizer")
);
}
@ -124,10 +145,13 @@ public final class Registry {
registry.registerAll(createTieredModule("filter_module", FilterModuleItem::new));
registry.registerAll(createTieredModule("speed_module", SpeedModuleItem::new));
registry.registerAll(createTieredModule("low_priority_module", LowPriorityModuleItem::new));
registry.registerAll(createTieredModule("high_priority_module", HighPriorityModuleItem::new));
registry.registerAll(createTieredModule("retrieval_module", RetrievalModuleItem::new));
registry.register(new StackSizeModuleItem("stack_size_module"));
registry.registerAll(Arrays.stream(ItemEqualityType.values()).map(t -> new FilterModifierModule(t.name().toLowerCase(Locale.ROOT) + "_filter_modifier", t)).toArray(Item[]::new));
registry.registerAll(Arrays.stream(ItemEqualityType.values()).map(t -> new FilterModifierModuleItem(t.name().toLowerCase(Locale.ROOT) + "_filter_modifier", t)).toArray(Item[]::new));
registry.register(new RedstoneModuleItem("redstone_module"));
registry.register(new FilterIncreaseModuleItem("filter_increase_modifier"));
registry.registerAll(createTieredModule("crafting_module", CraftingModuleItem::new));
ForgeRegistries.BLOCKS.getValues().stream()
.filter(b -> b.getRegistryName().getNamespace().equals(PrettyPipes.ID))
@ -139,7 +163,8 @@ public final class Registry {
event.getRegistry().registerAll(
pipeTileEntity = (TileEntityType<PipeTileEntity>) TileEntityType.Builder.create(PipeTileEntity::new, pipeBlock).build(null).setRegistryName("pipe"),
itemTerminalTileEntity = (TileEntityType<ItemTerminalTileEntity>) TileEntityType.Builder.create(ItemTerminalTileEntity::new, itemTerminalBlock).build(null).setRegistryName("item_terminal"),
craftingTerminalTileEntity = (TileEntityType<CraftingTerminalTileEntity>) TileEntityType.Builder.create(CraftingTerminalTileEntity::new, craftingTerminalBlock).build(null).setRegistryName("crafting_terminal")
craftingTerminalTileEntity = (TileEntityType<CraftingTerminalTileEntity>) TileEntityType.Builder.create(CraftingTerminalTileEntity::new, craftingTerminalBlock).build(null).setRegistryName("crafting_terminal"),
pressurizerTileEntity = (TileEntityType<PressurizerTileEntity>) TileEntityType.Builder.create(PressurizerTileEntity::new, pressurizerBlock).build(null).setRegistryName("pressurizer")
);
}
@ -156,10 +181,13 @@ public final class Registry {
pipeContainer = (ContainerType<MainPipeContainer>) IForgeContainerType.create((windowId, inv, data) -> new MainPipeContainer(pipeContainer, windowId, inv.player, data.readBlockPos())).setRegistryName("pipe"),
itemTerminalContainer = (ContainerType<ItemTerminalContainer>) IForgeContainerType.create((windowId, inv, data) -> new ItemTerminalContainer(itemTerminalContainer, windowId, inv.player, data.readBlockPos())).setRegistryName("item_terminal"),
craftingTerminalContainer = (ContainerType<CraftingTerminalContainer>) IForgeContainerType.create((windowId, inv, data) -> new CraftingTerminalContainer(craftingTerminalContainer, windowId, inv.player, data.readBlockPos())).setRegistryName("crafting_terminal"),
pressurizerContainer = (ContainerType<PressurizerContainer>) IForgeContainerType.create((windowId, inv, data) -> new PressurizerContainer(pressurizerContainer, windowId, inv.player, data.readBlockPos())).setRegistryName("pressurizer"),
extractionModuleContainer = createPipeContainer("extraction_module"),
filterModuleContainer = createPipeContainer("filter_module"),
retrievalModuleContainer = createPipeContainer("retrieval_module"),
stackSizeModuleContainer = createPipeContainer("stack_size_module")
stackSizeModuleContainer = createPipeContainer("stack_size_module"),
filterIncreaseModuleContainer = createPipeContainer("filter_increase_module"),
craftingModuleContainer = createPipeContainer("crafting_module")
);
}
@ -180,34 +208,42 @@ public final class Registry {
}
public static void setup(FMLCommonSetupEvent event) {
CapabilityManager.INSTANCE.register(PipeNetwork.class, new Capability.IStorage<PipeNetwork>() {
@Nullable
@Override
public INBT writeNBT(Capability<PipeNetwork> capability, PipeNetwork instance, Direction side) {
return null;
}
@Override
public void readNBT(Capability<PipeNetwork> capability, PipeNetwork instance, Direction side, INBT nbt) {
}
}, () -> null);
registerCap(PipeNetwork.class);
registerCap(IPipeConnectable.class);
PacketHandler.setup();
}
public static final class Client {
public static void setup(FMLClientSetupEvent event) {
RenderTypeLookup.setRenderLayer(pipeBlock, RenderType.cutout());
RenderTypeLookup.setRenderLayer(pipeBlock, RenderType.getCutout());
ClientRegistry.bindTileEntityRenderer(pipeTileEntity, PipeRenderer::new);
RenderingRegistry.registerEntityRenderingHandler(pipeFrameEntity, PipeFrameRenderer::new);
ScreenManager.registerFactory(pipeContainer, MainPipeGui::new);
ScreenManager.registerFactory(itemTerminalContainer, ItemTerminalGui::new);
ScreenManager.registerFactory(pressurizerContainer, PressurizerGui::new);
ScreenManager.registerFactory(craftingTerminalContainer, CraftingTerminalGui::new);
ScreenManager.registerFactory(extractionModuleContainer, ExtractionModuleGui::new);
ScreenManager.registerFactory(filterModuleContainer, FilterModuleGui::new);
ScreenManager.registerFactory(retrievalModuleContainer, RetrievalModuleGui::new);
ScreenManager.registerFactory(stackSizeModuleContainer, StackSizeModuleGui::new);
ScreenManager.registerFactory(filterIncreaseModuleContainer, FilterIncreaseModuleGui::new);
ScreenManager.registerFactory(craftingModuleContainer, CraftingModuleGui::new);
}
}
private static <T> void registerCap(Class<T> capClass) {
CapabilityManager.INSTANCE.register(capClass, new Capability.IStorage<T>() {
@Nullable
@Override
public INBT writeNBT(Capability<T> capability, T instance, Direction side) {
return null;
}
@Override
public void readNBT(Capability<T> capability, T instance, Direction side, INBT nbt) {
}
}, () -> null);
}
}

View file

@ -2,22 +2,32 @@ package de.ellpeck.prettypipes;
import de.ellpeck.prettypipes.items.IModule;
import de.ellpeck.prettypipes.network.PipeItem;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.inventory.ISidedInventoryProvider;
import net.minecraft.inventory.InventoryHelper;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.network.play.server.SUpdateTileEntityPacket;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.text.*;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.util.INBTSerializable;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.wrapper.SidedInvWrapper;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
@ -25,10 +35,11 @@ import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public final class Utility {
public static <T extends TileEntity> T getTileEntity(Class<T> type, World world, BlockPos pos) {
public static <T extends TileEntity> T getTileEntity(Class<T> type, IBlockReader world, BlockPos pos) {
TileEntity tile = world.getTileEntity(pos);
return type.isInstance(tile) ? (T) tile : null;
}
@ -107,13 +118,34 @@ public final class Utility {
return list;
}
public static void sendTileEntityToClients(TileEntity tile) {
ServerWorld world = (ServerWorld) tile.getWorld();
Stream<ServerPlayerEntity> entities = world.getChunkProvider().chunkManager.getTrackingPlayers(new ChunkPos(tile.getPos()), false);
SUpdateTileEntityPacket packet = new SUpdateTileEntityPacket(tile.getPos(), -1, tile.write(new CompoundNBT()));
entities.forEach(e -> e.connection.sendPacket(packet));
}
public static <T extends INBTSerializable<CompoundNBT>> List<T> deserializeAll(ListNBT list, Function<CompoundNBT, T> supplier) {
List<T> items = new ArrayList<>();
for (int i = 0; i < list.size(); i++)
items.add(supplier.apply(list.getCompound(i)));
for (int i = 0; i < list.size(); i++) {
T item = supplier.apply(list.getCompound(i));
if (item != null)
items.add(item);
}
return items;
}
public static IItemHandler getBlockItemHandler(World world, BlockPos pos, Direction direction) {
BlockState state = world.getBlockState(pos);
Block block = state.getBlock();
if (!(block instanceof ISidedInventoryProvider))
return null;
ISidedInventory inventory = ((ISidedInventoryProvider) block).createInventory(state, world, pos);
if (inventory == null)
return null;
return new SidedInvWrapper(inventory, direction);
}
public interface IMergeItemStack {
boolean mergeItemStack(ItemStack stack, int startIndex, int endIndex, boolean reverseDirection);
}

View file

@ -0,0 +1,52 @@
package de.ellpeck.prettypipes.compat.jei;
import de.ellpeck.prettypipes.misc.ItemEqualityType;
import de.ellpeck.prettypipes.packets.PacketCraftingModuleTransfer;
import de.ellpeck.prettypipes.packets.PacketHandler;
import de.ellpeck.prettypipes.pipe.modules.craft.CraftingModuleContainer;
import mezz.jei.api.gui.IRecipeLayout;
import mezz.jei.api.gui.ingredient.IGuiIngredient;
import mezz.jei.api.recipe.transfer.IRecipeTransferError;
import mezz.jei.api.recipe.transfer.IRecipeTransferHandler;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class CraftingModuleTransferHandler implements IRecipeTransferHandler<CraftingModuleContainer> {
@Override
public Class<CraftingModuleContainer> getContainerClass() {
return CraftingModuleContainer.class;
}
@Override
public IRecipeTransferError transferRecipe(CraftingModuleContainer container, IRecipeLayout recipeLayout, PlayerEntity player, boolean maxTransfer, boolean doTransfer) {
if (!doTransfer)
return null;
Map<Integer, ? extends IGuiIngredient<ItemStack>> ings = recipeLayout.getItemStacks().getGuiIngredients();
List<ItemStack> inputs = new ArrayList<>();
List<ItemStack> outputs = new ArrayList<>();
for (Map.Entry<Integer, ? extends IGuiIngredient<ItemStack>> entry : ings.entrySet()) {
List<ItemStack> allIngredients = entry.getValue().getAllIngredients();
if (allIngredients.isEmpty())
continue;
ItemStack remain = allIngredients.get(0).copy();
List<ItemStack> toAdd = entry.getValue().isInput() ? inputs : outputs;
for (ItemStack stack : toAdd) {
if (ItemEqualityType.compareItems(stack, remain)) {
int fits = Math.min(stack.getMaxStackSize() - stack.getCount(), remain.getCount());
stack.grow(fits);
remain.shrink(fits);
if (remain.isEmpty())
break;
}
}
if (!remain.isEmpty())
toAdd.add(remain);
}
PacketHandler.sendToServer(new PacketCraftingModuleTransfer(inputs, outputs));
return null;
}
}

View file

@ -2,19 +2,25 @@ package de.ellpeck.prettypipes.compat.jei;
import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.misc.PlayerPrefs;
import de.ellpeck.prettypipes.terminal.containers.CraftingTerminalGui;
import de.ellpeck.prettypipes.terminal.containers.ItemTerminalGui;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.JeiPlugin;
import mezz.jei.api.constants.VanillaRecipeCategoryUid;
import mezz.jei.api.gui.handlers.IGuiContainerHandler;
import mezz.jei.api.registration.IGuiHandlerRegistration;
import mezz.jei.api.registration.IRecipeTransferRegistration;
import mezz.jei.api.runtime.IIngredientFilter;
import mezz.jei.api.runtime.IJeiRuntime;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.client.renderer.Rectangle2d;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.client.event.GuiScreenEvent.DrawScreenEvent;
import net.minecraftforge.client.event.GuiScreenEvent.InitGuiEvent;
@ -24,6 +30,9 @@ import net.minecraftforge.event.TickEvent.ClientTickEvent;
import net.minecraftforge.event.world.PistonEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@JeiPlugin
@ -51,6 +60,23 @@ public class JEIPrettyPipesPlugin implements IModPlugin {
@Override
public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration) {
registration.addRecipeTransferHandler(new CraftingTerminalTransferHandler(), VanillaRecipeCategoryUid.CRAFTING);
registration.addUniversalRecipeTransferHandler(new CraftingModuleTransferHandler());
}
@Override
public void registerGuiHandlers(IGuiHandlerRegistration registration) {
registration.addGuiContainerHandler(ItemTerminalGui.class, new IGuiContainerHandler<ItemTerminalGui>() {
@Override
public List<Rectangle2d> getGuiExtraAreas(ItemTerminalGui containerScreen) {
List<Rectangle2d> ret = new ArrayList<>();
// sorting buttons
ret.add(new Rectangle2d(containerScreen.getGuiLeft() - 22, containerScreen.getGuiTop(), 22, 64));
// crafting hud
if (containerScreen.currentlyCrafting != null && !containerScreen.currentlyCrafting.isEmpty())
ret.add(new Rectangle2d(containerScreen.getGuiLeft() + containerScreen.getXSize(), containerScreen.getGuiTop() + 4, 65, 89));
return ret;
}
});
}
@SubscribeEvent

View file

@ -18,10 +18,7 @@ import net.minecraft.network.PacketBuffer;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.*;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.GameRules;

View file

@ -31,12 +31,12 @@ public class PipeFrameRenderer extends ItemFrameRenderer {
FontRenderer font = this.getFontRendererFromRenderManager();
int amount = ((PipeFrameEntity) entityIn).getAmount();
String ammountStrg = amount < 0 ? "?" : String.valueOf(amount);
float x = 0.5F - font.getStringWidth(ammountStrg) / 2F;
Matrix4f matrix4f = matrixStackIn.getLast().getPositionMatrix();
String amountStr = amount < 0 ? "?" : String.valueOf(amount);
float x = 0.5F - font.getStringWidth(amountStr) / 2F;
Matrix4f matrix4f = matrixStackIn.getLast().getMatrix();
matrixStackIn.translate(0, 0.285F, 0.415F);
matrixStackIn.scale(-0.02F, -0.02F, 0.02F);
font.renderString(ammountStrg, x, 0, 0xFFFFFF, true, matrix4f, bufferIn, false, 0, packedLightIn);
font.renderString(amountStr, x, 0, 0xFFFFFF, true, matrix4f, bufferIn, false, 0, packedLightIn);
matrixStackIn.pop();
}

View file

@ -1,12 +1,16 @@
package de.ellpeck.prettypipes.items;
import de.ellpeck.prettypipes.pipe.containers.AbstractPipeContainer;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import de.ellpeck.prettypipes.pipe.containers.AbstractPipeContainer;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.items.IItemHandler;
import java.util.List;
import java.util.function.Consumer;
public interface IModule {
void tick(ItemStack module, PipeTileEntity tile);
@ -28,4 +32,10 @@ public interface IModule {
float getItemSpeedIncrease(ItemStack module, PipeTileEntity tile);
boolean canPipeWork(ItemStack module, PipeTileEntity tile);
List<ItemStack> getAllCraftables(ItemStack module, PipeTileEntity tile);
int getCraftableAmount(ItemStack module, PipeTileEntity tile, Consumer<ItemStack> unavailableConsumer, ItemStack stack);
ItemStack craft(ItemStack module, PipeTileEntity tile, BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack);
}

View file

@ -2,6 +2,7 @@ package de.ellpeck.prettypipes.items;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.misc.ItemEqualityType;
import de.ellpeck.prettypipes.pipe.containers.AbstractPipeContainer;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import net.minecraft.client.util.ITooltipFlag;
@ -9,6 +10,7 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.*;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
@ -16,7 +18,9 @@ import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.items.IItemHandler;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
public abstract class ModuleItem extends Item implements IModule {
@ -73,4 +77,19 @@ public abstract class ModuleItem extends Item implements IModule {
public boolean canPipeWork(ItemStack module, PipeTileEntity tile) {
return true;
}
@Override
public List<ItemStack> getAllCraftables(ItemStack module, PipeTileEntity tile) {
return Collections.emptyList();
}
@Override
public int getCraftableAmount(ItemStack module, PipeTileEntity tile, Consumer<ItemStack> unavailableConsumer, ItemStack stack) {
return 0;
}
@Override
public ItemStack craft(ItemStack module, PipeTileEntity tile, BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack) {
return stack;
}
}

View file

@ -1,13 +1,17 @@
package de.ellpeck.prettypipes.items;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.pipe.PipeBlock;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.pipe.ConnectionType;
import de.ellpeck.prettypipes.pipe.PipeBlock;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.Enchantments;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.*;
import net.minecraft.state.EnumProperty;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
@ -16,11 +20,14 @@ import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.World;
import java.util.List;
import java.util.Map;
public class WrenchItem extends Item {
public WrenchItem() {
super(new Item.Properties().maxStackSize(1).group(Registry.GROUP));
}
@ -29,20 +36,56 @@ public class WrenchItem extends Item {
public ActionResultType onItemUse(ItemUseContext context) {
World world = context.getWorld();
BlockPos pos = context.getPos();
PlayerEntity player = context.getPlayer();
BlockState state = world.getBlockState(pos);
if (!(state.getBlock() instanceof PipeBlock))
return ActionResultType.PASS;
PipeTileEntity tile = Utility.getTileEntity(PipeTileEntity.class, world, pos);
if (tile == null)
return ActionResultType.FAIL;
if (player == null)
return ActionResultType.FAIL;
if (context.getPlayer().isShiftKeyDown()) {
if (player.isSneaking()) {
if (!world.isRemote) {
Block.spawnDrops(state, world, pos, world.getTileEntity(pos), null, ItemStack.EMPTY);
if (tile.cover != null) {
// remove the cover
tile.removeCover(player, context.getHand());
Utility.sendTileEntityToClients(tile);
} else {
// remove the pipe
Block.spawnDrops(state, world, pos, tile, null, ItemStack.EMPTY);
world.removeBlock(pos, false);
}
world.playSound(null, pos, SoundEvents.ENTITY_ITEM_FRAME_REMOVE_ITEM, SoundCategory.PLAYERS, 1, 1);
world.removeBlock(pos, false);
return ActionResultType.CONSUME;
} else {
return ActionResultType.SUCCESS;
}
return ActionResultType.SUCCESS;
}
// Blocking
// placing covers
if (tile.cover == null) {
ItemStack offhand = player.getHeldItemOffhand();
if (offhand.getItem() instanceof BlockItem) {
if (!world.isRemote) {
BlockItemUseContext blockContext = new BlockItemUseContext(context);
Block block = ((BlockItem) offhand.getItem()).getBlock();
BlockState cover = block.getStateForPlacement(blockContext);
if (cover != null && !block.hasTileEntity(cover)) {
tile.cover = cover;
Utility.sendTileEntityToClients(tile);
offhand.shrink(1);
world.playSound(null, pos, SoundEvents.ENTITY_ITEM_FRAME_ADD_ITEM, SoundCategory.PLAYERS, 1, 1);
}
return ActionResultType.CONSUME;
} else {
return ActionResultType.SUCCESS;
}
}
}
// disabling directions
for (Map.Entry<Direction, VoxelShape> entry : PipeBlock.DIR_SHAPES.entrySet()) {
AxisAlignedBB box = entry.getValue().getBoundingBox().offset(pos).grow(0.001F);
if (!box.contains(context.getHitVec()))
@ -65,9 +108,31 @@ public class WrenchItem extends Item {
world.setBlockState(pos, newState);
PipeBlock.onStateChanged(world, pos, newState);
world.playSound(null, pos, SoundEvents.ENTITY_ITEM_FRAME_ROTATE_ITEM, SoundCategory.PLAYERS, 1, 1);
return ActionResultType.CONSUME;
} else {
return ActionResultType.SUCCESS;
}
return ActionResultType.SUCCESS;
}
return ActionResultType.PASS;
}
@Override
public void addInformation(ItemStack stack, World worldIn, List<ITextComponent> tooltip, ITooltipFlag flagIn) {
Utility.addTooltip(this.getRegistryName().getPath(), tooltip);
}
@Override
public boolean isEnchantable(ItemStack stack) {
return true;
}
@Override
public int getItemEnchantability(ItemStack stack) {
return 1;
}
@Override
public boolean canApplyAtEnchantingTable(ItemStack stack, Enchantment enchantment) {
return enchantment == Enchantments.SILK_TOUCH;
}
}

View file

@ -8,8 +8,12 @@ import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.SlotItemHandler;
public class FilterSlot extends SlotItemHandler {
public FilterSlot(IItemHandler itemHandler, int index, int xPosition, int yPosition) {
private final boolean onlyOneItem;
public FilterSlot(IItemHandler itemHandler, int index, int xPosition, int yPosition, boolean onlyOneItem) {
super(itemHandler, index, xPosition, yPosition);
this.onlyOneItem = onlyOneItem;
}
public static boolean checkFilter(Container container, int slotId, PlayerEntity player) {
@ -31,7 +35,8 @@ public class FilterSlot extends SlotItemHandler {
this.putStack(ItemStack.EMPTY);
} else if (!heldStack.isEmpty()) {
ItemStack s = heldStack.copy();
s.setCount(1);
if (this.onlyOneItem)
s.setCount(1);
this.putStack(s);
}
}

View file

@ -17,7 +17,8 @@ public enum ItemEqualityType {
if (filterTags.isEmpty())
return false;
return stackTags.containsAll(filterTags);
}, true);
}, true),
MOD((stack, filter) -> stack.getItem().getCreatorModId(stack).equals(filter.getItem().getCreatorModId(filter)), true);
public final BiFunction<ItemStack, ItemStack, Boolean> filter;
public final boolean ignoreItemEquality;

View file

@ -1,9 +1,11 @@
package de.ellpeck.prettypipes.misc;
import com.mojang.blaze3d.matrix.MatrixStack;
import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.packets.PacketButton;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import de.ellpeck.prettypipes.pipe.modules.FilterModifierModule;
import de.ellpeck.prettypipes.pipe.modules.FilterModifierModuleItem;
import de.ellpeck.prettypipes.pipe.modules.filter.FilterIncreaseModuleItem;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.Widget;
import net.minecraft.client.gui.widget.button.Button;
@ -13,6 +15,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.Direction;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.items.IItemHandler;
@ -46,7 +49,7 @@ public class ItemFilter extends ItemStackHandler {
public List<Slot> getSlots(int x, int y) {
List<Slot> slots = new ArrayList<>();
for (int i = 0; i < this.getSlots(); i++)
slots.add(new FilterSlot(this, i, x + i % 9 * 18, y + i / 9 * 18));
slots.add(new FilterSlot(this, i, x + i % 9 * 18, y + i / 9 * 18, true));
return slots;
}
@ -75,10 +78,12 @@ public class ItemFilter extends ItemStackHandler {
if (id == 0 && this.canModifyWhitelist) {
this.isWhitelist = !this.isWhitelist;
this.modified = true;
this.save();
} else if (id == 1 && this.canPopulateFromInventories) {
// populate filter from inventories
List<ItemFilter> filters = this.getAllFilters();
for (Direction direction : Direction.values()) {
IItemHandler handler = this.pipe.getItemHandler(direction, null);
IItemHandler handler = this.pipe.getItemHandler(direction);
if (handler == null)
continue;
for (int i = 0; i < handler.getSlots(); i++) {
@ -87,12 +92,16 @@ public class ItemFilter extends ItemStackHandler {
continue;
ItemStack copy = stack.copy();
copy.setCount(1);
ItemHandlerHelper.insertItem(this, copy, false);
// try inserting into ourselves and any filter increase modifiers
for (ItemFilter filter : filters) {
if (ItemHandlerHelper.insertItem(filter, copy, false).isEmpty()) {
filter.save();
break;
}
}
}
}
this.modified = true;
}
this.save();
}
public boolean isAllowed(ItemStack stack) {
@ -100,23 +109,28 @@ public class ItemFilter extends ItemStackHandler {
}
private boolean isFiltered(ItemStack stack) {
ItemEqualityType[] types = this.getEqualityTypes();
for (int i = 0; i < this.getSlots(); i++) {
ItemStack filter = this.getStackInSlot(i);
if (filter.isEmpty())
continue;
if (ItemEqualityType.compareItems(stack, filter, types))
return true;
ItemEqualityType[] types = getEqualityTypes(this.pipe);
// also check if any filter increase modules have the item we need
for (ItemStackHandler handler : this.getAllFilters()) {
for (int i = 0; i < handler.getSlots(); i++) {
ItemStack filter = handler.getStackInSlot(i);
if (filter.isEmpty())
continue;
if (ItemEqualityType.compareItems(stack, filter, types))
return true;
}
}
return false;
}
public ItemEqualityType[] getEqualityTypes() {
return this.pipe.streamModules()
.map(Pair::getRight)
.filter(m -> m instanceof FilterModifierModule)
.map(m -> ((FilterModifierModule) m).type)
.toArray(ItemEqualityType[]::new);
private List<ItemFilter> getAllFilters() {
List<ItemFilter> filters = this.pipe.streamModules()
.filter(p -> p.getRight() instanceof FilterIncreaseModuleItem)
.map(p -> new ItemFilter(18, p.getLeft(), this.pipe))
.collect(Collectors.toList());
// add ourselves to the front
filters.add(0, this);
return filters;
}
public void save() {
@ -129,14 +143,16 @@ public class ItemFilter extends ItemStackHandler {
@Override
public CompoundNBT serializeNBT() {
CompoundNBT nbt = super.serializeNBT();
nbt.putBoolean("whitelist", this.isWhitelist);
if (this.canModifyWhitelist)
nbt.putBoolean("whitelist", this.isWhitelist);
return nbt;
}
@Override
public void deserializeNBT(CompoundNBT nbt) {
super.deserializeNBT(nbt);
this.isWhitelist = nbt.getBoolean("whitelist");
if (this.canModifyWhitelist)
this.isWhitelist = nbt.getBoolean("whitelist");
}
@Override
@ -144,6 +160,14 @@ public class ItemFilter extends ItemStackHandler {
this.modified = true;
}
public static ItemEqualityType[] getEqualityTypes(PipeTileEntity pipe) {
return pipe.streamModules()
.map(Pair::getRight)
.filter(m -> m instanceof FilterModifierModuleItem)
.map(m -> ((FilterModifierModuleItem) m).type)
.toArray(ItemEqualityType[]::new);
}
public interface IFilteredContainer {
ItemFilter getFilter();
}

View file

@ -6,8 +6,8 @@ import java.util.Comparator;
public enum ItemOrder {
AMOUNT(Comparator.comparingInt(ItemStack::getCount)),
NAME(Comparator.comparing(s -> s.getDisplayName().getFormattedText())),
MOD(Comparator.comparing(s -> s.getItem().getRegistryName().getNamespace()));
NAME(Comparator.comparing(s -> s.getDisplayName().getString())),
MOD(Comparator.comparing(s -> s.getItem().getCreatorModId(s)));
public final Comparator<ItemStack> comparator;

View file

@ -10,8 +10,12 @@ import net.minecraft.client.gui.widget.Widget;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextComponent;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.fml.client.gui.GuiUtils;
import org.apache.commons.lang3.tuple.Pair;
import java.util.List;
@ -23,6 +27,7 @@ public class ItemTerminalWidget extends Widget {
public final int gridY;
public boolean selected;
public ItemStack stack = ItemStack.EMPTY;
public boolean craftable;
public ItemTerminalWidget(int xIn, int yIn, int gridX, int gridY, ItemTerminalGui screen) {
super(xIn, yIn, 16, 16, "");
@ -48,10 +53,12 @@ public class ItemTerminalWidget extends Widget {
fill(this.x, this.y, this.x + 16, this.y + 16, -2130706433);
RenderSystem.enableDepthTest();
renderer.renderItemAndEffectIntoGUI(mc.player, this.stack, this.x, this.y);
int amount = this.stack.getCount();
int amount = !this.craftable ? this.stack.getCount() : 0;
String amountStrg = this.stack.getCount() >= 1000 ? amount / 1000 + "k" : String.valueOf(amount);
FontRenderer font = mc.getFontResourceManager().getFontRenderer(FONT);
renderer.renderItemOverlayIntoGUI(font, this.stack, this.x, this.y, amountStrg);
RenderSystem.pushMatrix();
RenderSystem.scalef(0.8F, 0.8F, 1);
renderer.renderItemOverlayIntoGUI(mc.fontRenderer, this.stack, (int) (this.x / 0.8F) + 4, (int) (this.y / 0.8F) + 4, amountStrg);
RenderSystem.popMatrix();
renderer.zLevel = 0;
this.setBlitOffset(0);

View file

@ -55,12 +55,13 @@ public class NetworkLocation implements INBTSerializable<CompoundNBT> {
IItemHandler handler = this.getItemHandler(world);
if (handler != null) {
for (int i = 0; i < handler.getSlots(); i++) {
ItemStack found = handler.extractItem(i, Integer.MAX_VALUE, true);
if (found.isEmpty())
// check if the slot is accessible to us
if (handler.extractItem(i, 1, true).isEmpty())
continue;
if (this.itemCache == null)
this.itemCache = new HashMap<>();
this.itemCache.put(i, found);
// use getStackInSlot since there might be more than 64 items in there
this.itemCache.put(i, handler.getStackInSlot(i));
}
}
}
@ -71,7 +72,7 @@ public class NetworkLocation implements INBTSerializable<CompoundNBT> {
if (this.handlerCache == null) {
PipeNetwork network = PipeNetwork.get(world);
PipeTileEntity pipe = network.getPipe(this.pipePos);
this.handlerCache = pipe.getItemHandler(this.direction, null);
this.handlerCache = pipe.getItemHandler(this.direction);
}
return this.handlerCache;
}

View file

@ -1,25 +1,37 @@
package de.ellpeck.prettypipes.network;
import com.mojang.blaze3d.matrix.MatrixStack;
import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.pipe.IPipeConnectable;
import de.ellpeck.prettypipes.pipe.IPipeItem;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import joptsimple.internal.Strings;
import net.minecraft.block.BlockState;
import net.minecraft.block.ILiquidContainer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.model.ItemCameraTransforms;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.IFluidState;
import net.minecraft.fluid.FluidState;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.INBTSerializable;
import net.minecraftforge.items.CapabilityItemHandler;
@ -28,12 +40,13 @@ import net.minecraftforge.items.ItemHandlerHelper;
import org.jgrapht.Graph;
import org.jgrapht.GraphPath;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
public class PipeItem implements INBTSerializable<CompoundNBT>, ILiquidContainer {
public class PipeItem implements IPipeItem {
public static final ResourceLocation TYPE = new ResourceLocation(PrettyPipes.ID, "pipe_item");
public ItemStack stack;
public float speed;
@ -51,17 +64,30 @@ public class PipeItem implements INBTSerializable<CompoundNBT>, ILiquidContainer
protected int currentTile;
protected boolean retryOnObstruction;
protected long lastWorldTick;
protected ResourceLocation type;
public PipeItem(ItemStack stack, float speed) {
public PipeItem(ResourceLocation type, ItemStack stack, float speed) {
this.type = type;
this.stack = stack;
this.speed = speed;
}
public PipeItem(CompoundNBT nbt) {
public PipeItem(ItemStack stack, float speed) {
this(TYPE, stack, speed);
}
public PipeItem(ResourceLocation type, CompoundNBT nbt) {
this.type = type;
this.path = new ArrayList<>();
this.deserializeNBT(nbt);
}
@Override
public ItemStack getContent() {
return this.stack;
}
@Override
public void setDestination(BlockPos startInventory, BlockPos destInventory, GraphPath<BlockPos, NetworkEdge> path) {
this.startInventory = startInventory;
this.destInventory = destInventory;
@ -77,6 +103,7 @@ public class PipeItem implements INBTSerializable<CompoundNBT>, ILiquidContainer
}
}
@Override
public void updateInPipe(PipeTileEntity currPipe) {
// this prevents pipes being updated after one another
// causing an item that just switched to tick twice
@ -85,72 +112,78 @@ public class PipeItem implements INBTSerializable<CompoundNBT>, ILiquidContainer
return;
this.lastWorldTick = worldTick;
float currSpeed = this.speed;
BlockPos myPos = new BlockPos(this.x, this.y, this.z);
if (!myPos.equals(currPipe.getPos()) && (currPipe.getPos().equals(this.getDestPipe()) || !myPos.equals(this.startInventory))) {
// we're done with the current pipe, so switch to the next one
currPipe.getItems().remove(this);
PipeTileEntity next = this.getNextTile(currPipe, true);
if (next == null) {
if (!currPipe.getWorld().isRemote) {
if (currPipe.getPos().equals(this.getDestPipe())) {
// ..or store in our destination container if we reached our destination
this.stack = this.store(currPipe);
if (!this.stack.isEmpty())
this.onPathObstructed(currPipe, true);
} else {
this.onPathObstructed(currPipe, false);
}
}
return;
} else {
next.getItems().add(this);
}
} else {
double dist = new Vec3d(this.currGoalPos).squareDistanceTo(this.x - 0.5F, this.y - 0.5F, this.z - 0.5F);
if (dist < this.speed * this.speed) {
// we're past the start of the pipe, so move to the center of the next pipe
BlockPos nextPos;
PipeTileEntity next = this.getNextTile(currPipe, false);
float motionLeft = this.speed;
while (motionLeft > 0) {
float currSpeed = Math.min(0.25F, motionLeft);
motionLeft -= currSpeed;
BlockPos myPos = new BlockPos(this.x, this.y, this.z);
if (!myPos.equals(currPipe.getPos()) && (currPipe.getPos().equals(this.getDestPipe()) || !myPos.equals(this.startInventory))) {
// we're done with the current pipe, so switch to the next one
currPipe.getItems().remove(this);
PipeTileEntity next = this.getNextTile(currPipe, true);
if (next == null) {
if (currPipe.getPos().equals(this.getDestPipe())) {
nextPos = this.destInventory;
} else {
currPipe.getItems().remove(this);
if (!currPipe.getWorld().isRemote)
if (!currPipe.getWorld().isRemote) {
if (currPipe.getPos().equals(this.getDestPipe())) {
// ..or store in our destination container if we reached our destination
this.stack = this.store(currPipe);
if (!this.stack.isEmpty())
this.onPathObstructed(currPipe, true);
} else {
this.onPathObstructed(currPipe, false);
return;
}
}
return;
} else {
nextPos = next.getPos();
next.getItems().add(this);
currPipe = next;
}
float tolerance = 0.001F;
if (dist >= tolerance * tolerance) {
// when going around corners, we want to move right up to the corner
Vec3d motion = new Vec3d(this.x - this.lastX, this.y - this.lastY, this.z - this.lastZ);
Vec3d diff = new Vec3d(nextPos.getX() + 0.5F - this.x, nextPos.getY() + 0.5F - this.y, nextPos.getZ() + 0.5F - this.z);
if (motion.crossProduct(diff).length() >= tolerance) {
currSpeed = (float) Math.sqrt(dist);
} else {
double dist = new Vec3d(this.currGoalPos).squareDistanceTo(this.x - 0.5F, this.y - 0.5F, this.z - 0.5F);
if (dist < currSpeed * currSpeed) {
// we're past the start of the pipe, so move to the center of the next pipe
BlockPos nextPos;
PipeTileEntity next = this.getNextTile(currPipe, false);
if (next == null) {
if (currPipe.getPos().equals(this.getDestPipe())) {
nextPos = this.destInventory;
} else {
currPipe.getItems().remove(this);
if (!currPipe.getWorld().isRemote)
this.onPathObstructed(currPipe, false);
return;
}
} else {
// we're not going around a corner, so continue
nextPos = next.getPos();
}
float tolerance = 0.001F;
if (dist >= tolerance * tolerance) {
// when going around corners, we want to move right up to the corner
Vec3d motion = new Vec3d(this.x - this.lastX, this.y - this.lastY, this.z - this.lastZ);
Vec3d diff = new Vec3d(nextPos.getX() + 0.5F - this.x, nextPos.getY() + 0.5F - this.y, nextPos.getZ() + 0.5F - this.z);
if (motion.crossProduct(diff).length() >= tolerance) {
currSpeed = (float) Math.sqrt(dist);
} else {
// we're not going around a corner, so continue
this.currGoalPos = nextPos;
}
} else {
// distance is very small, so continue
this.currGoalPos = nextPos;
}
} else {
// distance is very small, so continue
this.currGoalPos = nextPos;
}
}
this.lastX = this.x;
this.lastY = this.y;
this.lastZ = this.z;
Vec3d dist = new Vec3d(this.currGoalPos.getX() + 0.5F - this.x, this.currGoalPos.getY() + 0.5F - this.y, this.currGoalPos.getZ() + 0.5F - this.z);
dist = dist.normalize();
this.x += dist.x * currSpeed;
this.y += dist.y * currSpeed;
this.z += dist.z * currSpeed;
}
this.lastX = this.x;
this.lastY = this.y;
this.lastZ = this.z;
Vec3d dist = new Vec3d(this.currGoalPos.getX() + 0.5F - this.x, this.currGoalPos.getY() + 0.5F - this.y, this.currGoalPos.getZ() + 0.5F - this.z);
dist = dist.normalize();
this.x += dist.x * currSpeed;
this.y += dist.y * currSpeed;
this.z += dist.z * currSpeed;
}
protected void onPathObstructed(PipeTileEntity currPipe, boolean tryReturn) {
@ -159,22 +192,23 @@ public class PipeItem implements INBTSerializable<CompoundNBT>, ILiquidContainer
PipeNetwork network = PipeNetwork.get(currPipe.getWorld());
if (tryReturn) {
// first time: we try to return to our input chest
if (!this.retryOnObstruction && network.routeItemToLocation(currPipe.getPos(), this.destInventory, this.getStartPipe(), this.startInventory, speed -> this)) {
if (!this.retryOnObstruction && network.routeItemToLocation(currPipe.getPos(), this.destInventory, this.getStartPipe(), this.startInventory, this.stack, speed -> this)) {
this.retryOnObstruction = true;
return;
}
// second time: we arrived at our input chest, it is full, so we try to find a different goal location
ItemStack remain = network.routeItem(currPipe.getPos(), this.destInventory, this.stack, (stack, speed) -> this, false);
if (remain.isEmpty())
return;
this.stack = remain;
if (!remain.isEmpty())
this.drop(currPipe.getWorld(), remain.copy());
} else {
// if all re-routing attempts fail, we drop
this.drop(currPipe.getWorld(), this.stack);
}
// if all re-routing attempts fail, we drop
this.drop(currPipe.getWorld());
}
public void drop(World world) {
ItemEntity item = new ItemEntity(world, this.x, this.y, this.z, this.stack.copy());
@Override
public void drop(World world, ItemStack stack) {
ItemEntity item = new ItemEntity(world, this.x, this.y, this.z, stack.copy());
item.world.addEntity(item);
}
@ -182,8 +216,8 @@ public class PipeItem implements INBTSerializable<CompoundNBT>, ILiquidContainer
Direction dir = Utility.getDirectionFromOffset(this.destInventory, this.getDestPipe());
IPipeConnectable connectable = currPipe.getPipeConnectable(dir);
if (connectable != null)
return connectable.insertItem(currPipe.getWorld(), currPipe.getPos(), dir, this);
IItemHandler handler = currPipe.getItemHandler(dir, this);
return connectable.insertItem(currPipe.getPos(), dir, this.stack, false);
IItemHandler handler = currPipe.getItemHandler(dir);
if (handler != null)
return ItemHandlerHelper.insertItemStacked(handler, this.stack, false);
return this.stack;
@ -203,14 +237,17 @@ public class PipeItem implements INBTSerializable<CompoundNBT>, ILiquidContainer
return this.path.get(0);
}
@Override
public BlockPos getDestPipe() {
return this.path.get(this.path.size() - 1);
}
@Override
public BlockPos getCurrentPipe() {
return this.path.get(this.currentTile);
}
@Override
public BlockPos getDestInventory() {
return this.destInventory;
}
@ -218,6 +255,7 @@ public class PipeItem implements INBTSerializable<CompoundNBT>, ILiquidContainer
@Override
public CompoundNBT serializeNBT() {
CompoundNBT nbt = new CompoundNBT();
nbt.putString("type", this.type.toString());
nbt.put("stack", this.stack.serializeNBT());
nbt.putFloat("speed", this.speed);
nbt.put("start_inv", NBTUtil.writeBlockPos(this.startInventory));
@ -253,6 +291,59 @@ public class PipeItem implements INBTSerializable<CompoundNBT>, ILiquidContainer
this.path.add(NBTUtil.readBlockPos(list.getCompound(i)));
}
@Override
public int getItemsOnTheWay(BlockPos goalInv) {
return this.stack.getCount();
}
@Override
@OnlyIn(Dist.CLIENT)
public void render(PipeTileEntity tile, MatrixStack matrixStack, Random random, float partialTicks, int light, int overlay, IRenderTypeBuffer buffer) {
matrixStack.translate(
MathHelper.lerp(partialTicks, this.lastX, this.x),
MathHelper.lerp(partialTicks, this.lastY, this.y),
MathHelper.lerp(partialTicks, this.lastZ, this.z));
if (this.stack.getItem() instanceof BlockItem) {
float scale = 0.7F;
matrixStack.scale(scale, scale, scale);
matrixStack.translate(0, -0.2F, 0);
} else {
float scale = 0.45F;
matrixStack.scale(scale, scale, scale);
matrixStack.translate(0, -0.1F, 0);
}
random.setSeed(Item.getIdFromItem(this.stack.getItem()) + this.stack.getDamage());
int amount = this.getModelCount();
for (int i = 0; i < amount; i++) {
matrixStack.push();
if (amount > 1) {
matrixStack.translate(
(random.nextFloat() * 2.0F - 1.0F) * 0.25F * 0.5F,
(random.nextFloat() * 2.0F - 1.0F) * 0.25F * 0.5F,
(random.nextFloat() * 2.0F - 1.0F) * 0.25F * 0.5F);
}
Minecraft.getInstance().getItemRenderer().renderItem(this.stack, ItemCameraTransforms.TransformType.GROUND, light, overlay, matrixStack, buffer);
matrixStack.pop();
}
}
protected int getModelCount() {
int i = 1;
if (this.stack.getCount() > 48) {
i = 5;
} else if (this.stack.getCount() > 32) {
i = 4;
} else if (this.stack.getCount() > 16) {
i = 3;
} else if (this.stack.getCount() > 1) {
i = 2;
}
return i;
}
protected static List<BlockPos> compilePath(GraphPath<BlockPos, NetworkEdge> path) {
Graph<BlockPos, NetworkEdge> graph = path.getGraph();
List<BlockPos> ret = new ArrayList<>();
@ -285,14 +376,4 @@ public class PipeItem implements INBTSerializable<CompoundNBT>, ILiquidContainer
}
return ret;
}
@Override
public boolean canContainFluid(IBlockReader worldIn, BlockPos pos, BlockState state, Fluid fluidIn) {
return true;
}
@Override
public boolean receiveFluid(IWorld worldIn, BlockPos pos, BlockState state, IFluidState fluidStateIn) {
return false;
}
}

View file

@ -7,6 +7,7 @@ import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.misc.ItemEqualityType;
import de.ellpeck.prettypipes.pipe.IPipeItem;
import de.ellpeck.prettypipes.pipe.PipeBlock;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import de.ellpeck.prettypipes.packets.PacketHandler;
@ -17,16 +18,13 @@ import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.Direction;
import net.minecraft.util.Util;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import org.apache.commons.lang3.tuple.Pair;
import org.jgrapht.GraphPath;
import org.jgrapht.ListenableGraph;
@ -43,6 +41,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -53,9 +52,10 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
private final DijkstraShortestPath<BlockPos, NetworkEdge> dijkstra;
private final Map<BlockPos, List<BlockPos>> nodeToConnectedNodes = new HashMap<>();
private final Map<BlockPos, PipeTileEntity> tileCache = new HashMap<>();
private final ListMultimap<BlockPos, PipeItem> pipeItems = ArrayListMultimap.create();
private final ListMultimap<BlockPos, IPipeItem> pipeItems = ArrayListMultimap.create();
private final ListMultimap<BlockPos, NetworkLock> networkLocks = ArrayListMultimap.create();
private final World world;
private final LazyOptional<PipeNetwork> lazyThis = LazyOptional.of(() -> this);
public PipeNetwork(World world) {
this.world = world;
@ -67,7 +67,7 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
@Nonnull
@Override
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
return cap == Registry.pipeNetworkCapability ? LazyOptional.of(() -> (T) this) : LazyOptional.empty();
return cap == Registry.pipeNetworkCapability ? this.lazyThis.cast() : LazyOptional.empty();
}
@Override
@ -98,7 +98,7 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
ListNBT edges = nbt.getList("edges", NBT.TAG_COMPOUND);
for (int i = 0; i < edges.size(); i++)
this.addEdge(new NetworkEdge(edges.getCompound(i)));
for (PipeItem item : Utility.deserializeAll(nbt.getList("items", NBT.TAG_COMPOUND), PipeItem::new))
for (IPipeItem item : Utility.deserializeAll(nbt.getList("items", NBT.TAG_COMPOUND), IPipeItem::load))
this.pipeItems.put(item.getCurrentPipe(), item);
for (NetworkLock lock : Utility.deserializeAll(nbt.getList("locks", NBT.TAG_COMPOUND), NetworkLock::new))
this.createNetworkLock(lock);
@ -131,11 +131,11 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
}
}
public ItemStack tryInsertItem(BlockPos startPipePos, BlockPos startInventory, ItemStack stack, boolean preventOversending) {
public ItemStack routeItem(BlockPos startPipePos, BlockPos startInventory, ItemStack stack, boolean preventOversending) {
return this.routeItem(startPipePos, startInventory, stack, PipeItem::new, preventOversending);
}
public ItemStack routeItem(BlockPos startPipePos, BlockPos startInventory, ItemStack stack, BiFunction<ItemStack, Float, PipeItem> itemSupplier, boolean preventOversending) {
public ItemStack routeItem(BlockPos startPipePos, BlockPos startInventory, ItemStack stack, BiFunction<ItemStack, Float, IPipeItem> itemSupplier, boolean preventOversending) {
if (!this.isNode(startPipePos))
return stack;
if (!this.world.isBlockLoaded(startPipePos))
@ -151,8 +151,8 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
Pair<BlockPos, ItemStack> dest = pipe.getAvailableDestination(stack, false, preventOversending);
if (dest == null || dest.getLeft().equals(startInventory))
continue;
Function<Float, PipeItem> sup = speed -> itemSupplier.apply(dest.getRight(), speed);
if (this.routeItemToLocation(startPipePos, startInventory, pipe.getPos(), dest.getLeft(), sup)) {
Function<Float, IPipeItem> sup = speed -> itemSupplier.apply(dest.getRight(), speed);
if (this.routeItemToLocation(startPipePos, startInventory, pipe.getPos(), dest.getLeft(), dest.getRight(), sup)) {
ItemStack remain = stack.copy();
remain.shrink(dest.getRight().getCount());
this.endProfile();
@ -163,7 +163,7 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
return stack;
}
public boolean routeItemToLocation(BlockPos startPipePos, BlockPos startInventory, BlockPos destPipePos, BlockPos destInventory, Function<Float, PipeItem> itemSupplier) {
public boolean routeItemToLocation(BlockPos startPipePos, BlockPos startInventory, BlockPos destPipePos, BlockPos destInventory, ItemStack stack, Function<Float, IPipeItem> itemSupplier) {
if (!this.isNode(startPipePos) || !this.isNode(destPipePos))
return false;
if (!this.world.isBlockLoaded(startPipePos) || !this.world.isBlockLoaded(destPipePos))
@ -176,39 +176,66 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
this.endProfile();
if (path == null)
return false;
PipeItem item = itemSupplier.apply(startPipe.getItemSpeed());
IPipeItem item = itemSupplier.apply(startPipe.getItemSpeed(stack));
item.setDestination(startInventory, destInventory, path);
if (!startPipe.getItems().contains(item))
startPipe.getItems().add(item);
startPipe.addNewItem(item);
PacketHandler.sendToAllLoaded(this.world, startPipePos, new PacketItemEnterPipe(startPipePos, item));
return true;
}
public ItemStack requestItem(BlockPos destPipe, BlockPos destInventory, ItemStack stack, ItemEqualityType... equalityTypes) {
List<NetworkLocation> locations = this.getOrderedNetworkItems(destPipe);
if (locations.isEmpty())
return stack;
ItemStack remain = stack.copy();
for (NetworkLocation location : locations) {
remain = this.requestItem(location, destPipe, destInventory, remain, equalityTypes);
// check existing items
for (NetworkLocation location : this.getOrderedNetworkItems(destPipe)) {
remain = this.requestExistingItem(location, destPipe, destInventory, null, remain, equalityTypes);
if (remain.isEmpty())
break;
return remain;
}
return remain;
// check craftable items
return this.requestCraftedItem(destPipe, null, remain, equalityTypes);
}
public ItemStack requestItem(NetworkLocation location, BlockPos destPipe, BlockPos destInventory, ItemStack stack, ItemEqualityType... equalityTypes) {
public ItemStack requestCraftedItem(BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack, ItemEqualityType... equalityTypes) {
for (Pair<BlockPos, ItemStack> craftable : this.getAllCraftables(destPipe)) {
if (!ItemEqualityType.compareItems(stack, craftable.getRight(), equalityTypes))
continue;
PipeTileEntity pipe = this.getPipe(craftable.getLeft());
if (pipe == null)
continue;
stack = pipe.craft(destPipe, unavailableConsumer, stack);
if (stack.isEmpty())
break;
}
return stack;
}
public ItemStack requestExistingItem(NetworkLocation location, BlockPos destPipe, BlockPos destInventory, NetworkLock ignoredLock, ItemStack stack, ItemEqualityType... equalityTypes) {
return this.requestExistingItem(location, destPipe, destInventory, ignoredLock, PipeItem::new, stack, equalityTypes);
}
public ItemStack requestExistingItem(NetworkLocation location, BlockPos destPipe, BlockPos destInventory, NetworkLock ignoredLock, BiFunction<ItemStack, Float, IPipeItem> itemSupplier, ItemStack stack, ItemEqualityType... equalityTypes) {
if (location.getPos().equals(destInventory))
return stack;
// make sure we don't pull any locked items
int amount = location.getItemAmount(this.world, stack, equalityTypes);
if (amount <= 0)
return stack;
amount -= this.getLockedAmount(location.getPos(), stack, ignoredLock, equalityTypes);
if (amount <= 0)
return stack;
ItemStack remain = stack.copy();
// make sure we only extract less than or equal to the requested amount
if (remain.getCount() < amount)
amount = remain.getCount();
remain.shrink(amount);
for (int slot : location.getStackSlots(this.world, stack, equalityTypes)) {
// try to extract from that location's inventory and send the item
IItemHandler handler = location.getItemHandler(this.world);
ItemStack extracted = handler.extractItem(slot, remain.getCount(), true);
if (this.routeItemToLocation(location.pipePos, location.getPos(), destPipe, destInventory, speed -> new PipeItem(extracted, speed))) {
ItemStack extracted = handler.extractItem(slot, amount, true);
if (this.routeItemToLocation(location.pipePos, location.getPos(), destPipe, destInventory, extracted, speed -> itemSupplier.apply(extracted, speed))) {
handler.extractItem(slot, extracted.getCount(), false);
remain.shrink(extracted.getCount());
if (remain.isEmpty())
amount -= extracted.getCount();
if (amount <= 0)
break;
}
}
@ -224,6 +251,67 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
return tile;
}
public List<Pair<BlockPos, ItemStack>> getCurrentlyCrafting(BlockPos node) {
this.startProfile("get_currently_crafting");
List<Pair<BlockPos, ItemStack>> items = new ArrayList<>();
for (Pair<BlockPos, ItemStack> craftable : this.getAllCraftables(node)) {
PipeTileEntity pipe = this.getPipe(craftable.getLeft());
if (pipe == null)
continue;
for (Pair<BlockPos, ItemStack> request : pipe.craftResultRequests) {
BlockPos dest = request.getLeft();
ItemStack stack = request.getRight();
// add up all the items that should go to the same location
Optional<Pair<BlockPos, ItemStack>> existing = items.stream()
.filter(s -> s.getLeft().equals(dest) && ItemEqualityType.compareItems(s.getRight(), stack, ItemEqualityType.NBT))
.findFirst();
if (existing.isPresent()) {
existing.get().getRight().grow(stack.getCount());
} else {
items.add(Pair.of(dest, stack.copy()));
}
}
}
this.endProfile();
return items;
}
public int getCurrentlyCraftingAmount(BlockPos destNode, ItemStack stack, ItemEqualityType... equalityTypes) {
return this.getCurrentlyCrafting(destNode).stream()
.filter(p -> p.getLeft().equals(destNode) && ItemEqualityType.compareItems(p.getRight(), stack, equalityTypes))
.mapToInt(p -> p.getRight().getCount()).sum();
}
public List<Pair<BlockPos, ItemStack>> getAllCraftables(BlockPos node) {
if (!this.isNode(node))
return Collections.emptyList();
this.startProfile("get_all_craftables");
List<Pair<BlockPos, ItemStack>> craftables = new ArrayList<>();
for (BlockPos dest : this.getOrderedNetworkNodes(node)) {
if (!this.world.isBlockLoaded(dest))
continue;
PipeTileEntity pipe = this.getPipe(dest);
for (ItemStack stack : pipe.getAllCraftables())
craftables.add(Pair.of(pipe.getPos(), stack));
}
this.endProfile();
return craftables;
}
public int getCraftableAmount(BlockPos node, Consumer<ItemStack> unavailableConsumer, ItemStack stack, ItemEqualityType... equalityTypes) {
int total = 0;
for (Pair<BlockPos, ItemStack> pair : this.getAllCraftables(node)) {
if (!ItemEqualityType.compareItems(pair.getRight(), stack, equalityTypes))
continue;
if (!this.world.isBlockLoaded(pair.getLeft()))
continue;
PipeTileEntity pipe = this.getPipe(pair.getLeft());
if (pipe != null)
total += pipe.getCraftableAmount(unavailableConsumer, stack);
}
return total;
}
public List<NetworkLocation> getOrderedNetworkItems(BlockPos node) {
if (!this.isNode(node))
return Collections.emptyList();
@ -236,11 +324,11 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
if (!pipe.canNetworkSee())
continue;
for (Direction dir : Direction.values()) {
IItemHandler handler = pipe.getItemHandler(dir, null);
IItemHandler handler = pipe.getItemHandler(dir);
if (handler == null)
continue;
// check if this handler already exists (double-connected pipes, double chests etc.)
if (info.stream().anyMatch(l -> l.getItemHandler(this.world) == handler))
if (info.stream().anyMatch(l -> handler.equals(l.getItemHandler(this.world))))
continue;
NetworkLocation location = new NetworkLocation(dest, dir);
if (!location.isEmpty(this.world))
@ -263,9 +351,9 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
return this.networkLocks.get(pos);
}
public int getLockedAmount(BlockPos pos, ItemStack stack, ItemEqualityType... equalityTypes) {
public int getLockedAmount(BlockPos pos, ItemStack stack, NetworkLock ignoredLock, ItemEqualityType... equalityTypes) {
return this.getNetworkLocks(pos).stream()
.filter(l -> ItemEqualityType.compareItems(l.stack, stack, equalityTypes))
.filter(l -> !l.equals(ignoredLock) && ItemEqualityType.compareItems(l.stack, stack, equalityTypes))
.mapToInt(l -> l.stack.getCount()).sum();
}
@ -352,7 +440,9 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
return null;
}
private List<BlockPos> getOrderedNetworkNodes(BlockPos node) {
public List<BlockPos> getOrderedNetworkNodes(BlockPos node) {
if (!this.isNode(node))
return Collections.emptyList();
List<BlockPos> ret = this.nodeToConnectedNodes.get(node);
if (ret == null) {
this.startProfile("compile_connected_nodes");
@ -360,6 +450,7 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
// sort destinations first by their priority (eg trash pipes should be last)
// and then by their distance from the specified node
ret = Streams.stream(new BreadthFirstIterator<>(this.graph, node))
.filter(p -> this.getPipe(p) != null)
.sorted(Comparator.<BlockPos>comparingInt(p -> this.getPipe(p).getPriority()).reversed().thenComparing(paths::getWeight))
.collect(Collectors.toList());
this.nodeToConnectedNodes.put(node, ret);
@ -378,22 +469,21 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
this.endProfile();
}
public List<PipeItem> getItemsInPipe(BlockPos pos) {
public List<IPipeItem> getItemsInPipe(BlockPos pos) {
return this.pipeItems.get(pos);
}
private Stream<PipeItem> getPipeItemsOnTheWay(BlockPos goalInv) {
public Stream<IPipeItem> getPipeItemsOnTheWay(BlockPos goalInv) {
this.startProfile("get_pipe_items_on_the_way");
Stream<PipeItem> ret = this.pipeItems.values().stream().filter(i -> i.getDestInventory().equals(goalInv));
Stream<IPipeItem> ret = this.pipeItems.values().stream().filter(i -> i.getDestInventory().equals(goalInv));
this.endProfile();
return ret;
}
public int getItemsOnTheWay(BlockPos goalInv, ItemStack type, ItemEqualityType... equalityTypes) {
// TODO pending auto-crafting requests should be marked as "on the way" here to allow over-sending prevention
return this.getPipeItemsOnTheWay(goalInv)
.filter(i -> type == null || ItemEqualityType.compareItems(i.stack, type, equalityTypes))
.mapToInt(i -> i.stack.getCount()).sum();
.filter(i -> type == null || ItemEqualityType.compareItems(i.getContent(), type, equalityTypes))
.mapToInt(i -> i.getItemsOnTheWay(goalInv)).sum();
}
@Override

View file

@ -113,8 +113,11 @@ public class PacketButton {
}),
CRAFT_TERMINAL_REQUEST((pos, data, player) -> {
CraftingTerminalTileEntity tile = Utility.getTileEntity(CraftingTerminalTileEntity.class, player.world, pos);
boolean all = data[0] > 0;
tile.requestCraftingItems(player, all);
tile.requestCraftingItems(player, data[0]);
}),
CANCEL_CRAFTING((pos, data, player) -> {
ItemTerminalTileEntity tile = Utility.getTileEntity(ItemTerminalTileEntity.class, player.world, pos);
tile.cancelCrafting();
});
public final TriConsumer<BlockPos, int[], PlayerEntity> action;

View file

@ -0,0 +1,88 @@
package de.ellpeck.prettypipes.packets;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.misc.FilterSlot;
import de.ellpeck.prettypipes.misc.ItemEqualityType;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import de.ellpeck.prettypipes.pipe.modules.craft.CraftingModuleContainer;
import de.ellpeck.prettypipes.pipe.modules.craft.CraftingModuleItem;
import de.ellpeck.prettypipes.terminal.CraftingTerminalTileEntity;
import de.ellpeck.prettypipes.terminal.ItemTerminalTileEntity;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.fml.network.NetworkEvent;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.ItemStackHandler;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class PacketCraftingModuleTransfer {
private List<ItemStack> inputs;
private List<ItemStack> outputs;
public PacketCraftingModuleTransfer(List<ItemStack> inputs, List<ItemStack> outputs) {
this.inputs = inputs;
this.outputs = outputs;
}
private PacketCraftingModuleTransfer() {
}
public static PacketCraftingModuleTransfer fromBytes(PacketBuffer buf) {
PacketCraftingModuleTransfer packet = new PacketCraftingModuleTransfer();
packet.inputs = new ArrayList<>();
for (int i = buf.readInt(); i > 0; i--)
packet.inputs.add(buf.readItemStack());
packet.outputs = new ArrayList<>();
for (int i = buf.readInt(); i > 0; i--)
packet.outputs.add(buf.readItemStack());
return packet;
}
public static void toBytes(PacketCraftingModuleTransfer packet, PacketBuffer buf) {
buf.writeInt(packet.inputs.size());
for (ItemStack stack : packet.inputs)
buf.writeItemStack(stack);
buf.writeInt(packet.outputs.size());
for (ItemStack stack : packet.outputs)
buf.writeItemStack(stack);
}
@SuppressWarnings("Convert2Lambda")
public static void onMessage(PacketCraftingModuleTransfer message, Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(new Runnable() {
@Override
public void run() {
PlayerEntity player = ctx.get().getSender();
if (player != null && player.openContainer instanceof CraftingModuleContainer) {
CraftingModuleContainer container = (CraftingModuleContainer) player.openContainer;
copy(container.input, message.inputs);
copy(container.output, message.outputs);
container.modified = true;
container.detectAndSendChanges();
}
}
});
ctx.get().setPacketHandled(true);
}
private static void copy(ItemStackHandler container, List<ItemStack> contents) {
for (int i = 0; i < container.getSlots(); i++)
container.setStackInSlot(i, ItemStack.EMPTY);
for (ItemStack stack : contents)
ItemHandlerHelper.insertItem(container, stack, false);
}
}

View file

@ -23,6 +23,7 @@ public final class PacketHandler {
network.registerMessage(2, PacketNetworkItems.class, PacketNetworkItems::toBytes, PacketNetworkItems::fromBytes, PacketNetworkItems::onMessage);
network.registerMessage(3, PacketRequest.class, PacketRequest::toBytes, PacketRequest::fromBytes, PacketRequest::onMessage);
network.registerMessage(4, PacketGhostSlot.class, PacketGhostSlot::toBytes, PacketGhostSlot::fromBytes, PacketGhostSlot::onMessage);
network.registerMessage(5, PacketCraftingModuleTransfer.class, PacketCraftingModuleTransfer::toBytes, PacketCraftingModuleTransfer::fromBytes, PacketCraftingModuleTransfer::onMessage);
}
public static void sendToAllLoaded(World world, BlockPos pos, Object message) {

View file

@ -1,6 +1,7 @@
package de.ellpeck.prettypipes.packets;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.pipe.IPipeItem;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import de.ellpeck.prettypipes.network.PipeItem;
import net.minecraft.client.Minecraft;
@ -16,7 +17,7 @@ public class PacketItemEnterPipe {
private BlockPos tilePos;
private CompoundNBT item;
public PacketItemEnterPipe(BlockPos tilePos, PipeItem item) {
public PacketItemEnterPipe(BlockPos tilePos, IPipeItem item) {
this.tilePos = tilePos;
this.item = item.serializeNBT();
}
@ -45,7 +46,7 @@ public class PacketItemEnterPipe {
Minecraft mc = Minecraft.getInstance();
if (mc.world == null)
return;
PipeItem item = new PipeItem(message.item);
IPipeItem item = IPipeItem.load(message.item);
PipeTileEntity pipe = Utility.getTileEntity(PipeTileEntity.class, mc.world, message.tilePos);
if (pipe != null)
pipe.getItems().add(item);

View file

@ -22,9 +22,13 @@ import java.util.function.Supplier;
public class PacketNetworkItems {
private List<ItemStack> items;
private List<ItemStack> craftables;
private List<ItemStack> currentlyCrafting;
public PacketNetworkItems(List<ItemStack> items) {
public PacketNetworkItems(List<ItemStack> items, List<ItemStack> craftables, List<ItemStack> currentlyCrafting) {
this.items = items;
this.craftables = craftables;
this.currentlyCrafting = currentlyCrafting;
}
private PacketNetworkItems() {
@ -39,6 +43,12 @@ public class PacketNetworkItems {
stack.setCount(buf.readVarInt());
client.items.add(stack);
}
client.craftables = new ArrayList<>();
for (int i = buf.readVarInt(); i > 0; i--)
client.craftables.add(buf.readItemStack());
client.currentlyCrafting = new ArrayList<>();
for (int i = buf.readVarInt(); i > 0; i--)
client.currentlyCrafting.add(buf.readItemStack());
return client;
}
@ -50,6 +60,12 @@ public class PacketNetworkItems {
buf.writeItemStack(copy);
buf.writeVarInt(stack.getCount());
}
buf.writeVarInt(packet.craftables.size());
for (ItemStack stack : packet.craftables)
buf.writeItemStack(stack);
buf.writeVarInt(packet.currentlyCrafting.size());
for (ItemStack stack : packet.currentlyCrafting)
buf.writeItemStack(stack);
}
@SuppressWarnings("Convert2Lambda")
@ -59,7 +75,7 @@ public class PacketNetworkItems {
public void run() {
Minecraft mc = Minecraft.getInstance();
if (mc.currentScreen instanceof ItemTerminalGui)
((ItemTerminalGui) mc.currentScreen).updateItemList(message.items);
((ItemTerminalGui) mc.currentScreen).updateItemList(message.items, message.craftables, message.currentlyCrafting);
}
});
ctx.get().setPacketHandled(true);

View file

@ -1,26 +1,18 @@
package de.ellpeck.prettypipes.pipe;
import de.ellpeck.prettypipes.network.PipeItem;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.items.IItemHandler;
public interface IPipeConnectable {
ConnectionType getConnectionType(World world, BlockPos pipePos, Direction direction);
ConnectionType getConnectionType(BlockPos pipePos, Direction direction);
default IItemHandler getItemHandler(World world, BlockPos pipePos, Direction direction, PipeItem item) {
return null;
default ItemStack insertItem(BlockPos pipePos, Direction direction, ItemStack stack, boolean simulate) {
return stack;
}
default ItemStack insertItem(World world, BlockPos pipePos, Direction direction, PipeItem item) {
return item.stack;
}
default boolean allowsModules(World world, BlockPos pipePos, Direction direction) {
default boolean allowsModules(BlockPos pipePos, Direction direction) {
return false;
}
}

View file

@ -0,0 +1,58 @@
package de.ellpeck.prettypipes.pipe;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.mojang.blaze3d.matrix.MatrixStack;
import de.ellpeck.prettypipes.network.NetworkEdge;
import de.ellpeck.prettypipes.network.PipeItem;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.INBTSerializable;
import org.jgrapht.GraphPath;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.function.BiFunction;
public interface IPipeItem extends INBTSerializable<CompoundNBT> {
Map<ResourceLocation, BiFunction<ResourceLocation, CompoundNBT, IPipeItem>> TYPES = new HashMap<>(
Collections.singletonMap(PipeItem.TYPE, PipeItem::new));
ItemStack getContent();
void setDestination(BlockPos startInventory, BlockPos destInventory, GraphPath<BlockPos, NetworkEdge> path);
void updateInPipe(PipeTileEntity currPipe);
void drop(World world, ItemStack stack);
BlockPos getDestPipe();
BlockPos getCurrentPipe();
BlockPos getDestInventory();
int getItemsOnTheWay(BlockPos goalInv);
@OnlyIn(Dist.CLIENT)
void render(PipeTileEntity tile, MatrixStack matrixStack, Random random, float partialTicks, int light, int overlay, IRenderTypeBuffer buffer);
static IPipeItem load(CompoundNBT nbt) {
// TODO legacy compat, remove eventually
if (!nbt.contains("type"))
nbt.putString("type", PipeItem.TYPE.toString());
ResourceLocation type = new ResourceLocation(nbt.getString("type"));
BiFunction<ResourceLocation, CompoundNBT, IPipeItem> func = TYPES.get(type);
return func != null ? func.apply(type, nbt) : null;
}
}

View file

@ -1,6 +1,8 @@
package de.ellpeck.prettypipes.pipe;
import com.google.common.collect.ImmutableMap;
import com.mojang.datafixers.types.Func;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.items.IModule;
import de.ellpeck.prettypipes.network.PipeItem;
@ -10,6 +12,7 @@ import net.minecraft.block.material.Material;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.fluid.FluidState;
import net.minecraft.fluid.Fluids;
import net.minecraft.fluid.IFluidState;
import net.minecraft.item.BlockItemUseContext;
@ -24,6 +27,7 @@ import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.shapes.IBooleanFunction;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
@ -34,15 +38,19 @@ import net.minecraftforge.fml.network.NetworkHooks;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import org.apache.commons.lang3.mutable.MutableObject;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class PipeBlock extends ContainerBlock implements IPipeConnectable {
public class PipeBlock extends ContainerBlock {
public static final Map<Direction, EnumProperty<ConnectionType>> DIRECTIONS = new HashMap<>();
private static final Map<BlockState, VoxelShape> SHAPE_CACHE = new HashMap<>();
private static final Map<Pair<BlockState, BlockState>, VoxelShape> SHAPE_CACHE = new HashMap<>();
private static final Map<Pair<BlockState, BlockState>, VoxelShape> COLL_SHAPE_CACHE = new HashMap<>();
private static final VoxelShape CENTER_SHAPE = makeCuboidShape(5, 5, 5, 11, 11, 11);
public static final Map<Direction, VoxelShape> DIR_SHAPES = ImmutableMap.<Direction, VoxelShape>builder()
.put(Direction.UP, makeCuboidShape(5, 10, 5, 11, 16, 11))
@ -55,7 +63,7 @@ public class PipeBlock extends ContainerBlock implements IPipeConnectable {
static {
for (Direction dir : Direction.values())
DIRECTIONS.put(dir, EnumProperty.create(dir.getName(), ConnectionType.class));
DIRECTIONS.put(dir, EnumProperty.create(dir.getName2(), ConnectionType.class));
}
public PipeBlock() {
@ -130,22 +138,46 @@ public class PipeBlock extends ContainerBlock implements IPipeConnectable {
}
@Override
public boolean isNormalCube(BlockState state, IBlockReader worldIn, BlockPos pos) {
return false;
public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) {
return this.cacheAndGetShape(state, worldIn, pos, s -> s.getShape(worldIn, pos, context), SHAPE_CACHE, null);
}
@Override
public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) {
VoxelShape shape = SHAPE_CACHE.get(state);
if (shape != null)
return shape;
public VoxelShape getCollisionShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) {
return this.cacheAndGetShape(state, worldIn, pos, s -> s.getCollisionShape(worldIn, pos, context), COLL_SHAPE_CACHE, s -> {
// make the shape a bit higher so we can jump up onto a higher block
MutableObject<VoxelShape> newShape = new MutableObject<>(VoxelShapes.empty());
s.forEachBox((x1, y1, z1, x2, y2, z2) -> newShape.setValue(VoxelShapes.combine(VoxelShapes.create(x1, y1, z1, x2, y2 + 3 / 16F, z2), newShape.getValue(), IBooleanFunction.OR)));
return newShape.getValue().simplify();
});
}
shape = CENTER_SHAPE;
for (Map.Entry<Direction, EnumProperty<ConnectionType>> entry : DIRECTIONS.entrySet()) {
if (state.get(entry.getValue()).isConnected())
shape = VoxelShapes.or(shape, DIR_SHAPES.get(entry.getKey()));
private VoxelShape cacheAndGetShape(BlockState state, IBlockReader worldIn, BlockPos pos, Function<BlockState, VoxelShape> coverShapeSelector, Map<Pair<BlockState, BlockState>, VoxelShape> cache, Function<VoxelShape, VoxelShape> shapeModifier) {
VoxelShape coverShape = null;
BlockState cover = null;
PipeTileEntity tile = Utility.getTileEntity(PipeTileEntity.class, worldIn, pos);
if (tile != null && tile.cover != null) {
cover = tile.cover;
// try catch since the block might expect to find itself at the position
try {
coverShape = coverShapeSelector.apply(cover);
} catch (Exception ignored) {
}
}
Pair<BlockState, BlockState> key = Pair.of(state, cover);
VoxelShape shape = cache.get(key);
if (shape == null) {
shape = CENTER_SHAPE;
for (Map.Entry<Direction, EnumProperty<ConnectionType>> entry : DIRECTIONS.entrySet()) {
if (state.get(entry.getValue()).isConnected())
shape = VoxelShapes.or(shape, DIR_SHAPES.get(entry.getKey()));
}
if (shapeModifier != null)
shape = shapeModifier.apply(shape);
if (coverShape != null)
shape = VoxelShapes.or(shape, coverShape);
cache.put(key, shape);
}
SHAPE_CACHE.put(state, shape);
return shape;
}
@ -170,16 +202,20 @@ public class PipeBlock extends ContainerBlock implements IPipeConnectable {
BlockPos offset = pos.offset(direction);
if (!world.isBlockLoaded(offset))
return ConnectionType.DISCONNECTED;
BlockState offState = world.getBlockState(offset);
Block block = offState.getBlock();
if (block instanceof IPipeConnectable)
return ((IPipeConnectable) block).getConnectionType(world, pos, direction);
Direction opposite = direction.getOpposite();
TileEntity tile = world.getTileEntity(offset);
if (tile != null) {
IItemHandler handler = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, direction.getOpposite()).orElse(null);
IPipeConnectable connectable = tile.getCapability(Registry.pipeConnectableCapability, opposite).orElse(null);
if (connectable != null)
return connectable.getConnectionType(pos, direction);
IItemHandler handler = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, opposite).orElse(null);
if (handler != null)
return ConnectionType.CONNECTED;
}
IItemHandler blockHandler = Utility.getBlockItemHandler(world, offset, opposite);
if (blockHandler != null)
return ConnectionType.CONNECTED;
BlockState offState = world.getBlockState(offset);
if (hasLegsTo(world, offState, offset, direction)) {
if (DIRECTIONS.values().stream().noneMatch(d -> state.get(d) == ConnectionType.LEGS))
return ConnectionType.LEGS;
@ -191,14 +227,15 @@ public class PipeBlock extends ContainerBlock implements IPipeConnectable {
if (state.getBlock() instanceof WallBlock || state.getBlock() instanceof FenceBlock)
return direction == Direction.DOWN;
if (state.getMaterial() == Material.ROCK || state.getMaterial() == Material.IRON)
return hasSolidSide(state, world, pos, direction.getOpposite());
return hasEnoughSolidSide(world, pos, direction.getOpposite());
return false;
}
public static void onStateChanged(World world, BlockPos pos, BlockState newState) {
// wait a few ticks before checking if we have to drop our modules, so that things like iron -> gold chest work
PipeTileEntity tile = Utility.getTileEntity(PipeTileEntity.class, world, pos);
if (tile != null && !tile.canHaveModules())
Utility.dropInventory(tile, tile.modules);
if (tile != null)
tile.moduleDropCheck = 5;
PipeNetwork network = PipeNetwork.get(world);
int connections = 0;
@ -226,12 +263,6 @@ public class PipeBlock extends ContainerBlock implements IPipeConnectable {
@Override
public void onReplaced(BlockState state, World worldIn, BlockPos pos, BlockState newState, boolean isMoving) {
if (state.getBlock() != newState.getBlock()) {
PipeTileEntity tile = Utility.getTileEntity(PipeTileEntity.class, worldIn, pos);
if (tile != null) {
Utility.dropInventory(tile, tile.modules);
for (PipeItem item : tile.getItems())
item.drop(worldIn);
}
PipeNetwork network = PipeNetwork.get(worldIn);
network.removeNode(pos);
network.onPipeChanged(pos, state);
@ -239,6 +270,19 @@ public class PipeBlock extends ContainerBlock implements IPipeConnectable {
}
}
@Override
public void onBlockHarvested(World worldIn, BlockPos pos, BlockState state, PlayerEntity player) {
PipeTileEntity tile = Utility.getTileEntity(PipeTileEntity.class, worldIn, pos);
if (tile != null) {
Utility.dropInventory(tile, tile.modules);
for (IPipeItem item : tile.getItems())
item.drop(worldIn, item.getContent());
if (tile.cover != null)
tile.removeCover(player, Hand.MAIN_HAND);
}
super.onBlockHarvested(worldIn, pos, state, player);
}
@Override
public boolean hasComparatorInputOverride(BlockState state) {
return true;
@ -262,12 +306,4 @@ public class PipeBlock extends ContainerBlock implements IPipeConnectable {
public BlockRenderType getRenderType(BlockState state) {
return BlockRenderType.MODEL;
}
@Override
public ConnectionType getConnectionType(World world, BlockPos pipePos, Direction direction) {
BlockState state = world.getBlockState(pipePos.offset(direction));
if (state.get(DIRECTIONS.get(direction.getOpposite())) == ConnectionType.BLOCKED)
return ConnectionType.BLOCKED;
return ConnectionType.CONNECTED;
}
}

View file

@ -1,17 +1,22 @@
package de.ellpeck.prettypipes.pipe;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.network.PipeItem;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BlockModelRenderer;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.model.ItemCameraTransforms;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.RenderTypeLookup;
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.data.EmptyModelData;
import java.util.Random;
@ -24,59 +29,30 @@ public class PipeRenderer extends TileEntityRenderer<PipeTileEntity> {
}
@Override
public void render(PipeTileEntity tile, float v, MatrixStack matrixStack, IRenderTypeBuffer iRenderTypeBuffer, int k, int i1) {
if (tile.getItems().isEmpty())
return;
matrixStack.push();
BlockPos tilePos = tile.getPos();
matrixStack.translate(-tilePos.getX(), -tilePos.getY(), -tilePos.getZ());
for (PipeItem item : tile.getItems()) {
public void render(PipeTileEntity tile, float partialTicks, MatrixStack matrixStack, IRenderTypeBuffer buffer, int light, int overlay) {
if (!tile.getItems().isEmpty()) {
matrixStack.push();
matrixStack.translate(
MathHelper.lerp(v, item.lastX, item.x),
MathHelper.lerp(v, item.lastY, item.y),
MathHelper.lerp(v, item.lastZ, item.z));
if (item.stack.getItem() instanceof BlockItem) {
float scale = 0.7F;
matrixStack.scale(scale, scale, scale);
matrixStack.translate(0, -0.2F, 0);
} else {
float scale = 0.45F;
matrixStack.scale(scale, scale, scale);
matrixStack.translate(0, -0.1F, 0);
}
this.random.setSeed(Item.getIdFromItem(item.stack.getItem()) + item.stack.getDamage());
int amount = this.getModelCount(item.stack);
for (int i = 0; i < amount; i++) {
BlockPos tilePos = tile.getPos();
matrixStack.translate(-tilePos.getX(), -tilePos.getY(), -tilePos.getZ());
for (IPipeItem item : tile.getItems()) {
matrixStack.push();
if (amount > 1) {
matrixStack.translate(
(this.random.nextFloat() * 2.0F - 1.0F) * 0.25F * 0.5F,
(this.random.nextFloat() * 2.0F - 1.0F) * 0.25F * 0.5F,
(this.random.nextFloat() * 2.0F - 1.0F) * 0.25F * 0.5F);
}
Minecraft.getInstance().getItemRenderer().renderItem(item.stack, ItemCameraTransforms.TransformType.GROUND, k, i1, matrixStack, iRenderTypeBuffer);
item.render(tile, matrixStack, this.random, partialTicks, light, overlay, buffer);
matrixStack.pop();
}
matrixStack.pop();
}
matrixStack.pop();
}
protected int getModelCount(ItemStack stack) {
int i = 1;
if (stack.getCount() > 48) {
i = 5;
} else if (stack.getCount() > 32) {
i = 4;
} else if (stack.getCount() > 16) {
i = 3;
} else if (stack.getCount() > 1) {
i = 2;
if (tile.cover != null) {
matrixStack.push();
BlockModelRenderer.enableCache();
for (RenderType layer : RenderType.getBlockRenderTypes()) {
if (!RenderTypeLookup.canRenderInLayer(tile.cover, layer))
continue;
ForgeHooksClient.setRenderLayer(layer);
Minecraft.getInstance().getBlockRendererDispatcher().renderBlock(tile.cover, matrixStack, buffer, light, overlay, EmptyModelData.INSTANCE);
}
ForgeHooksClient.setRenderLayer(null);
BlockModelRenderer.disableCache();
matrixStack.pop();
}
return i;
}
}

View file

@ -4,11 +4,12 @@ import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.items.IModule;
import de.ellpeck.prettypipes.network.PipeItem;
import de.ellpeck.prettypipes.network.NetworkLock;
import de.ellpeck.prettypipes.network.PipeNetwork;
import de.ellpeck.prettypipes.pipe.containers.MainPipeContainer;
import de.ellpeck.prettypipes.pressurizer.PressurizerTileEntity;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ChestBlock;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.Container;
@ -16,30 +17,40 @@ import net.minecraft.inventory.container.INamedContainerProvider;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SUpdateTileEntityPacket;
import net.minecraft.profiler.IProfiler;
import net.minecraft.tileentity.ChestTileEntity;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraftforge.common.util.Constants;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.items.wrapper.InvWrapper;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class PipeTileEntity extends TileEntity implements INamedContainerProvider, ITickableTileEntity {
public class PipeTileEntity extends TileEntity implements INamedContainerProvider, ITickableTileEntity, IPipeConnectable {
public final ItemStackHandler modules = new ItemStackHandler(3) {
@Override
@ -56,9 +67,15 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
return 1;
}
};
protected List<PipeItem> items;
public final Queue<NetworkLock> craftIngredientRequests = new LinkedList<>();
public final List<Pair<BlockPos, ItemStack>> craftResultRequests = new ArrayList<>();
public PressurizerTileEntity pressurizer;
public BlockState cover;
public int moduleDropCheck;
protected List<IPipeItem> items;
private int lastItemAmount;
private int priority;
private final LazyOptional<PipeTileEntity> lazyThis = LazyOptional.of(() -> this);
public PipeTileEntity() {
this(Registry.pipeTileEntity);
@ -71,12 +88,36 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
@Override
public CompoundNBT write(CompoundNBT compound) {
compound.put("modules", this.modules.serializeNBT());
compound.putInt("module_drop_check", this.moduleDropCheck);
compound.put("requests", Utility.serializeAll(this.craftIngredientRequests));
if (this.cover != null)
compound.put("cover", NBTUtil.writeBlockState(this.cover));
ListNBT results = new ListNBT();
for (Pair<BlockPos, ItemStack> triple : this.craftResultRequests) {
CompoundNBT nbt = new CompoundNBT();
nbt.putLong("dest_pipe", triple.getLeft().toLong());
nbt.put("item", triple.getRight().serializeNBT());
results.add(nbt);
}
compound.put("craft_results", results);
return super.write(compound);
}
@Override
public void read(CompoundNBT compound) {
this.modules.deserializeNBT(compound.getCompound("modules"));
this.moduleDropCheck = compound.getInt("module_drop_check");
this.cover = compound.contains("cover") ? NBTUtil.readBlockState(compound.getCompound("cover")) : null;
this.craftIngredientRequests.clear();
this.craftIngredientRequests.addAll(Utility.deserializeAll(compound.getList("requests", NBT.TAG_COMPOUND), NetworkLock::new));
this.craftResultRequests.clear();
ListNBT results = compound.getList("craft_results", NBT.TAG_COMPOUND);
for (int i = 0; i < results.size(); i++) {
CompoundNBT nbt = results.getCompound(i);
this.craftResultRequests.add(Pair.of(
BlockPos.fromLong(nbt.getLong("dest_pipe")),
ItemStack.read(nbt.getCompound("item"))));
}
super.read(compound);
}
@ -91,18 +132,34 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
@Override
public void handleUpdateTag(CompoundNBT nbt) {
this.read(nbt);
List<PipeItem> items = this.getItems();
List<IPipeItem> items = this.getItems();
items.clear();
items.addAll(Utility.deserializeAll(nbt.getList("items", NBT.TAG_COMPOUND), PipeItem::new));
items.addAll(Utility.deserializeAll(nbt.getList("items", NBT.TAG_COMPOUND), IPipeItem::load));
}
@Override
public void onDataPacket(NetworkManager net, SUpdateTileEntityPacket pkt) {
this.read(pkt.getNbtCompound());
}
@Override
public void tick() {
// invalidate our pressurizer reference if it was removed
if (this.pressurizer != null && this.pressurizer.isRemoved())
this.pressurizer = null;
if (!this.world.isAreaLoaded(this.pos, 1))
return;
IProfiler profiler = this.world.getProfiler();
if (!this.world.isRemote) {
// drop modules here to give a bit of time for blocks to update (iron -> gold chest etc.)
if (this.moduleDropCheck > 0) {
this.moduleDropCheck--;
if (this.moduleDropCheck <= 0 && !this.canHaveModules())
Utility.dropInventory(this, this.modules);
}
profiler.startSection("ticking_modules");
int prio = 0;
Iterator<Pair<ItemStack, IModule>> modules = this.streamModules().iterator();
@ -120,7 +177,7 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
}
profiler.startSection("ticking_items");
List<PipeItem> items = this.getItems();
List<IPipeItem> items = this.getItems();
for (int i = items.size() - 1; i >= 0; i--)
items.get(i).updateInPipe(this);
if (items.size() != this.lastItemAmount) {
@ -130,23 +187,50 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
profiler.endSection();
}
public List<PipeItem> getItems() {
public List<IPipeItem> getItems() {
if (this.items == null)
this.items = PipeNetwork.get(this.world).getItemsInPipe(this.pos);
return this.items;
}
public void addNewItem(IPipeItem item) {
// an item might be re-routed from a previous location, but it should still count as a new item then
if (!this.getItems().contains(item))
this.getItems().add(item);
if (this.pressurizer != null)
this.pressurizer.pressurizeItem(item.getContent(), false);
}
public boolean isConnected(Direction dir) {
return this.getBlockState().get(PipeBlock.DIRECTIONS.get(dir)).isConnected();
}
public Pair<BlockPos, ItemStack> getAvailableDestinationOrConnectable(ItemStack stack, boolean force, boolean preventOversending) {
Pair<BlockPos, ItemStack> dest = this.getAvailableDestination(stack, force, preventOversending);
if (dest != null)
return dest;
// if there's no available destination, try inserting into terminals etc.
for (Direction dir : Direction.values()) {
IPipeConnectable connectable = this.getPipeConnectable(dir);
if (connectable == null)
continue;
ItemStack connectableRemain = connectable.insertItem(this.getPos(), dir, stack, true);
if (connectableRemain.getCount() != stack.getCount()) {
ItemStack inserted = stack.copy();
inserted.shrink(connectableRemain.getCount());
return Pair.of(this.getPos().offset(dir), inserted);
}
}
return null;
}
public Pair<BlockPos, ItemStack> getAvailableDestination(ItemStack stack, boolean force, boolean preventOversending) {
if (!this.canWork())
return null;
if (!force && this.streamModules().anyMatch(m -> !m.getRight().canAcceptItem(m.getLeft(), this, stack)))
return null;
for (Direction dir : Direction.values()) {
IItemHandler handler = this.getItemHandler(dir, null);
IItemHandler handler = this.getItemHandler(dir);
if (handler == null)
continue;
ItemStack remain = ItemHandlerHelper.insertItem(handler, stack, true);
@ -172,15 +256,21 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
if (toInsert.getCount() + onTheWaySame > maxAmount)
toInsert.setCount(maxAmount - onTheWaySame);
}
ItemStack copy = stack.copy();
copy.setCount(copy.getMaxStackSize());
// totalSpace will be the amount of items that fit into the attached container
int totalSpace = 0;
for (int i = 0; i < handler.getSlots(); i++) {
ItemStack copy = stack.copy();
int maxStackSize = copy.getMaxStackSize();
// if the container can store more than 64 items in this slot, then it's likely
// a barrel or similar, meaning that the slot limit matters more than the max stack size
int limit = handler.getSlotLimit(i);
if (limit > 64)
maxStackSize = limit;
copy.setCount(maxStackSize);
// this is an inaccurate check since it ignores the fact that some slots might
// have space for items of other types, but it'll be good enough for us
ItemStack left = handler.insertItem(i, copy, true);
totalSpace += copy.getMaxStackSize() - left.getCount();
totalSpace += maxStackSize - left.getCount();
}
// if the items on the way plus the items we're trying to move are too much, reduce
if (onTheWay + toInsert.getCount() > totalSpace)
@ -198,46 +288,65 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
return this.priority;
}
public float getItemSpeed() {
float speed = (float) this.streamModules().mapToDouble(m -> m.getRight().getItemSpeedIncrease(m.getLeft(), this)).sum();
return 0.05F + speed;
public float getItemSpeed(ItemStack stack) {
float moduleSpeed = (float) this.streamModules().mapToDouble(m -> m.getRight().getItemSpeedIncrease(m.getLeft(), this)).sum();
float pressureSpeed = this.pressurizer != null && this.pressurizer.pressurizeItem(stack, true) ? 0.45F : 0;
return 0.05F + moduleSpeed + pressureSpeed;
}
public boolean canWork() {
return this.streamModules().allMatch(m -> m.getRight().canPipeWork(m.getLeft(), this));
}
public IItemHandler getItemHandler(Direction dir, PipeItem item) {
public List<ItemStack> getAllCraftables() {
return this.streamModules()
.flatMap(m -> m.getRight().getAllCraftables(m.getLeft(), this).stream())
.collect(Collectors.toList());
}
public int getCraftableAmount(Consumer<ItemStack> unavailableConsumer, ItemStack stack) {
return this.streamModules()
.mapToInt(m -> m.getRight().getCraftableAmount(m.getLeft(), this, unavailableConsumer, stack))
.sum();
}
public ItemStack craft(BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack) {
Iterator<Pair<ItemStack, IModule>> modules = this.streamModules().iterator();
while (modules.hasNext()) {
Pair<ItemStack, IModule> module = modules.next();
stack = module.getRight().craft(module.getLeft(), this, destPipe, unavailableConsumer, stack);
if (stack.isEmpty())
break;
}
return stack;
}
public IItemHandler getItemHandler(Direction dir) {
IItemHandler handler = this.getNeighborCap(dir, CapabilityItemHandler.ITEM_HANDLER_CAPABILITY);
if (handler != null)
return handler;
return Utility.getBlockItemHandler(this.world, this.pos.offset(dir), dir.getOpposite());
}
public <T> T getNeighborCap(Direction dir, Capability<T> cap) {
if (!this.isConnected(dir))
return null;
BlockPos pos = this.pos.offset(dir);
TileEntity tile = this.world.getTileEntity(pos);
if (tile != null) {
// if we don't do this, then chests get really weird
if (tile instanceof ChestTileEntity) {
BlockState state = this.world.getBlockState(tile.getPos());
if (state.getBlock() instanceof ChestBlock)
return new InvWrapper(ChestBlock.func_226916_a_((ChestBlock) state.getBlock(), state, this.world, tile.getPos(), true));
}
IItemHandler handler = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, dir.getOpposite()).orElse(null);
if (handler != null)
return handler;
}
IPipeConnectable connectable = this.getPipeConnectable(dir);
if (connectable != null)
return connectable.getItemHandler(this.world, this.pos, dir, item);
if (tile != null)
return tile.getCapability(cap, dir.getOpposite()).orElse(null);
return null;
}
public IPipeConnectable getPipeConnectable(Direction dir) {
BlockState state = this.world.getBlockState(this.pos.offset(dir));
if (state.getBlock() instanceof IPipeConnectable)
return (IPipeConnectable) state.getBlock();
TileEntity tile = this.world.getTileEntity(this.pos.offset(dir));
if (tile != null)
return tile.getCapability(Registry.pipeConnectableCapability, dir.getOpposite()).orElse(null);
return null;
}
public boolean isConnectedInventory(Direction dir) {
return this.getItemHandler(dir, null) != null;
return this.getItemHandler(dir) != null;
}
public boolean canHaveModules() {
@ -245,7 +354,7 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
if (this.isConnectedInventory(dir))
return true;
IPipeConnectable connectable = this.getPipeConnectable(dir);
if (connectable != null && connectable.allowsModules(this.world, this.pos, dir))
if (connectable != null && connectable.allowsModules(this.pos, dir))
return true;
}
return false;
@ -266,10 +375,23 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
return builder.build();
}
public void removeCover(PlayerEntity player, Hand hand) {
if (this.world.isRemote)
return;
List<ItemStack> drops = Block.getDrops(this.cover, (ServerWorld) this.world, this.pos, null, player, player.getHeldItem(hand));
for (ItemStack drop : drops)
Block.spawnAsEntity(this.world, this.pos, drop);
this.cover = null;
}
@Override
public void remove() {
super.remove();
this.getItems().clear();
PipeNetwork network = PipeNetwork.get(this.world);
for (NetworkLock lock : this.craftIngredientRequests)
network.resolveNetworkLock(lock);
this.lazyThis.invalidate();
}
@Override
@ -282,4 +404,26 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
public Container createMenu(int window, PlayerInventory inv, PlayerEntity player) {
return new MainPipeContainer(Registry.pipeContainer, window, player, PipeTileEntity.this.pos);
}
@Override
@OnlyIn(Dist.CLIENT)
public AxisAlignedBB getRenderBoundingBox() {
// our render bounding box should always be the full block in case we're covered
return new AxisAlignedBB(this.pos);
}
@Override
public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
if (cap == Registry.pipeConnectableCapability)
return this.lazyThis.cast();
return LazyOptional.empty();
}
@Override
public ConnectionType getConnectionType(BlockPos pipePos, Direction direction) {
BlockState state = this.world.getBlockState(pipePos.offset(direction));
if (state.get(PipeBlock.DIRECTIONS.get(direction.getOpposite())) == ConnectionType.BLOCKED)
return ConnectionType.BLOCKED;
return ConnectionType.CONNECTED;
}
}

View file

@ -19,6 +19,7 @@ public abstract class AbstractPipeContainer<T extends IModule> extends Container
public final PipeTileEntity tile;
public final T module;
public final int moduleIndex;
public final ItemStack moduleStack;
public AbstractPipeContainer(@Nullable ContainerType<?> type, int id, PlayerEntity player, BlockPos pos, int moduleIndex) {
@ -26,6 +27,7 @@ public abstract class AbstractPipeContainer<T extends IModule> extends Container
this.tile = Utility.getTileEntity(PipeTileEntity.class, player.world, pos);
this.moduleStack = moduleIndex < 0 ? null : this.tile.modules.getStackInSlot(moduleIndex);
this.module = moduleIndex < 0 ? null : (T) this.moduleStack.getItem();
this.moduleIndex = moduleIndex;
// needs to be done here so transferStackInSlot works correctly, bleh
this.addSlots();

View file

@ -1,5 +1,6 @@
package de.ellpeck.prettypipes.pipe.containers;
import com.mojang.blaze3d.matrix.MatrixStack;
import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.items.IModule;
@ -20,7 +21,7 @@ import java.util.List;
public abstract class AbstractPipeGui<T extends AbstractPipeContainer<?>> extends ContainerScreen<T> {
private static final ResourceLocation TEXTURE = new ResourceLocation(PrettyPipes.ID, "textures/gui/pipe.png");
protected static final ResourceLocation TEXTURE = new ResourceLocation(PrettyPipes.ID, "textures/gui/pipe.png");
private final List<Tab> tabs = new ArrayList<>();
private final ItemStack[] lastItems = new ItemStack[this.container.tile.modules.getSlots()];
@ -98,27 +99,25 @@ public abstract class AbstractPipeGui<T extends AbstractPipeContainer<?>> extend
private void initTabs() {
this.tabs.clear();
this.tabs.add(new Tab(new ItemStack(Registry.pipeBlock), null, 0, -1));
this.tabs.add(new Tab(new ItemStack(Registry.pipeBlock), 0, -1));
for (int i = 0; i < this.container.tile.modules.getSlots(); i++) {
ItemStack stack = this.container.tile.modules.getStackInSlot(i);
if (stack.isEmpty())
continue;
IModule module = (IModule) stack.getItem();
if (module.hasContainer(stack, this.container.tile))
this.tabs.add(new Tab(stack, module, this.tabs.size(), i));
this.tabs.add(new Tab(stack, this.tabs.size(), i));
}
}
private class Tab {
private final ItemStack moduleStack;
private final IModule module;
private final int index;
private final int x;
private final int y;
public Tab(ItemStack moduleStack, IModule module, int tabIndex, int index) {
public Tab(ItemStack moduleStack, int tabIndex, int index) {
this.moduleStack = moduleStack;
this.module = module;
this.index = index;
this.x = AbstractPipeGui.this.guiLeft + 5 + tabIndex * 28;
this.y = AbstractPipeGui.this.guiTop;
@ -129,7 +128,7 @@ public abstract class AbstractPipeGui<T extends AbstractPipeContainer<?>> extend
int v = 0;
int height = 30;
int itemOffset = 9;
if (this.module == AbstractPipeGui.this.container.module) {
if (this.index == AbstractPipeGui.this.container.moduleIndex) {
y = 0;
v = 30;
height = 32;
@ -148,7 +147,7 @@ public abstract class AbstractPipeGui<T extends AbstractPipeContainer<?>> extend
}
private boolean onClicked(double mouseX, double mouseY, int button) {
if (this.module == AbstractPipeGui.this.container.module)
if (this.index == AbstractPipeGui.this.container.moduleIndex)
return false;
if (button != 0)
return false;

View file

@ -15,11 +15,11 @@ import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
public class FilterModifierModule extends ModuleItem {
public class FilterModifierModuleItem extends ModuleItem {
public final ItemEqualityType type;
public FilterModifierModule(String name, ItemEqualityType type) {
public FilterModifierModuleItem(String name, ItemEqualityType type) {
super(name);
this.type = type;
this.setRegistryName(name);

View file

@ -0,0 +1,31 @@
package de.ellpeck.prettypipes.pipe.modules;
import de.ellpeck.prettypipes.items.IModule;
import de.ellpeck.prettypipes.items.ModuleItem;
import de.ellpeck.prettypipes.items.ModuleTier;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import net.minecraft.item.ItemStack;
public class HighPriorityModuleItem extends ModuleItem {
private final int priority;
public HighPriorityModuleItem(String name, ModuleTier tier) {
super(name);
this.priority = tier.forTier(50, 100, 200);
}
@Override
public int getPriority(ItemStack module, PipeTileEntity tile) {
return this.priority;
}
@Override
public boolean isCompatible(ItemStack module, PipeTileEntity tile, IModule other) {
return !(other instanceof HighPriorityModuleItem) && !(other instanceof LowPriorityModuleItem);
}
@Override
public boolean hasContainer(ItemStack module, PipeTileEntity tile) {
return false;
}
}

View file

@ -21,7 +21,7 @@ public class LowPriorityModuleItem extends ModuleItem {
@Override
public boolean isCompatible(ItemStack module, PipeTileEntity tile, IModule other) {
return !(other instanceof LowPriorityModuleItem);
return !(other instanceof LowPriorityModuleItem) && !(other instanceof HighPriorityModuleItem);
}
@Override

View file

@ -0,0 +1,51 @@
package de.ellpeck.prettypipes.pipe.modules.craft;
import de.ellpeck.prettypipes.misc.FilterSlot;
import de.ellpeck.prettypipes.pipe.containers.AbstractPipeContainer;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.items.ItemStackHandler;
public class CraftingModuleContainer extends AbstractPipeContainer<CraftingModuleItem> {
public ItemStackHandler input;
public ItemStackHandler output;
public boolean modified;
public CraftingModuleContainer(ContainerType<?> type, int id, PlayerEntity player, BlockPos pos, int moduleIndex) {
super(type, id, player, pos, moduleIndex);
}
@Override
protected void addSlots() {
this.input = this.module.getInput(this.moduleStack);
for (int i = 0; i < this.input.getSlots(); i++) {
this.addSlot(new FilterSlot(this.input, i, (176 - this.module.inputSlots * 18) / 2 + 1 + i % 9 * 18, 17 + 32 + i / 9 * 18, false) {
@Override
public void onSlotChanged() {
super.onSlotChanged();
CraftingModuleContainer.this.modified = true;
}
});
}
this.output = this.module.getOutput(this.moduleStack);
for (int i = 0; i < this.output.getSlots(); i++) {
this.addSlot(new FilterSlot(this.output, i, (176 - this.module.outputSlots * 18) / 2 + 1 + i % 9 * 18, 85 + i / 9 * 18, false) {
@Override
public void onSlotChanged() {
super.onSlotChanged();
CraftingModuleContainer.this.modified = true;
}
});
}
}
@Override
public void onContainerClosed(PlayerEntity playerIn) {
super.onContainerClosed(playerIn);
if (this.modified)
this.module.save(this.input, this.output, this.moduleStack);
}
}

View file

@ -0,0 +1,19 @@
package de.ellpeck.prettypipes.pipe.modules.craft;
import com.mojang.blaze3d.matrix.MatrixStack;
import de.ellpeck.prettypipes.pipe.containers.AbstractPipeGui;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.text.ITextComponent;
public class CraftingModuleGui extends AbstractPipeGui<CraftingModuleContainer> {
public CraftingModuleGui(CraftingModuleContainer screenContainer, PlayerInventory inv, ITextComponent titleIn) {
super(screenContainer, inv, titleIn);
}
@Override
protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) {
super.drawGuiContainerBackgroundLayer(partialTicks, mouseX, mouseY);
this.getMinecraft().getTextureManager().bindTexture(TEXTURE);
this.blit(this.guiLeft + 176 / 2 - 16 / 2, this.guiTop + 32 + 18 * 2, 176, 80, 16, 16);
}
}

View file

@ -0,0 +1,227 @@
package de.ellpeck.prettypipes.pipe.modules.craft;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.items.IModule;
import de.ellpeck.prettypipes.items.ModuleItem;
import de.ellpeck.prettypipes.items.ModuleTier;
import de.ellpeck.prettypipes.misc.ItemEqualityType;
import de.ellpeck.prettypipes.misc.ItemFilter;
import de.ellpeck.prettypipes.network.NetworkLocation;
import de.ellpeck.prettypipes.network.NetworkLock;
import de.ellpeck.prettypipes.network.PipeNetwork;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import de.ellpeck.prettypipes.pipe.containers.AbstractPipeContainer;
import de.ellpeck.prettypipes.terminal.CraftingTerminalTileEntity;
import de.ellpeck.prettypipes.terminal.ItemTerminalTileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.items.ItemStackHandler;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class CraftingModuleItem extends ModuleItem {
public final int inputSlots;
public final int outputSlots;
private final int speed;
public CraftingModuleItem(String name, ModuleTier tier) {
super(name);
this.inputSlots = tier.forTier(1, 4, 9);
this.outputSlots = tier.forTier(1, 2, 4);
this.speed = tier.forTier(20, 10, 5);
}
@Override
public boolean isCompatible(ItemStack module, PipeTileEntity tile, IModule other) {
return true;
}
@Override
public boolean hasContainer(ItemStack module, PipeTileEntity tile) {
return true;
}
@Override
public AbstractPipeContainer<?> getContainer(ItemStack module, PipeTileEntity tile, int windowId, PlayerInventory inv, PlayerEntity player, int moduleIndex) {
return new CraftingModuleContainer(Registry.craftingModuleContainer, windowId, player, tile.getPos(), moduleIndex);
}
@Override
public boolean canNetworkSee(ItemStack module, PipeTileEntity tile) {
return false;
}
@Override
public boolean canAcceptItem(ItemStack module, PipeTileEntity tile, ItemStack stack) {
return false;
}
@Override
public void tick(ItemStack module, PipeTileEntity tile) {
if (tile.getWorld().getGameTime() % this.speed != 0 || !tile.canWork())
return;
PipeNetwork network = PipeNetwork.get(tile.getWorld());
// process crafting ingredient requests
if (!tile.craftIngredientRequests.isEmpty()) {
network.startProfile("crafting_ingredients");
NetworkLock request = tile.craftIngredientRequests.peek();
ItemEqualityType[] equalityTypes = ItemFilter.getEqualityTypes(tile);
Pair<BlockPos, ItemStack> dest = tile.getAvailableDestination(request.stack, true, true);
if (dest != null) {
ItemStack requestRemain = network.requestExistingItem(request.location, tile.getPos(), dest.getLeft(), request, dest.getRight(), equalityTypes);
network.resolveNetworkLock(request);
tile.craftIngredientRequests.remove();
// if we couldn't fit all items into the destination, create another request for the rest
ItemStack remain = request.stack.copy();
remain.shrink(dest.getRight().getCount() - requestRemain.getCount());
if (!remain.isEmpty()) {
NetworkLock remainRequest = new NetworkLock(request.location, remain);
tile.craftIngredientRequests.add(remainRequest);
network.createNetworkLock(remainRequest);
}
}
network.endProfile();
}
// pull requested crafting results from the network once they are stored
if (!tile.craftResultRequests.isEmpty()) {
network.startProfile("crafting_results");
List<NetworkLocation> items = network.getOrderedNetworkItems(tile.getPos());
ItemEqualityType[] equalityTypes = ItemFilter.getEqualityTypes(tile);
for (Pair<BlockPos, ItemStack> request : tile.craftResultRequests) {
ItemStack remain = request.getRight().copy();
PipeTileEntity destPipe = network.getPipe(request.getLeft());
if (destPipe != null) {
Pair<BlockPos, ItemStack> dest = destPipe.getAvailableDestinationOrConnectable(remain, true, true);
if (dest == null)
continue;
for (NetworkLocation item : items) {
ItemStack requestRemain = network.requestExistingItem(item, request.getLeft(), dest.getLeft(), null, dest.getRight(), equalityTypes);
remain.shrink(dest.getRight().getCount() - requestRemain.getCount());
if (remain.isEmpty())
break;
}
if (remain.getCount() != request.getRight().getCount()) {
tile.craftResultRequests.remove(request);
// if we couldn't pull everything, log a new request
if (!remain.isEmpty())
tile.craftResultRequests.add(Pair.of(request.getLeft(), remain));
network.endProfile();
return;
}
}
}
network.endProfile();
}
}
@Override
public List<ItemStack> getAllCraftables(ItemStack module, PipeTileEntity tile) {
List<ItemStack> ret = new ArrayList<>();
ItemStackHandler output = this.getOutput(module);
for (int i = 0; i < output.getSlots(); i++) {
ItemStack stack = output.getStackInSlot(i);
if (!stack.isEmpty())
ret.add(stack);
}
return ret;
}
@Override
public int getCraftableAmount(ItemStack module, PipeTileEntity tile, Consumer<ItemStack> unavailableConsumer, ItemStack stack) {
PipeNetwork network = PipeNetwork.get(tile.getWorld());
List<NetworkLocation> items = network.getOrderedNetworkItems(tile.getPos());
ItemEqualityType[] equalityTypes = ItemFilter.getEqualityTypes(tile);
ItemStackHandler input = this.getInput(module);
int craftable = 0;
ItemStackHandler output = this.getOutput(module);
for (int i = 0; i < output.getSlots(); i++) {
ItemStack out = output.getStackInSlot(i);
if (!out.isEmpty() && ItemEqualityType.compareItems(out, stack, equalityTypes)) {
// figure out how many crafting operations we can actually do with the input items we have in the network
int availableCrafts = CraftingTerminalTileEntity.getAvailableCrafts(tile, input.getSlots(), input::getStackInSlot, k -> true, s -> items, unavailableConsumer, equalityTypes);
if (availableCrafts > 0)
craftable += out.getCount() * availableCrafts;
}
}
return craftable;
}
@Override
public ItemStack craft(ItemStack module, PipeTileEntity tile, BlockPos destPipe, Consumer<ItemStack> unavailableConsumer, ItemStack stack) {
// check if we can craft the required amount of items
int craftableAmount = this.getCraftableAmount(module, tile, unavailableConsumer, stack);
if (craftableAmount <= 0)
return stack;
PipeNetwork network = PipeNetwork.get(tile.getWorld());
List<NetworkLocation> items = network.getOrderedNetworkItems(tile.getPos());
ItemEqualityType[] equalityTypes = ItemFilter.getEqualityTypes(tile);
int resultAmount = this.getResultAmountPerCraft(module, stack, equalityTypes);
int requiredCrafts = MathHelper.ceil(stack.getCount() / (float) resultAmount);
int toCraft = Math.min(craftableAmount, requiredCrafts);
ItemStackHandler input = this.getInput(module);
for (int i = 0; i < input.getSlots(); i++) {
ItemStack in = input.getStackInSlot(i);
if (in.isEmpty())
continue;
ItemStack copy = in.copy();
copy.setCount(in.getCount() * toCraft);
Pair<List<NetworkLock>, ItemStack> ret = ItemTerminalTileEntity.requestItemLater(tile.getWorld(), tile.getPos(), items, unavailableConsumer, copy, equalityTypes);
tile.craftIngredientRequests.addAll(ret.getLeft());
}
ItemStack remain = stack.copy();
remain.shrink(resultAmount * toCraft);
ItemStack result = stack.copy();
result.shrink(remain.getCount());
tile.craftResultRequests.add(Pair.of(destPipe, result));
return remain;
}
public ItemStackHandler getInput(ItemStack module) {
ItemStackHandler handler = new ItemStackHandler(this.inputSlots);
if (module.hasTag())
handler.deserializeNBT(module.getTag().getCompound("input"));
return handler;
}
public ItemStackHandler getOutput(ItemStack module) {
ItemStackHandler handler = new ItemStackHandler(this.outputSlots);
if (module.hasTag())
handler.deserializeNBT(module.getTag().getCompound("output"));
return handler;
}
public void save(ItemStackHandler input, ItemStackHandler output, ItemStack module) {
CompoundNBT tag = module.getOrCreateTag();
if (input != null)
tag.put("input", input.serializeNBT());
if (output != null)
tag.put("output", output.serializeNBT());
}
private int getResultAmountPerCraft(ItemStack module, ItemStack stack, ItemEqualityType... equalityTypes) {
ItemStackHandler output = this.getOutput(module);
int resultAmount = 0;
for (int i = 0; i < output.getSlots(); i++) {
ItemStack out = output.getStackInSlot(i);
if (ItemEqualityType.compareItems(stack, out, equalityTypes))
resultAmount += out.getCount();
}
return resultAmount;
}
}

View file

@ -39,7 +39,7 @@ public class ExtractionModuleItem extends ModuleItem {
PipeNetwork network = PipeNetwork.get(tile.getWorld());
for (Direction dir : Direction.values()) {
IItemHandler handler = tile.getItemHandler(dir, null);
IItemHandler handler = tile.getItemHandler(dir);
if (handler == null)
continue;
for (int j = 0; j < handler.getSlots(); j++) {
@ -48,7 +48,7 @@ public class ExtractionModuleItem extends ModuleItem {
continue;
if (!filter.isAllowed(stack))
continue;
ItemStack remain = network.tryInsertItem(tile.getPos(), tile.getPos().offset(dir), stack, this.preventOversending);
ItemStack remain = network.routeItem(tile.getPos(), tile.getPos().offset(dir), stack, this.preventOversending);
if (remain.getCount() != stack.getCount()) {
handler.extractItem(j, stack.getCount() - remain.getCount(), false);
return;

View file

@ -0,0 +1,38 @@
package de.ellpeck.prettypipes.pipe.modules.filter;
import de.ellpeck.prettypipes.misc.FilterSlot;
import de.ellpeck.prettypipes.misc.ItemFilter;
import de.ellpeck.prettypipes.pipe.containers.AbstractPipeContainer;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.inventory.container.Slot;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.items.ItemStackHandler;
public class FilterIncreaseModuleContainer extends AbstractPipeContainer<FilterIncreaseModuleItem> implements ItemFilter.IFilteredContainer {
public ItemFilter filter;
public FilterIncreaseModuleContainer(ContainerType<?> type, int id, PlayerEntity player, BlockPos pos, int moduleIndex) {
super(type, id, player, pos, moduleIndex);
}
@Override
protected void addSlots() {
this.filter = new ItemFilter(18, this.moduleStack, this.tile);
this.filter.canModifyWhitelist = false;
for (Slot slot : this.filter.getSlots(8, 49))
this.addSlot(slot);
}
@Override
public void onContainerClosed(PlayerEntity playerIn) {
super.onContainerClosed(playerIn);
this.filter.save();
}
@Override
public ItemFilter getFilter() {
return this.filter;
}
}

View file

@ -0,0 +1,11 @@
package de.ellpeck.prettypipes.pipe.modules.filter;
import de.ellpeck.prettypipes.pipe.containers.AbstractPipeGui;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.text.ITextComponent;
public class FilterIncreaseModuleGui extends AbstractPipeGui<FilterIncreaseModuleContainer> {
public FilterIncreaseModuleGui(FilterIncreaseModuleContainer screenContainer, PlayerInventory inv, ITextComponent titleIn) {
super(screenContainer, inv, titleIn);
}
}

View file

@ -0,0 +1,35 @@
package de.ellpeck.prettypipes.pipe.modules.filter;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.items.IModule;
import de.ellpeck.prettypipes.items.ModuleItem;
import de.ellpeck.prettypipes.misc.ItemFilter;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import de.ellpeck.prettypipes.pipe.containers.AbstractPipeContainer;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.ItemStackHandler;
public class FilterIncreaseModuleItem extends ModuleItem {
public FilterIncreaseModuleItem(String name) {
super(name);
this.setRegistryName(name);
}
@Override
public boolean isCompatible(ItemStack module, PipeTileEntity tile, IModule other) {
return true;
}
@Override
public boolean hasContainer(ItemStack module, PipeTileEntity tile) {
return true;
}
@Override
public AbstractPipeContainer<?> getContainer(ItemStack module, PipeTileEntity tile, int windowId, PlayerInventory inv, PlayerEntity player, int moduleIndex) {
return new FilterIncreaseModuleContainer(Registry.filterIncreaseModuleContainer, windowId, player, tile.getPos(), moduleIndex);
}
}

View file

@ -43,6 +43,7 @@ public class RetrievalModuleItem extends ModuleItem {
PipeNetwork network = PipeNetwork.get(tile.getWorld());
ItemFilter filter = new ItemFilter(this.filterSlots, module, tile);
ItemEqualityType[] equalityTypes = ItemFilter.getEqualityTypes(tile);
filter.isWhitelist = true;
// loop through filter to see which items to pull
for (int f = 0; f < filter.getSlots(); f++) {
@ -54,7 +55,10 @@ public class RetrievalModuleItem extends ModuleItem {
Pair<BlockPos, ItemStack> dest = tile.getAvailableDestination(copy, true, this.preventOversending);
if (dest == null)
continue;
if (network.requestItem(tile.getPos(), dest.getLeft(), dest.getRight(), filter.getEqualityTypes()).isEmpty())
ItemStack remain = dest.getRight().copy();
// are we already waiting for crafting results? If so, don't request those again
remain.shrink(network.getCurrentlyCraftingAmount(tile.getPos(), copy, equalityTypes));
if (network.requestItem(tile.getPos(), dest.getLeft(), remain, equalityTypes).isEmpty())
break;
}
}

View file

@ -0,0 +1,59 @@
package de.ellpeck.prettypipes.pressurizer;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.pipe.ConnectionType;
import de.ellpeck.prettypipes.pipe.IPipeConnectable;
import de.ellpeck.prettypipes.terminal.ItemTerminalTileEntity;
import net.minecraft.block.BlockRenderType;
import net.minecraft.block.BlockState;
import net.minecraft.block.ContainerBlock;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraftforge.fml.network.NetworkHooks;
import javax.annotation.Nullable;
import java.util.List;
public class PressurizerBlock extends ContainerBlock {
public PressurizerBlock() {
super(Properties.create(Material.ROCK).hardnessAndResistance(3).sound(SoundType.STONE));
}
@Override
public ActionResultType onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult result) {
PressurizerTileEntity tile = Utility.getTileEntity(PressurizerTileEntity.class, worldIn, pos);
if (tile == null)
return ActionResultType.PASS;
if (!worldIn.isRemote)
NetworkHooks.openGui((ServerPlayerEntity) player, tile, pos);
return ActionResultType.SUCCESS;
}
@Override
public TileEntity createNewTileEntity(IBlockReader worldIn) {
return new PressurizerTileEntity();
}
@Override
public BlockRenderType getRenderType(BlockState state) {
return BlockRenderType.MODEL;
}
@Override
public void addInformation(ItemStack stack, @Nullable IBlockReader worldIn, List<ITextComponent> tooltip, ITooltipFlag flagIn) {
Utility.addTooltip(this.getRegistryName().getPath(), tooltip);
}
}

View file

@ -0,0 +1,38 @@
package de.ellpeck.prettypipes.pressurizer;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.terminal.ItemTerminalTileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nullable;
public class PressurizerContainer extends Container {
public final PressurizerTileEntity tile;
public PressurizerContainer(@Nullable ContainerType<?> type, int id, PlayerEntity player, BlockPos pos) {
super(type, id);
this.tile = Utility.getTileEntity(PressurizerTileEntity.class, player.world, pos);
for (int l = 0; l < 3; ++l)
for (int j1 = 0; j1 < 9; ++j1)
this.addSlot(new Slot(player.inventory, j1 + l * 9 + 9, 8 + j1 * 18, 55 + l * 18));
for (int i1 = 0; i1 < 9; ++i1)
this.addSlot(new Slot(player.inventory, i1, 8 + i1 * 18, 113));
}
@Override
public ItemStack transferStackInSlot(PlayerEntity player, int slotIndex) {
return Utility.transferStackInSlot(this, this::mergeItemStack, player, slotIndex, stack -> null);
}
@Override
public boolean canInteractWith(PlayerEntity playerIn) {
return true;
}
}

View file

@ -0,0 +1,44 @@
package de.ellpeck.prettypipes.pressurizer;
import com.mojang.blaze3d.matrix.MatrixStack;
import de.ellpeck.prettypipes.PrettyPipes;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.client.gui.widget.Widget;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
public class PressurizerGui extends ContainerScreen<PressurizerContainer> {
private static final ResourceLocation TEXTURE = new ResourceLocation(PrettyPipes.ID, "textures/gui/pressurizer.png");
public PressurizerGui(PressurizerContainer screenContainer, PlayerInventory inv, ITextComponent titleIn) {
super(screenContainer, inv, titleIn);
this.xSize = 176;
this.ySize = 137;
}
@Override
public void render(int mouseX, int mouseY, float partialTicks) {
this.renderBackground();
super.render(mouseX, mouseY, partialTicks);
// this.func_230459_a_(matrix, mouseX, mouseY);
if (mouseX >= this.guiLeft + 26 && mouseY >= this.guiTop + 22 && mouseX < this.guiLeft + 26 + 124 && mouseY < this.guiTop + 22 + 12)
this.renderTooltip(I18n.format("info." + PrettyPipes.ID + ".energy", this.container.tile.getEnergy(), this.container.tile.getMaxEnergy()), mouseX, mouseY);
}
@Override
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) {
this.font.drawString(this.playerInventory.getDisplayName().getString(), 8, this.ySize - 96 + 2, 4210752);
this.font.drawString(this.title.getString(), 8, 6, 4210752);
}
@Override
protected void drawGuiContainerBackgroundLayer(float partialTicks, int x, int y) {
this.getMinecraft().getTextureManager().bindTexture(TEXTURE);
this.blit(this.guiLeft, this.guiTop, 0, 0, 176, 137);
int energy = (int) (this.container.tile.getEnergyPercentage() * 124);
this.blit(this.guiLeft + 26, this.guiTop + 22, 0, 137, energy, 12);
}
}

View file

@ -0,0 +1,168 @@
package de.ellpeck.prettypipes.pressurizer;
import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.network.PipeNetwork;
import de.ellpeck.prettypipes.pipe.ConnectionType;
import de.ellpeck.prettypipes.pipe.IPipeConnectable;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import de.ellpeck.prettypipes.terminal.containers.ItemTerminalContainer;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.INamedContainerProvider;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SUpdateTileEntityPacket;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.EnergyStorage;
import net.minecraftforge.energy.IEnergyStorage;
import javax.annotation.Nullable;
public class PressurizerTileEntity extends TileEntity implements INamedContainerProvider, ITickableTileEntity, IPipeConnectable {
private final ModifiableEnergyStorage storage = new ModifiableEnergyStorage(64000, 512, 0);
private final LazyOptional<IEnergyStorage> lazyStorage = LazyOptional.of(() -> this.storage);
private final LazyOptional<IPipeConnectable> lazyThis = LazyOptional.of(() -> this);
private int lastEnergy;
public PressurizerTileEntity() {
super(Registry.pressurizerTileEntity);
}
public boolean pressurizeItem(ItemStack stack, boolean simulate) {
int amount = 100 * stack.getCount();
return this.storage.extractInternal(amount, simulate) >= amount;
}
public float getEnergyPercentage() {
return this.getEnergy() / (float) this.getMaxEnergy();
}
public int getEnergy() {
return this.storage.getEnergyStored();
}
public int getMaxEnergy() {
return this.storage.getMaxEnergyStored();
}
@Override
public CompoundNBT write(CompoundNBT compound) {
compound.putInt("energy", this.getEnergy());
return super.write(compound);
}
@Override
public void read(CompoundNBT nbt) {
this.storage.setEnergyStored(nbt.getInt("energy"));
super.read(nbt);
}
@Override
public CompoundNBT getUpdateTag() {
return this.write(new CompoundNBT());
}
@Override
public void handleUpdateTag(CompoundNBT tag) {
this.read(tag);
}
@Override
public void onDataPacket(NetworkManager net, SUpdateTileEntityPacket pkt) {
this.read(pkt.getNbtCompound());
}
@Override
public ITextComponent getDisplayName() {
return new TranslationTextComponent("container." + PrettyPipes.ID + ".pressurizer");
}
@Nullable
@Override
public Container createMenu(int window, PlayerInventory inv, PlayerEntity player) {
return new PressurizerContainer(Registry.pressurizerContainer, window, player, this.pos);
}
@Override
public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
if (cap == CapabilityEnergy.ENERGY) {
return this.lazyStorage.cast();
} else if (cap == Registry.pipeConnectableCapability) {
return this.lazyThis.cast();
} else {
return LazyOptional.empty();
}
}
@Override
public void remove() {
super.remove();
this.lazyStorage.invalidate();
this.lazyThis.invalidate();
}
@Override
public void tick() {
if (this.world.isRemote)
return;
// notify pipes in network about us
if (this.world.getGameTime() % 10 == 0) {
PipeNetwork network = PipeNetwork.get(this.world);
for (Direction dir : Direction.values()) {
BlockPos offset = this.pos.offset(dir);
for (BlockPos node : network.getOrderedNetworkNodes(offset)) {
if (!this.world.isBlockLoaded(node))
continue;
PipeTileEntity pipe = network.getPipe(node);
if (pipe != null)
pipe.pressurizer = this;
}
}
}
// send energy update
if (this.lastEnergy != this.storage.getEnergyStored() && this.world.getGameTime() % 10 == 0) {
this.lastEnergy = this.storage.getEnergyStored();
Utility.sendTileEntityToClients(this);
}
}
@Override
public ConnectionType getConnectionType(BlockPos pipePos, Direction direction) {
return ConnectionType.CONNECTED;
}
private static class ModifiableEnergyStorage extends EnergyStorage {
public ModifiableEnergyStorage(int capacity, int maxReceive, int maxExtract) {
super(capacity, maxReceive, maxExtract);
}
private void setEnergyStored(int energy) {
this.energy = energy;
}
private int extractInternal(int maxExtract, boolean simulate) {
int energyExtracted = Math.min(this.energy, maxExtract);
if (!simulate)
this.energy -= energyExtracted;
return energyExtracted;
}
}
}

View file

@ -1,16 +1,7 @@
package de.ellpeck.prettypipes.terminal;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.misc.ItemEqualityType;
import de.ellpeck.prettypipes.network.PipeItem;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.wrapper.CombinedInvWrapper;
import javax.annotation.Nullable;
@ -22,34 +13,4 @@ public class CraftingTerminalBlock extends ItemTerminalBlock {
return new CraftingTerminalTileEntity();
}
@Override
public ItemStack insertItem(World world, BlockPos pipePos, Direction direction, PipeItem item) {
BlockPos pos = pipePos.offset(direction);
CraftingTerminalTileEntity tile = Utility.getTileEntity(CraftingTerminalTileEntity.class, world, pos);
if (tile != null) {
ItemStack remain = item.stack;
int lowestSlot = -1;
do {
for (int i = 0; i < tile.craftItems.getSlots(); i++) {
ItemStack stack = tile.getRequestedCraftItem(i);
int count = tile.isGhostItem(i) ? 0 : stack.getCount();
if (!ItemHandlerHelper.canItemStacksStackRelaxed(stack, remain))
continue;
if (lowestSlot < 0 || !tile.isGhostItem(lowestSlot) && count < tile.getRequestedCraftItem(lowestSlot).getCount())
lowestSlot = i;
}
if (lowestSlot >= 0) {
ItemStack copy = remain.copy();
copy.setCount(1);
remain.shrink(1 - tile.craftItems.insertItem(lowestSlot, copy, false).getCount());
if (remain.isEmpty())
return ItemStack.EMPTY;
}
}
while (lowestSlot >= 0);
return ItemHandlerHelper.insertItemStacked(tile.items, remain, false);
}
return item.stack;
}
}

View file

@ -4,32 +4,37 @@ import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.misc.EquatableItemStack;
import de.ellpeck.prettypipes.misc.ItemEqualityType;
import de.ellpeck.prettypipes.network.NetworkItem;
import de.ellpeck.prettypipes.network.NetworkLocation;
import de.ellpeck.prettypipes.network.NetworkLock;
import de.ellpeck.prettypipes.network.PipeNetwork;
import de.ellpeck.prettypipes.packets.PacketGhostSlot;
import de.ellpeck.prettypipes.packets.PacketHandler;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import de.ellpeck.prettypipes.terminal.containers.CraftingTerminalContainer;
import de.ellpeck.prettypipes.terminal.containers.ItemTerminalContainer;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.Container;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.ItemStackHandler;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public class CraftingTerminalTileEntity extends ItemTerminalTileEntity {
@ -59,29 +64,38 @@ public class CraftingTerminalTileEntity extends ItemTerminalTileEntity {
public void setGhostItems(ListMultimap<Integer, ItemStack> stacks) {
this.updateItems();
items:
for (int i = 0; i < this.ghostItems.getSlots(); i++) {
List<ItemStack> items = stacks.get(i);
if (items.isEmpty()) {
this.ghostItems.setStackInSlot(i, ItemStack.EMPTY);
continue;
}
ItemStack toSet = items.get(0);
// if we have more than one item to choose from, we want to pick the one that we have most of in the system
if (items.size() > 1) {
// set the item into the ghost slot that already has a variant of itself available in the system
int highestAmount = 0;
for (ItemStack stack : items) {
EquatableItemStack equatable = new EquatableItemStack(stack);
NetworkItem network = this.networkItems.get(equatable);
if (network == null)
continue;
if (network.getLocations().stream().anyMatch(l -> l.getItemAmount(this.world, stack, ItemEqualityType.NBT) > 0)) {
this.ghostItems.setStackInSlot(i, stack);
continue items;
int amount = 0;
// check existing items
NetworkItem network = this.networkItems.get(new EquatableItemStack(stack));
if (network != null) {
amount = network.getLocations().stream()
.mapToInt(l -> l.getItemAmount(this.world, stack, ItemEqualityType.NBT))
.sum();
}
// check craftables
if (amount <= 0 && highestAmount <= 0) {
PipeTileEntity pipe = this.getConnectedPipe();
if (pipe != null)
amount = PipeNetwork.get(this.world).getCraftableAmount(pipe.getPos(), null, stack, ItemEqualityType.NBT);
}
if (amount > highestAmount) {
highestAmount = amount;
toSet = stack;
}
}
}
// if the ghost slot wasn't set, then we don't have the item in the system
// so just pick a random one to put into the slot
this.ghostItems.setStackInSlot(i, items.get(0));
this.ghostItems.setStackInSlot(i, toSet.copy());
}
if (!this.world.isRemote) {
@ -92,60 +106,29 @@ public class CraftingTerminalTileEntity extends ItemTerminalTileEntity {
}
}
public void requestCraftingItems(PlayerEntity player, boolean all) {
public void requestCraftingItems(PlayerEntity player, int maxAmount) {
PipeTileEntity pipe = this.getConnectedPipe();
if (pipe == null)
return;
PipeNetwork network = PipeNetwork.get(this.world);
network.startProfile("terminal_request_crafting");
this.updateItems();
// the highest amount we can craft with the items we have
int lowestAvailable = Integer.MAX_VALUE;
// this is the amount of items required for each ingredient when crafting ONE
Map<EquatableItemStack, MutableInt> requiredItems = new HashMap<>();
for (int i = 0; i < this.craftItems.getSlots(); i++) {
ItemStack requested = this.getRequestedCraftItem(i);
if (requested.isEmpty())
continue;
MutableInt amount = requiredItems.computeIfAbsent(new EquatableItemStack(requested), s -> new MutableInt());
amount.add(1);
int fit = requested.getMaxStackSize() - (this.isGhostItem(i) ? 0 : requested.getCount());
if (lowestAvailable > fit)
lowestAvailable = fit;
}
for (Map.Entry<EquatableItemStack, MutableInt> entry : requiredItems.entrySet()) {
EquatableItemStack stack = entry.getKey();
NetworkItem item = this.networkItems.get(stack);
// total amount of available items of this type
int available = 0;
if (item != null) {
for (NetworkLocation location : item.getLocations()) {
int amount = location.getItemAmount(this.world, stack.stack, ItemEqualityType.NBT);
if (amount <= 0)
continue;
amount -= network.getLockedAmount(location.getPos(), stack.stack, ItemEqualityType.NBT);
available += amount;
}
// divide the total by the amount required to get the amount that
// we have available for each crafting slot that contains this item
available /= entry.getValue().intValue();
if (available < lowestAvailable)
lowestAvailable = available;
} else {
lowestAvailable = 0;
}
if (available <= 0)
player.sendMessage(new TranslationTextComponent("info." + PrettyPipes.ID + ".not_found", stack.stack.getDisplayName()).setStyle(new Style().setColor(TextFormatting.RED)));
}
// get the amount of crafts that we can do
int lowestAvailable = getAvailableCrafts(pipe, this.craftItems.getSlots(), i -> ItemHandlerHelper.copyStackWithSize(this.getRequestedCraftItem(i), 1), this::isGhostItem, s -> {
NetworkItem item = this.networkItems.get(s);
return item != null ? item.getLocations() : Collections.emptyList();
}, onItemUnavailable(player), ItemEqualityType.NBT);
if (lowestAvailable > 0) {
// if we're only crafting one item, pretend we only have enough for one
if (!all)
lowestAvailable = 1;
// if we're limiting the amount, pretend we only have that amount available
if (maxAmount < lowestAvailable)
lowestAvailable = maxAmount;
for (int i = 0; i < this.craftItems.getSlots(); i++) {
ItemStack requested = this.getRequestedCraftItem(i);
if (requested.isEmpty())
continue;
requested = requested.copy();
requested.setCount(lowestAvailable);
this.requestItemImpl(requested);
this.requestItemImpl(requested, onItemUnavailable(player));
}
player.sendMessage(new TranslationTextComponent("info." + PrettyPipes.ID + ".sending_ingredients", lowestAvailable).setStyle(new Style().setColor(TextFormatting.GREEN)));
}
@ -174,4 +157,90 @@ public class CraftingTerminalTileEntity extends ItemTerminalTileEntity {
public Container createMenu(int window, PlayerInventory inv, PlayerEntity player) {
return new CraftingTerminalContainer(Registry.craftingTerminalContainer, window, player, this.pos);
}
@Override
public ItemStack insertItem(BlockPos pipePos, Direction direction, ItemStack remain, boolean simulate) {
BlockPos pos = pipePos.offset(direction);
CraftingTerminalTileEntity tile = Utility.getTileEntity(CraftingTerminalTileEntity.class, this.world, pos);
if (tile != null) {
remain = remain.copy();
int lowestSlot = -1;
do {
for (int i = 0; i < tile.craftItems.getSlots(); i++) {
ItemStack stack = tile.getRequestedCraftItem(i);
int count = tile.isGhostItem(i) ? 0 : stack.getCount();
if (!ItemHandlerHelper.canItemStacksStack(stack, remain))
continue;
// ensure that a single non-stackable item can still enter a ghost slot
if (!stack.isStackable() && count >= 1)
continue;
if (lowestSlot < 0 || !tile.isGhostItem(lowestSlot) && count < tile.getRequestedCraftItem(lowestSlot).getCount())
lowestSlot = i;
}
if (lowestSlot >= 0) {
ItemStack copy = remain.copy();
copy.setCount(1);
// if there were remaining items inserting into the slot with lowest contents, we're overflowing
if (tile.craftItems.insertItem(lowestSlot, copy, simulate).getCount() > 0)
break;
remain.shrink(1);
if (remain.isEmpty())
return ItemStack.EMPTY;
}
}
while (lowestSlot >= 0);
return ItemHandlerHelper.insertItemStacked(tile.items, remain, simulate);
}
return remain;
}
public static int getAvailableCrafts(PipeTileEntity tile, int slots, Function<Integer, ItemStack> inputFunction, Predicate<Integer> isGhost, Function<EquatableItemStack, Collection<NetworkLocation>> locationsFunction, Consumer<ItemStack> unavailableConsumer, ItemEqualityType... equalityTypes) {
PipeNetwork network = PipeNetwork.get(tile.getWorld());
// the highest amount we can craft with the items we have
int lowestAvailable = Integer.MAX_VALUE;
// this is the amount of items required for each ingredient when crafting ONE
Map<EquatableItemStack, MutableInt> requiredItems = new HashMap<>();
for (int i = 0; i < slots; i++) {
ItemStack requested = inputFunction.apply(i);
if (requested.isEmpty())
continue;
MutableInt amount = requiredItems.computeIfAbsent(new EquatableItemStack(requested), s -> new MutableInt());
amount.add(requested.getCount());
// if no items fit into the crafting input, we still want to pretend they do for requesting
int fit = Math.max(requested.getMaxStackSize() - (isGhost.test(i) ? 0 : requested.getCount()), 1);
if (lowestAvailable > fit)
lowestAvailable = fit;
}
for (Map.Entry<EquatableItemStack, MutableInt> entry : requiredItems.entrySet()) {
EquatableItemStack stack = entry.getKey();
// total amount of available items of this type
int available = 0;
for (NetworkLocation location : locationsFunction.apply(stack)) {
int amount = location.getItemAmount(tile.getWorld(), stack.stack, equalityTypes);
if (amount <= 0)
continue;
amount -= network.getLockedAmount(location.getPos(), stack.stack, null, equalityTypes);
available += amount;
}
// divide the total by the amount required to get the amount that
// we have available for each crafting slot that contains this item
available /= entry.getValue().intValue();
// check how many craftable items we have and add those on if we need to
if (available < lowestAvailable) {
int craftable = network.getCraftableAmount(tile.getPos(), unavailableConsumer, stack.stack, equalityTypes);
if (craftable > 0)
available += craftable / entry.getValue().intValue();
}
// clamp to lowest available
if (available < lowestAvailable)
lowestAvailable = available;
if (available <= 0 && unavailableConsumer != null)
unavailableConsumer.accept(stack.stack);
}
return lowestAvailable;
}
}

View file

@ -30,7 +30,7 @@ import net.minecraftforge.items.ItemHandlerHelper;
import javax.annotation.Nullable;
import java.util.List;
public class ItemTerminalBlock extends ContainerBlock implements IPipeConnectable {
public class ItemTerminalBlock extends ContainerBlock {
public ItemTerminalBlock() {
super(Properties.create(Material.ROCK).hardnessAndResistance(3).sound(SoundType.STONE));
}
@ -63,25 +63,6 @@ public class ItemTerminalBlock extends ContainerBlock implements IPipeConnectabl
return new ItemTerminalTileEntity();
}
@Override
public ConnectionType getConnectionType(World world, BlockPos pipePos, Direction direction) {
return ConnectionType.CONNECTED;
}
@Override
public ItemStack insertItem(World world, BlockPos pipePos, Direction direction, PipeItem item) {
BlockPos pos = pipePos.offset(direction);
ItemTerminalTileEntity tile = Utility.getTileEntity(ItemTerminalTileEntity.class, world, pos);
if (tile != null)
return ItemHandlerHelper.insertItemStacked(tile.items, item.stack, false);
return item.stack;
}
@Override
public boolean allowsModules(World world, BlockPos pipePos, Direction direction) {
return true;
}
@Override
public BlockRenderType getRenderType(BlockState state) {
return BlockRenderType.MODEL;

View file

@ -9,8 +9,11 @@ import de.ellpeck.prettypipes.misc.ItemOrder;
import de.ellpeck.prettypipes.network.*;
import de.ellpeck.prettypipes.packets.PacketHandler;
import de.ellpeck.prettypipes.packets.PacketNetworkItems;
import de.ellpeck.prettypipes.pipe.ConnectionType;
import de.ellpeck.prettypipes.pipe.IPipeConnectable;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import de.ellpeck.prettypipes.terminal.containers.ItemTerminalContainer;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.entity.player.ServerPlayerEntity;
@ -27,10 +30,14 @@ import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.ItemStackHandler;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.commons.lang3.tuple.Pair;
@ -39,9 +46,11 @@ import org.apache.commons.lang3.tuple.Triple;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
public class ItemTerminalTileEntity extends TileEntity implements INamedContainerProvider, ITickableTileEntity {
public class ItemTerminalTileEntity extends TileEntity implements INamedContainerProvider, ITickableTileEntity, IPipeConnectable {
public final ItemStackHandler items = new ItemStackHandler(12) {
@Override
@ -49,8 +58,9 @@ public class ItemTerminalTileEntity extends TileEntity implements INamedContaine
return true;
}
};
public Map<EquatableItemStack, NetworkItem> networkItems;
private final Queue<NetworkLock> pendingRequests = new ArrayDeque<>();
protected Map<EquatableItemStack, NetworkItem> networkItems;
private final Queue<NetworkLock> existingRequests = new LinkedList<>();
private final LazyOptional<IPipeConnectable> lazyThis = LazyOptional.of(() -> this);
protected ItemTerminalTileEntity(TileEntityType<?> tileEntityTypeIn) {
super(tileEntityTypeIn);
@ -70,22 +80,23 @@ public class ItemTerminalTileEntity extends TileEntity implements INamedContaine
return;
boolean update = false;
if (this.world.getGameTime() % 10 == 0) {
int interval = pipe.pressurizer != null ? 2 : 10;
if (this.world.getGameTime() % interval == 0) {
for (int i = 6; i < 12; i++) {
ItemStack extracted = this.items.extractItem(i, Integer.MAX_VALUE, true);
if (extracted.isEmpty())
continue;
ItemStack remain = network.tryInsertItem(pipe.getPos(), this.pos, extracted, true);
ItemStack remain = network.routeItem(pipe.getPos(), this.pos, extracted, true);
if (remain.getCount() == extracted.getCount())
continue;
this.items.extractItem(i, extracted.getCount() - remain.getCount(), false);
break;
}
if (!this.pendingRequests.isEmpty()) {
NetworkLock request = this.pendingRequests.remove();
if (!this.existingRequests.isEmpty()) {
NetworkLock request = this.existingRequests.remove();
network.resolveNetworkLock(request);
network.requestItem(request.location, pipe.getPos(), this.pos, request.stack, ItemEqualityType.NBT);
network.requestExistingItem(request.location, pipe.getPos(), this.pos, request, request.stack, ItemEqualityType.NBT);
update = true;
}
}
@ -101,8 +112,9 @@ public class ItemTerminalTileEntity extends TileEntity implements INamedContaine
public void remove() {
super.remove();
PipeNetwork network = PipeNetwork.get(this.world);
for (NetworkLock lock : this.pendingRequests)
for (NetworkLock lock : this.existingRequests)
network.resolveNetworkLock(lock);
this.lazyThis.invalidate();
}
public PipeTileEntity getConnectedPipe() {
@ -116,18 +128,21 @@ public class ItemTerminalTileEntity extends TileEntity implements INamedContaine
}
public void updateItems(PlayerEntity... playersToSync) {
if (this.getConnectedPipe() == null)
PipeTileEntity pipe = this.getConnectedPipe();
if (pipe == null)
return;
this.networkItems = this.collectItems();
if (playersToSync.length > 0) {
List<ItemStack> clientItems = this.networkItems.values().stream().map(NetworkItem::asStack).collect(Collectors.toList());
List<ItemStack> clientCraftables = PipeNetwork.get(this.world).getAllCraftables(pipe.getPos()).stream().map(Pair::getRight).collect(Collectors.toList());
List<ItemStack> currentlyCrafting = this.getCurrentlyCrafting().stream().sorted(Comparator.comparingInt(ItemStack::getCount).reversed()).collect(Collectors.toList());
for (PlayerEntity player : playersToSync) {
if (!(player.openContainer instanceof ItemTerminalContainer))
continue;
ItemTerminalTileEntity tile = ((ItemTerminalContainer) player.openContainer).tile;
if (tile != this)
continue;
PacketHandler.sendTo(player, new PacketNetworkItems(clientItems));
PacketHandler.sendTo(player, new PacketNetworkItems(clientItems, clientCraftables, currentlyCrafting));
}
}
}
@ -136,46 +151,21 @@ public class ItemTerminalTileEntity extends TileEntity implements INamedContaine
PipeNetwork network = PipeNetwork.get(this.world);
network.startProfile("terminal_request_item");
this.updateItems();
int requested = this.requestItemImpl(stack);
int requested = this.requestItemImpl(stack, onItemUnavailable(player));
if (requested > 0) {
player.sendMessage(new TranslationTextComponent("info." + PrettyPipes.ID + ".sending", requested, stack.getDisplayName()).setStyle(new Style().setColor(TextFormatting.GREEN)));
} else {
player.sendMessage(new TranslationTextComponent("info." + PrettyPipes.ID + ".not_found", stack.getDisplayName()).setStyle(new Style().setColor(TextFormatting.RED)));
onItemUnavailable(player).accept(stack);
}
network.endProfile();
}
protected int requestItemImpl(ItemStack stack) {
PipeNetwork network = PipeNetwork.get(this.world);
EquatableItemStack equatable = new EquatableItemStack(stack);
NetworkItem item = this.networkItems.get(equatable);
if (item != null) {
int remain = stack.getCount();
for (NetworkLocation location : item.getLocations()) {
int amount = location.getItemAmount(this.world, stack, ItemEqualityType.NBT);
if (amount <= 0)
continue;
amount -= network.getLockedAmount(location.getPos(), stack, ItemEqualityType.NBT);
if (amount > 0) {
if (remain < amount)
amount = remain;
remain -= amount;
while (amount > 0) {
ItemStack copy = stack.copy();
copy.setCount(Math.min(stack.getMaxStackSize(), amount));
NetworkLock lock = new NetworkLock(location, copy);
this.pendingRequests.add(lock);
network.createNetworkLock(lock);
amount -= copy.getCount();
}
if (remain <= 0)
break;
}
}
return stack.getCount() - remain;
}
return 0;
public int requestItemImpl(ItemStack stack, Consumer<ItemStack> unavailableConsumer) {
NetworkItem item = this.networkItems.get(new EquatableItemStack(stack));
Collection<NetworkLocation> locations = item == null ? Collections.emptyList() : item.getLocations();
Pair<List<NetworkLock>, ItemStack> ret = requestItemLater(this.world, this.getConnectedPipe().getPos(), locations, unavailableConsumer, stack, ItemEqualityType.NBT);
this.existingRequests.addAll(ret.getLeft());
return stack.getCount() - ret.getRight().getCount();
}
protected PlayerEntity[] getLookingPlayers() {
@ -201,18 +191,46 @@ public class ItemTerminalTileEntity extends TileEntity implements INamedContaine
return items;
}
private List<ItemStack> getCurrentlyCrafting() {
PipeNetwork network = PipeNetwork.get(this.world);
PipeTileEntity pipe = this.getConnectedPipe();
if (pipe == null)
return Collections.emptyList();
List<Pair<BlockPos, ItemStack>> crafting = network.getCurrentlyCrafting(pipe.getPos());
return crafting.stream().map(Pair::getRight).collect(Collectors.toList());
}
public void cancelCrafting() {
PipeNetwork network = PipeNetwork.get(this.world);
PipeTileEntity pipe = this.getConnectedPipe();
if (pipe == null)
return;
for (Pair<BlockPos, ItemStack> craftable : network.getAllCraftables(pipe.getPos())) {
PipeTileEntity otherPipe = network.getPipe(craftable.getLeft());
if (otherPipe != null) {
for (NetworkLock lock : otherPipe.craftIngredientRequests)
network.resolveNetworkLock(lock);
otherPipe.craftIngredientRequests.clear();
otherPipe.craftResultRequests.clear();
}
}
PlayerEntity[] lookingPlayers = this.getLookingPlayers();
if (lookingPlayers.length > 0)
this.updateItems(lookingPlayers);
}
@Override
public CompoundNBT write(CompoundNBT compound) {
compound.put("items", this.items.serializeNBT());
compound.put("requests", Utility.serializeAll(this.pendingRequests));
compound.put("requests", Utility.serializeAll(this.existingRequests));
return super.write(compound);
}
@Override
public void read(CompoundNBT compound) {
this.items.deserializeNBT(compound.getCompound("items"));
this.pendingRequests.clear();
this.pendingRequests.addAll(Utility.deserializeAll(compound.getList("requests", NBT.TAG_COMPOUND), NetworkLock::new));
this.existingRequests.clear();
this.existingRequests.addAll(Utility.deserializeAll(compound.getList("requests", NBT.TAG_COMPOUND), NetworkLock::new));
super.read(compound);
}
@ -226,4 +244,66 @@ public class ItemTerminalTileEntity extends TileEntity implements INamedContaine
public Container createMenu(int window, PlayerInventory inv, PlayerEntity player) {
return new ItemTerminalContainer(Registry.itemTerminalContainer, window, player, this.pos);
}
@Override
public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
if (cap == Registry.pipeConnectableCapability)
return this.lazyThis.cast();
return LazyOptional.empty();
}
@Override
public ConnectionType getConnectionType(BlockPos pipePos, Direction direction) {
return ConnectionType.CONNECTED;
}
@Override
public ItemStack insertItem(BlockPos pipePos, Direction direction, ItemStack stack, boolean simulate) {
BlockPos pos = pipePos.offset(direction);
ItemTerminalTileEntity tile = Utility.getTileEntity(ItemTerminalTileEntity.class, world, pos);
if (tile != null)
return ItemHandlerHelper.insertItemStacked(tile.items, stack, simulate);
return stack;
}
@Override
public boolean allowsModules(BlockPos pipePos, Direction direction) {
return true;
}
public static Pair<List<NetworkLock>, ItemStack> requestItemLater(World world, BlockPos destPipe, Collection<NetworkLocation> locations, Consumer<ItemStack> unavailableConsumer, ItemStack stack, ItemEqualityType... equalityTypes) {
List<NetworkLock> requests = new ArrayList<>();
ItemStack remain = stack.copy();
PipeNetwork network = PipeNetwork.get(world);
// check for existing items
for (NetworkLocation location : locations) {
int amount = location.getItemAmount(world, stack, ItemEqualityType.NBT);
if (amount <= 0)
continue;
amount -= network.getLockedAmount(location.getPos(), stack, null, ItemEqualityType.NBT);
if (amount > 0) {
if (remain.getCount() < amount)
amount = remain.getCount();
remain.shrink(amount);
while (amount > 0) {
ItemStack copy = stack.copy();
copy.setCount(Math.min(stack.getMaxStackSize(), amount));
NetworkLock lock = new NetworkLock(location, copy);
network.createNetworkLock(lock);
requests.add(lock);
amount -= copy.getCount();
}
if (remain.isEmpty())
break;
}
}
// check for craftable items
if (!remain.isEmpty())
remain = network.requestCraftedItem(destPipe, unavailableConsumer, remain, equalityTypes);
return Pair.of(requests, remain);
}
public static Consumer<ItemStack> onItemUnavailable(PlayerEntity player) {
return s -> player.sendMessage(new TranslationTextComponent("info." + PrettyPipes.ID + ".not_found").setStyle(new Style().setColor(TextFormatting.RED)));
}
}

View file

@ -2,7 +2,6 @@ package de.ellpeck.prettypipes.terminal.containers;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.terminal.CraftingTerminalTileEntity;
import de.ellpeck.prettypipes.terminal.ItemTerminalTileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.inventory.CraftResultInventory;
@ -17,10 +16,8 @@ import net.minecraft.item.crafting.ICraftingRecipe;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.network.play.server.SSetSlotPacket;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.items.SlotItemHandler;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Optional;
@ -71,9 +68,9 @@ public class CraftingTerminalContainer extends ItemTerminalContainer {
@Override
public ItemStack slotClick(int slotId, int dragType, ClickType clickTypeIn, PlayerEntity player) {
if (slotId > 0) {
if (slotId > 0 && clickTypeIn == ClickType.PICKUP) {
Slot slot = this.inventorySlots.get(slotId);
if (slot.inventory == this.craftInventory)
if (slot.inventory == this.craftInventory && !slot.getHasStack())
this.getTile().ghostItems.setStackInSlot(slot.getSlotIndex(), ItemStack.EMPTY);
}
return super.slotClick(slotId, dragType, clickTypeIn, player);

View file

@ -1,5 +1,6 @@
package de.ellpeck.prettypipes.terminal.containers;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import de.ellpeck.prettypipes.PrettyPipes;
@ -15,6 +16,7 @@ import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
public class CraftingTerminalGui extends ItemTerminalGui {
private static final ResourceLocation TEXTURE = new ResourceLocation(PrettyPipes.ID, "textures/gui/crafting_terminal.png");
@ -29,8 +31,8 @@ public class CraftingTerminalGui extends ItemTerminalGui {
protected void init() {
super.init();
this.requestButton = this.addButton(new Button(this.guiLeft + 8, this.guiTop + 100, 50, 20, I18n.format("info." + PrettyPipes.ID + ".request"), button -> {
int all = hasShiftDown() ? 1 : 0;
PacketHandler.sendToServer(new PacketButton(this.container.tile.getPos(), PacketButton.ButtonResult.CRAFT_TERMINAL_REQUEST, all));
int amount = requestModifier();
PacketHandler.sendToServer(new PacketButton(this.container.tile.getPos(), PacketButton.ButtonResult.CRAFT_TERMINAL_REQUEST, amount));
}));
this.tick();
}

View file

@ -1,7 +1,7 @@
package de.ellpeck.prettypipes.terminal.containers;
import com.mojang.blaze3d.matrix.MatrixStack;
import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.misc.ItemOrder;
import de.ellpeck.prettypipes.misc.ItemTerminalWidget;
import de.ellpeck.prettypipes.misc.PlayerPrefs;
import de.ellpeck.prettypipes.packets.PacketButton;
@ -19,24 +19,35 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import org.apache.commons.lang3.tuple.Pair;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ItemTerminalGui extends ContainerScreen<ItemTerminalContainer> {
private static final ResourceLocation TEXTURE = new ResourceLocation(PrettyPipes.ID, "textures/gui/item_terminal.png");
public List<ItemStack> currentlyCrafting;
public TextFieldWidget search;
// craftables have the second parameter set to true
private final List<Pair<ItemStack, Boolean>> sortedItems = new ArrayList<>();
private List<ItemStack> items;
private List<ItemStack> sortedItems;
private List<ItemStack> craftables;
private Button minusButton;
private Button plusButton;
private Button requestButton;
private Button orderButton;
private Button ascendingButton;
private Button cancelCraftingButton;
private String lastSearchText;
private int requestAmount = 1;
private int scrollOffset;
public TextFieldWidget search;
private ItemStack hoveredCrafting;
public ItemTerminalGui(ItemTerminalContainer screenContainer, PlayerInventory inv, ITextComponent titleIn) {
super(screenContainer, inv, titleIn);
@ -54,6 +65,7 @@ public class ItemTerminalGui extends ContainerScreen<ItemTerminalContainer> {
} else {
this.requestAmount += modifier;
}
// 384 items is 6 stacks, which is what fits into the terminal slots
if (this.requestAmount > 384)
this.requestAmount = 384;
}));
@ -89,6 +101,9 @@ public class ItemTerminalGui extends ContainerScreen<ItemTerminalContainer> {
prefs.save();
this.updateWidgets();
}));
this.cancelCraftingButton = this.addButton(new Button(this.guiLeft + this.xSize + 4, this.guiTop + 4 + 64, 54, 20, I18n.format("info." + PrettyPipes.ID + ".cancel_all"), b -> {
}));
this.cancelCraftingButton.visible = false;
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 9; x++)
this.addButton(new ItemTerminalWidget(this.guiLeft + this.getXOffset() + 8 + x * 18, this.guiTop + 18 + y * 18, x, y, this));
@ -121,6 +136,20 @@ public class ItemTerminalGui extends ContainerScreen<ItemTerminalContainer> {
}
}
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
// we have to do the click logic here because JEI is activated when letting go of the mouse button
// and vanilla buttons are activated when the click starts, so we'll always invoke jei accidentally by default
if (button == 0 && this.cancelCraftingButton.visible && this.cancelCraftingButton.isHovered()) {
if (this.currentlyCrafting != null && !this.currentlyCrafting.isEmpty()) {
PacketHandler.sendToServer(new PacketButton(this.container.tile.getPos(), PacketButton.ButtonResult.CANCEL_CRAFTING));
return true;
}
}
return super.mouseReleased(mouseX, mouseY, button);
}
@Override
public boolean keyPressed(int x, int y, int z) {
// for some reason we have to do this to make the text field allow the inventory key to be typed
@ -132,8 +161,10 @@ public class ItemTerminalGui extends ContainerScreen<ItemTerminalContainer> {
return super.keyPressed(x, y, z);
}
public void updateItemList(List<ItemStack> items) {
public void updateItemList(List<ItemStack> items, List<ItemStack> craftables, List<ItemStack> currentlyCrafting) {
this.items = items;
this.craftables = craftables;
this.currentlyCrafting = currentlyCrafting;
this.updateWidgets();
}
@ -141,13 +172,22 @@ public class ItemTerminalGui extends ContainerScreen<ItemTerminalContainer> {
PlayerPrefs prefs = PlayerPrefs.get();
this.ascendingButton.setMessage(prefs.terminalAscending ? "^" : "v");
this.orderButton.setMessage(prefs.terminalItemOrder.name().substring(0, 1));
this.cancelCraftingButton.visible = this.currentlyCrafting != null && !this.currentlyCrafting.isEmpty();
Comparator<ItemStack> comparator = prefs.terminalItemOrder.comparator;
if (!prefs.terminalAscending)
comparator = comparator.reversed();
this.sortedItems = new ArrayList<>(this.items);
this.sortedItems.sort(comparator);
// add all items to the sorted items list
this.sortedItems.clear();
for (ItemStack stack : this.items)
this.sortedItems.add(Pair.of(stack, false));
for (ItemStack stack : this.craftables)
this.sortedItems.add(Pair.of(stack, true));
// compare by craftability first, and then by the player's chosen order
Comparator<Pair<ItemStack, Boolean>> fullComparator = Comparator.comparing(Pair::getRight);
this.sortedItems.sort(fullComparator.thenComparing(Pair::getLeft, comparator));
String searchText = this.search.getText();
if (!Strings.isNullOrEmpty(searchText)) {
@ -155,11 +195,11 @@ public class ItemTerminalGui extends ContainerScreen<ItemTerminalContainer> {
String search = searchText;
String toCompare;
if (search.startsWith("@")) {
toCompare = s.getItem().getRegistryName().getNamespace();
toCompare = s.getLeft().getItem().getCreatorModId(s.getLeft());
search = search.substring(1);
} else {
// don't use formatted text here since we want to search for name
toCompare = s.getDisplayName().getString();
toCompare = s.getLeft().getDisplayName().getString();
}
return !toCompare.toLowerCase(Locale.ROOT).contains(search.toLowerCase(Locale.ROOT));
});
@ -174,9 +214,12 @@ public class ItemTerminalGui extends ContainerScreen<ItemTerminalContainer> {
int index = i + this.scrollOffset * 9;
if (index >= this.sortedItems.size()) {
widget.stack = ItemStack.EMPTY;
widget.craftable = false;
widget.visible = false;
} else {
widget.stack = this.sortedItems.get(index);
Pair<ItemStack, Boolean> stack = this.sortedItems.get(index);
widget.stack = stack.getLeft();
widget.craftable = stack.getRight();
widget.visible = true;
}
}
@ -198,15 +241,29 @@ public class ItemTerminalGui extends ContainerScreen<ItemTerminalContainer> {
this.renderTooltip(I18n.format("info." + PrettyPipes.ID + "." + (prefs.terminalAscending ? "ascending" : "descending")), mouseX, mouseY);
}
this.renderHoveredToolTip(mouseX, mouseY);
// if (this.cancelCraftingButton.visible && this.cancelCraftingButton.isHovered()) {
// String[] tooltip = I18n.format("info." + PrettyPipes.ID + ".cancel_all.desc").split("\n");
// this.func_243308_b(Arrays.stream(tooltip).map(StringTextComponent::new).collect(Collectors.toList()), mouseX, mouseY);
// }
if (!this.hoveredCrafting.isEmpty())
this.renderTooltip(this.hoveredCrafting, mouseX, mouseY);
// this.func_230459_a_(mouseX, mouseY);
}
@Override
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) {
this.font.drawString(this.playerInventory.getDisplayName().getFormattedText(), 8 + this.getXOffset(), this.ySize - 96 + 2, 4210752);
this.font.drawString(this.title.getFormattedText(), 8, 6, 4210752);
this.font.drawString(this.playerInventory.getDisplayName().getString(), 8 + this.getXOffset(), this.ySize - 96 + 2, 4210752);
this.font.drawString(this.title.getString(), 8, 6, 4210752);
String amount = String.valueOf(this.requestAmount);
this.font.drawString(amount, (176 + 15 - this.font.getStringWidth(amount)) / 2F - 7 + this.getXOffset(), 106, 4210752);
if (this.currentlyCrafting != null && !this.currentlyCrafting.isEmpty()) {
this.font.drawString(I18n.format("info." + PrettyPipes.ID + ".crafting"), this.xSize + 4, 4 + 6, 4210752);
if (this.currentlyCrafting.size() > 6)
this.font.drawString(". . .", this.xSize + 24, 4 + 51, 4210752);
}
}
@Override
@ -220,6 +277,31 @@ public class ItemTerminalGui extends ContainerScreen<ItemTerminalContainer> {
} else {
this.blit(this.guiLeft + this.getXOffset() + 172, this.guiTop + 18, 244, 241, 12, 15);
}
// draw the items that are currently crafting
this.hoveredCrafting = ItemStack.EMPTY;
if (this.currentlyCrafting != null && !this.currentlyCrafting.isEmpty()) {
this.getMinecraft().getTextureManager().bindTexture(TEXTURE);
this.blit(this.guiLeft + this.xSize, this.guiTop + 4, 191, 0, 65, 89);
int x = 0;
int y = 0;
for (ItemStack stack : this.currentlyCrafting) {
int itemX = this.guiLeft + this.xSize + 4 + x * 18;
int itemY = this.guiTop + 4 + 16 + y * 18;
this.itemRenderer.renderItemAndEffectIntoGUI(stack, itemX, itemY);
this.itemRenderer.renderItemOverlayIntoGUI(this.font, stack, itemX, itemY, String.valueOf(stack.getCount()));
if (mouseX >= itemX && mouseY >= itemY && mouseX < itemX + 16 && mouseY < itemY + 18)
this.hoveredCrafting = stack;
x++;
if (x >= 3) {
x = 0;
y++;
if (y >= 2)
break;
}
}
}
}
protected ResourceLocation getTexture() {
@ -244,7 +326,7 @@ public class ItemTerminalGui extends ContainerScreen<ItemTerminalContainer> {
.map(w -> (ItemTerminalWidget) w);
}
private static int requestModifier() {
public static int requestModifier() {
if (hasControlDown()) {
return 10;
} else if (hasShiftDown()) {

View file

@ -9,6 +9,7 @@ modLoader="javafml" #mandatory
loaderVersion="[28,)" #mandatory (28 is current forge version)
# A URL to refer people to when problems occur with this mod
issueTrackerURL="https://github.com/Ellpeck/PrettyPipes" #optional
license="MIT"
# A list of mods - how many allowed here is determined by the individual mod loader
[[mods]] #mandatory
# The modid of the mod
@ -24,9 +25,9 @@ displayName="Pretty Pipes" #mandatory
# A file name (in the root of the mod JAR) containing a logo for display
#logoFile="examplemod.png" #optional
# A text field displayed in the mod UI
credits="Commission for Violet" #optional
#credits="" #optional
# A text field displayed in the mod UI
authors="Ellpeck" #optional
# The description text for the mod (multi line!) (#mandatory)
description='''
wow pipes, how creative'''
wow pipes, how creative'''

View file

@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "prettypipes:block/pressurizer"
}
}
}

View file

@ -1,10 +0,0 @@
{
"_comment": "Unicode fonts only",
"providers": [
{
"type": "legacy_unicode",
"sizes": "minecraft:font/glyph_sizes.bin",
"template": "minecraft:font/unicode_page_%s.png"
}
]
}

View file

@ -1,11 +1,15 @@
{
"item.prettypipes.wrench": "Pipe Wrench",
"info.prettypipes.wrench": "Allows modifying pipe connections\nPlaces blocks in offhand as covers\nSneak to remove cover or pipe",
"item.prettypipes.blank_module": "Blank Module",
"item.prettypipes.pipe_frame": "Pipe Frame",
"info.prettypipes.pipe_frame": "Attach to a pipe or pipe-adjacent block\nShows amount of an item in the pipe's network",
"item.prettypipes.low_extraction_module": "Low Extraction Module",
"item.prettypipes.medium_extraction_module": "Medium Extraction Module",
"item.prettypipes.high_extraction_module": "High Extraction Module",
"item.prettypipes.low_crafting_module": "Low Crafting Module",
"item.prettypipes.medium_crafting_module": "Medium Crafting Module",
"item.prettypipes.high_crafting_module": "High Crafting Module",
"item.prettypipes.low_filter_module": "Low Filter Module",
"item.prettypipes.medium_filter_module": "Medium Filter Module",
"item.prettypipes.high_filter_module": "High Filter Module",
@ -15,6 +19,9 @@
"item.prettypipes.low_low_priority_module": "Low Priority Module",
"item.prettypipes.medium_low_priority_module": "Lower Priority Module",
"item.prettypipes.high_low_priority_module": "Lowest Priority Module",
"item.prettypipes.low_high_priority_module": "High Priority Module",
"item.prettypipes.medium_high_priority_module": "Higher Priority Module",
"item.prettypipes.high_high_priority_module": "Highest Priority Module",
"item.prettypipes.low_retrieval_module": "Low Retrieval Module",
"item.prettypipes.medium_retrieval_module": "Medium Retrieval Module",
"item.prettypipes.high_retrieval_module": "High Retrieval Module",
@ -22,26 +29,35 @@
"item.prettypipes.damage_filter_modifier": "Damage Filter Modifier",
"item.prettypipes.nbt_filter_modifier": "Data Filter Modifier",
"item.prettypipes.tag_filter_modifier": "Tag Filter Modifier",
"item.prettypipes.mod_filter_modifier": "Mod Filter Modifier",
"item.prettypipes.redstone_module": "Redstone Module",
"item.prettypipes.filter_increase_modifier": "Filter Increase Modifier",
"info.prettypipes.extraction_module": "Pulls items from adjacent inventories\nFilters and pull rates vary by tier\nHigh tiers prevent over-sending",
"info.prettypipes.filter_module": "Restricts flow from pipes into adjacent inventories\nFilter amount varies by tier",
"info.prettypipes.speed_module": "Increases speed of items exiting adjacent inventories\nSpeed varies by tier",
"info.prettypipes.low_priority_module": "Decreases the reception priority of adjacent inventories\nLower priority means items will prefer other inventories",
"info.prettypipes.low_priority_module": "Decreases the reception priority of adjacent inventories\nLower priority means items will prefer other inventories, regardless of distance",
"info.prettypipes.high_priority_module": "Increases the reception priority of adjacent inventories\nHigher priority means items will prefer this inventory, regardless of distance",
"info.prettypipes.retrieval_module": "Pulls items from other inventories in the network\nFilters and pull rates vary by tier\nHigh tiers prevent over-sending",
"info.prettypipes.stack_size_module": "Limits the amount of items that can enter adjacent inventories\nAutomatically prevents over-sending",
"info.prettypipes.damage_filter_modifier": "Causes any filter slots to filter by item damage",
"info.prettypipes.nbt_filter_modifier": "Causes any filter slots to filter by item data (NBT)",
"info.prettypipes.tag_filter_modifier": "Causes any filter slots to filter by tags\n(Modern equivalent of the Ore Dictionary)",
"info.prettypipes.mod_filter_modifier": "Causes any filter slots to filter by mod",
"info.prettypipes.redstone_module": "Allows disabling the pipe with a redstone signal\nWorks for both extraction and retrieval",
"info.prettypipes.item_terminal": "Allows viewing and requesting all items in a pipe network\nAlso has slots for putting items into the network",
"info.prettypipes.crafting_terminal": "Allows requesting ingredients for crafting recipes\nSupports auto-filling from JEI if installed",
"info.prettypipes.pressurizer": "Drastically increases item speed in the entire pipe network\nRequires FE (or RF) for each item transferred",
"info.prettypipes.filter_increase_modifier": "Adds additional filter slots to the pipe\nSlots will be used by any module that filters items",
"info.prettypipes.crafting_module": "Allows automatically crafting items into other items using the connected block\nDoesn't automatically extract from the block\nInput and output slots vary by tier\nSupports auto-filling from JEI if installed",
"block.prettypipes.pipe": "Pipe",
"block.prettypipes.item_terminal": "Item Terminal",
"block.prettypipes.crafting_terminal": "Crafting Terminal",
"block.prettypipes.pressurizer": "Pipe Pressurizer",
"itemGroup.prettypipes": "Pretty Pipes",
"container.prettypipes.pipe": "Pipe",
"container.prettypipes.item_terminal": "Item Terminal",
"container.prettypipes.crafting_terminal": "Crafting Terminal",
"container.prettypipes.pressurizer": "Pipe Pressurizer",
"info.prettypipes.whitelist": "Allowed",
"info.prettypipes.blacklist": "Disallowed",
"info.prettypipes.shift": "Hold Shift for info",
@ -61,5 +77,9 @@
"info.prettypipes.ascending": "Ascending",
"info.prettypipes.descending": "Descending",
"info.prettypipes.sync_jei.on": "Sync JEI Search",
"info.prettypipes.sync_jei.off": "Don't Sync JEI Search"
"info.prettypipes.sync_jei.off": "Don't Sync JEI Search",
"info.prettypipes.energy": "%s / %s FE",
"info.prettypipes.crafting": "Awaiting",
"info.prettypipes.cancel_all": "Cancel",
"info.prettypipes.cancel_all.desc": "Stops waiting for current crafting outputs\nDoesn't remove inputs from blocks"
}

View file

@ -1,65 +1,75 @@
{
"item.prettypipes.wrench": "Ключ трубы",
"item.prettypipes.wrench": "Разводной ключ",
"item.prettypipes.blank_module": "Пустой модуль",
"item.prettypipes.pipe_frame": "Рама трубы",
"info.prettypipes.pipe_frame": "Прикрепите к трубе или смежному с трубой блоку\nПоказывает количество предметов в трубопроводной сети",
"info.prettypipes.pipe_frame": "Прикрепите к трубе или к соседнему блоку трубы.\nПоказывает кол-во предметов в сети труб.",
"item.prettypipes.low_extraction_module": "Модуль низкого извлечения",
"item.prettypipes.medium_extraction_module": "Модуль среднего извлечения",
"item.prettypipes.high_extraction_module": "Модуль высокого извлечения",
"item.prettypipes.low_crafting_module": "Модуль низкого создания",
"item.prettypipes.medium_crafting_module": "Модуль среднего создания",
"item.prettypipes.high_crafting_module": "Модуль высокого создания",
"item.prettypipes.low_filter_module": "Модуль низкой фильтрации",
"item.prettypipes.medium_filter_module": "Модуль средней фильтрации",
"item.prettypipes.high_filter_module": "Модуль высокой фильтрации",
"item.prettypipes.low_speed_module": "Модуль низкого увеличения скорости",
"item.prettypipes.medium_speed_module": "Модуль среднего увеличения скорости",
"item.prettypipes.high_speed_module": "Модуль высокого увеличения скорости",
"item.prettypipes.low_speed_module": "Модуль низкого повышения скорости",
"item.prettypipes.medium_speed_module": "Модуль среднего повышения скорости",
"item.prettypipes.high_speed_module": "Модуль высокого повышения скорости",
"item.prettypipes.low_low_priority_module": "Модуль низкого приоритета",
"item.prettypipes.medium_low_priority_module": "Модуль более низкого приоритета",
"item.prettypipes.high_low_priority_module": "Модуль самого низкого приоритета",
"item.prettypipes.low_retrieval_module": "Модуль низкого поиска",
"item.prettypipes.medium_retrieval_module": "Модуль среднего поиска",
"item.prettypipes.high_retrieval_module": "Модуль высокого поиска",
"item.prettypipes.stack_size_module": "Модуль ограничения стака",
"item.prettypipes.damage_filter_modifier": "Модификатор фильтрации повреждений",
"item.prettypipes.nbt_filter_modifier": "Модификатор фильтрации данных",
"item.prettypipes.tag_filter_modifier": "Модификатор фильтрации тегов",
"item.prettypipes.redstone_module": "Модуль редстоуна",
"info.prettypipes.extraction_module": "Вытягивает предметы из соседних запасов\nФильтры и скорость вытягивания варьируются в зависимости от уровня\nВысокие уровни предотвращают чрезмерную отправку",
"info.prettypipes.filter_module": "Ограничивает поток из труб в соседние запасы\nКоличество фильтров варьируется в зависимости от уровня",
"info.prettypipes.speed_module": "Увеличивает скорость предметов, выходящих из соседних запасов\nСкорость зависит от уровня",
"info.prettypipes.low_priority_module": "Уменьшает приоритет приема соседних запасов\nБолее низкий приоритет означает, что предметы предпочтут другие запасы",
"info.prettypipes.retrieval_module": "Вытягивает товары из других запасов в сети\nФильтры и скорость вытягивания различаются в зависимости от уровня\nВысокие уровни предотвращают чрезмерную отправку",
"info.prettypipes.stack_size_module": "Ограничивает количество предметов, которые могут войти в соседние запасы\nАвтоматически предотвращает чрезмерную отправку",
"info.prettypipes.damage_filter_modifier": "Заставляет любые слоты фильтра фильтровать повреждение предмета",
"info.prettypipes.nbt_filter_modifier": "Заставляет любые слоты фильтра фильтровать данные предмета (NBT)",
"info.prettypipes.tag_filter_modifier": "Заставляет любые слоты фильтра фильтровать по тегам\n(Современный эквивалент словаря руд)",
"info.prettypipes.redstone_module": "Позволяет отключить трубу с сигналом редстоуна\nРаботы как добыча, так и как поиск",
"info.prettypipes.item_terminal": "Позволяет просматривать и запрашивать все элементы в трубопроводной сети\nТакже есть слоты для размещения предметов в сети",
"info.prettypipes.crafting_terminal": "Позволяет запрашивать ингредиенты для изготовления рецептов\nПоддерживает автозаполнение из JEI, если установлено",
"item.prettypipes.stack_size_module": "Модуль для ограничителя стэка",
"item.prettypipes.damage_filter_modifier": "Модификатор для фильтрации повреждения",
"item.prettypipes.nbt_filter_modifier": "Модификатор для фильтрации Данных",
"item.prettypipes.tag_filter_modifier": "Модификатор для фильтрации Тэга",
"item.prettypipes.redstone_module": "Модуль красного камня",
"item.prettypipes.filter_increase_modifier": "Модификатор для ускорения фильтрации",
"info.prettypipes.extraction_module": "Вытягивает предметы из соседних запасов.\nФильтры и скорость вытягивания варьируются в зависимости от уровня.\nВысокие уровни предотвращают завышенную отправку.",
"info.prettypipes.filter_module": "Ограничивает поток из труб в соседние запасы.\nКо-во фильтров варьируются в зависимости от уровня.",
"info.prettypipes.speed_module": "Повышает скорость предметов покидающих из соседних запасов.\nСкорость варьируются в зависимости от уровня.",
"info.prettypipes.low_priority_module": "Снижает приоритет приёма из соседних запасов.\nБолее низкий приоритет означает, что предметы будут предпочитать другие запасы.",
"info.prettypipes.retrieval_module": "Вытягивает предметы из других запасов в сети.\nФильтры и вытягивание варьируются в зависимости от уровня\nВысокие уровни предотвращают завышенную отправку.",
"info.prettypipes.stack_size_module": "Ограничивает кол-во предметов, которые могут входить в соседние запасы\nАвтоматически предотвращает завышенную отправку.",
"info.prettypipes.damage_filter_modifier": "Заставляет любые слоты фильтра, фильтровать по повреждению предмета.",
"info.prettypipes.nbt_filter_modifier": "Заставляет любые слоты фильтра, фильтровать по Данным предмета (NBT).",
"info.prettypipes.tag_filter_modifier": "Заставляет любые слоты фильтра, фильтровать по Тэгам предмета.\n(Современный эквивалент Словаря руд.).",
"info.prettypipes.redstone_module": "Позволяет отключать трубы сигналом красного камня.\nРаботает как для извлечения, так и для поиска.",
"info.prettypipes.item_terminal": "Позволяет просматривать и запрашивать все предметы в сети труб.\nТакже имеет слоты для введения предметов в сеть.",
"info.prettypipes.crafting_terminal": "Позволяет запрашивать ингредиенты для рецептов созданий.\nПоддерживает автоматическую заполнение из JEI, если установлен.",
"info.prettypipes.pressurizer": "Значительно повышает скорость предмета входящий в сеть труб.\nТребует FE (или RF) на каждый перемещённый предмет.",
"info.prettypipes.filter_increase_modifier": "Добавляет дополнительную фильтрацию слотов в трубы.\nСлоты станут использовать любой модуль, который фильтрует предметы.",
"info.prettypipes.crafting_module": "Позволяет автоматически создавать предметы в другие предметы при помощи соединённого блока.\nНе извлекается автоматически из блока\nВходные и выходные слоты варьируются в зависимости от уровня.\nПоддерживает автоматическое заполнение из JEI, если установлен.",
"block.prettypipes.pipe": "Труба",
"block.prettypipes.item_terminal": "Терминал предметов",
"block.prettypipes.crafting_terminal": "Терминал предметов",
"itemGroup.prettypipes": "Хорошенькие трубы",
"block.prettypipes.item_terminal": "Предметный терминал",
"block.prettypipes.crafting_terminal": "Ремесленный терминал",
"block.prettypipes.pressurizer": "Трубный компенсатор давления",
"itemGroup.prettypipes": "Pretty Pipes",
"container.prettypipes.pipe": "Труба",
"container.prettypipes.item_terminal": "Терминал предметов",
"container.prettypipes.crafting_terminal": "Терминал предметов",
"info.prettypipes.whitelist": "Разрешено",
"info.prettypipes.blacklist": "Запрещено",
"info.prettypipes.shift": "Удерживайте [Shift], чтобы узнать подробности",
"info.prettypipes.populate": "Заполнять",
"container.prettypipes.item_terminal": "Предметный терминал",
"container.prettypipes.crafting_terminal": "Ремесленный терминал",
"container.prettypipes.pressurizer": "Трубный компенсатор давления",
"info.prettypipes.whitelist": "Разрешённый",
"info.prettypipes.blacklist": "Запрещённый",
"info.prettypipes.shift": "Зажмите Shift, для информации",
"info.prettypipes.populate": "Заполнить",
"info.prettypipes.populate.description": "Фильтрует предметы из соседних запасов",
"info.prettypipes.max_stack_size": "Максимальное количество предметов",
"info.prettypipes.limit_to_max_on": "Ограничить до 1 стака",
"info.prettypipes.limit_to_max_off": "Не ограничать до 1 стака",
"info.prettypipes.request": "Запрос",
"info.prettypipes.max_stack_size": "Макс. кол-во предмета",
"info.prettypipes.limit_to_max_on": "Ограничить на 1 стэк",
"info.prettypipes.limit_to_max_off": "Не ограничивать на 1 стэк",
"info.prettypipes.request": "Запросить",
"info.prettypipes.not_found": "%s не найден",
"info.prettypipes.sending": "Отправка %s %s",
"info.prettypipes.sending_ingredients": "Отправка наборов ингридиентов: %s",
"info.prettypipes.sending": "Отправление %s %s",
"info.prettypipes.sending_ingredients": "Отправление наборов ингридиентов: %s.",
"info.prettypipes.order": "Сортировать по %s",
"info.prettypipes.order.amount": "Количеству",
"info.prettypipes.order.name": "Имени",
"info.prettypipes.order.mod": "Моду",
"info.prettypipes.ascending": "Возростанию",
"info.prettypipes.descending": "Убыванию",
"info.prettypipes.sync_jei.on": "Синхронизировать поиск JEI",
"info.prettypipes.sync_jei.off": "Не синхронизировать поиск JEI"
"info.prettypipes.order.amount": "Кол-во",
"info.prettypipes.order.name": "Название",
"info.prettypipes.order.mod": "Мод",
"info.prettypipes.ascending": "Возрастание",
"info.prettypipes.descending": "Убывание",
"info.prettypipes.sync_jei.on": "Синхронизовать поиск JEI",
"info.prettypipes.sync_jei.off": "Не синхронизовать поиск JEI",
"info.prettypipes.energy": "%s / %s FE"
}

View file

@ -0,0 +1,7 @@
{
"parent": "block/cube_column",
"textures": {
"end": "prettypipes:block/pressurizer",
"side": "prettypipes:block/pressurizer"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "prettypipes:item/filter_increase_modifier"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "prettypipes:item/high_crafting_module"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "prettypipes:item/high_high_priority_module"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "prettypipes:item/low_crafting_module"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "prettypipes:item/low_high_priority_module"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "prettypipes:item/medium_crafting_module"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "prettypipes:item/medium_high_priority_module"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "prettypipes:item/mod_filter_modifier"
}
}

View file

@ -0,0 +1,3 @@
{
"parent": "prettypipes:block/pressurizer"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

View file

@ -0,0 +1,19 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "prettypipes:pressurizer"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
}

View file

@ -0,0 +1,22 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
" B ",
"RMR",
" R "
],
"key": {
"R": {
"item": "minecraft:redstone"
},
"B": {
"item": "minecraft:iron_bars"
},
"M": {
"item": "prettypipes:blank_module"
}
},
"result": {
"item": "prettypipes:filter_increase_modifier"
}
}

View file

@ -0,0 +1,22 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"GIG",
"GMG",
"GIG"
],
"key": {
"I": {
"item": "minecraft:iron_ingot"
},
"G": {
"item": "minecraft:gold_ingot"
},
"M": {
"item": "prettypipes:medium_crafting_module"
}
},
"result": {
"item": "prettypipes:high_crafting_module"
}
}

View file

@ -0,0 +1,19 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"CCC",
"CMC",
"CCC"
],
"key": {
"C": {
"item": "minecraft:paper"
},
"M": {
"item": "prettypipes:medium_high_priority_module"
}
},
"result": {
"item": "prettypipes:high_high_priority_module"
}
}

View file

@ -0,0 +1,22 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
" P ",
"RMR",
" R "
],
"key": {
"R": {
"item": "minecraft:redstone"
},
"P": {
"item": "minecraft:crafting_table"
},
"M": {
"item": "prettypipes:blank_module"
}
},
"result": {
"item": "prettypipes:low_crafting_module"
}
}

View file

@ -0,0 +1,22 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
" C ",
"RMR",
" R "
],
"key": {
"R": {
"item": "minecraft:redstone"
},
"C": {
"item": "minecraft:paper"
},
"M": {
"item": "prettypipes:blank_module"
}
},
"result": {
"item": "prettypipes:low_high_priority_module"
}
}

View file

@ -0,0 +1,22 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"GIG",
"IMI",
"GIG"
],
"key": {
"I": {
"item": "minecraft:iron_ingot"
},
"G": {
"item": "minecraft:gold_ingot"
},
"M": {
"item": "prettypipes:low_crafting_module"
}
},
"result": {
"item": "prettypipes:medium_crafting_module"
}
}

View file

@ -0,0 +1,19 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
" C ",
"CMC",
" C "
],
"key": {
"C": {
"item": "minecraft:paper"
},
"M": {
"item": "prettypipes:low_high_priority_module"
}
},
"result": {
"item": "prettypipes:medium_high_priority_module"
}
}

View file

@ -0,0 +1,22 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
" H ",
"RMR",
" R "
],
"key": {
"R": {
"item": "minecraft:redstone"
},
"H": {
"item": "minecraft:anvil"
},
"M": {
"item": "prettypipes:blank_module"
}
},
"result": {
"item": "prettypipes:mod_filter_modifier"
}
}

View file

@ -0,0 +1,22 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"TST",
"SRS",
"TST"
],
"key": {
"S": {
"item": "prettypipes:high_speed_module"
},
"R": {
"item": "minecraft:redstone_block"
},
"T": {
"item": "minecraft:iron_ingot"
}
},
"result": {
"item": "prettypipes:pressurizer"
}
}