diff --git a/src/main/java/de/ellpeck/prettypipes/Registry.java b/src/main/java/de/ellpeck/prettypipes/Registry.java index 0742d4c..213de31 100644 --- a/src/main/java/de/ellpeck/prettypipes/Registry.java +++ b/src/main/java/de/ellpeck/prettypipes/Registry.java @@ -1,14 +1,13 @@ package de.ellpeck.prettypipes; -import de.ellpeck.prettypipes.items.IModule; -import de.ellpeck.prettypipes.items.ModuleItem; +import de.ellpeck.prettypipes.entities.PipeFrameEntity; +import de.ellpeck.prettypipes.entities.PipeFrameRenderer; +import de.ellpeck.prettypipes.items.*; import de.ellpeck.prettypipes.pipe.modules.LowPriorityModuleItem; import de.ellpeck.prettypipes.pipe.modules.SpeedModuleItem; 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.items.ModuleTier; -import de.ellpeck.prettypipes.items.WrenchItem; import de.ellpeck.prettypipes.network.PipeNetwork; import de.ellpeck.prettypipes.packets.PacketHandler; import de.ellpeck.prettypipes.pipe.*; @@ -26,6 +25,8 @@ import net.minecraft.block.Block; import net.minecraft.client.gui.ScreenManager; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderTypeLookup; +import net.minecraft.entity.EntityClassification; +import net.minecraft.entity.EntityType; import net.minecraft.inventory.container.ContainerType; import net.minecraft.item.BlockItem; import net.minecraft.item.Item; @@ -41,6 +42,7 @@ import net.minecraftforge.common.extensions.IForgeContainerType; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.client.registry.ClientRegistry; +import net.minecraftforge.fml.client.registry.RenderingRegistry; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; @@ -72,6 +74,8 @@ public final class Registry { public static Block pipeBlock; public static TileEntityType pipeTileEntity; + public static EntityType pipeFrameEntity; + public static ContainerType pipeContainer; public static ContainerType extractionModuleContainer; public static ContainerType filterModuleContainer; @@ -90,7 +94,8 @@ public final class Registry { IForgeRegistry registry = event.getRegistry(); registry.registerAll( wrenchItem = new WrenchItem().setRegistryName("wrench"), - new Item(new Item.Properties().group(GROUP)).setRegistryName("blank_module") + new Item(new Item.Properties().group(GROUP)).setRegistryName("blank_module"), + new PipeFrameItem().setRegistryName("pipe_frame") ); registry.registerAll(createTieredModule("extraction_module", ExtractionModuleItem::new)); registry.registerAll(createTieredModule("filter_module", FilterModuleItem::new)); @@ -112,7 +117,14 @@ public final class Registry { } @SubscribeEvent - public static void registerContainer(RegistryEvent.Register> event) { + public static void registerEntities(RegistryEvent.Register> event) { + event.getRegistry().registerAll( + pipeFrameEntity = (EntityType) EntityType.Builder.create(PipeFrameEntity::new, EntityClassification.MISC).build("pipe_frame").setRegistryName("pipe_frame") + ); + } + + @SubscribeEvent + public static void registerContainers(RegistryEvent.Register> event) { event.getRegistry().registerAll( // this needs to be registered manually since it doesn't send the module slot pipeContainer = (ContainerType) IForgeContainerType.create((windowId, inv, data) -> new MainPipeContainer(pipeContainer, windowId, inv.player, data.readBlockPos())).setRegistryName("pipe"), @@ -159,6 +171,7 @@ public final class Registry { public static void setup(FMLClientSetupEvent event) { RenderTypeLookup.setRenderLayer(pipeBlock, RenderType.cutout()); ClientRegistry.bindTileEntityRenderer(pipeTileEntity, PipeRenderer::new); + RenderingRegistry.registerEntityRenderingHandler(pipeFrameEntity, PipeFrameRenderer::new); ScreenManager.registerFactory(pipeContainer, MainPipeGui::new); ScreenManager.registerFactory(extractionModuleContainer, ExtractionModuleGui::new); diff --git a/src/main/java/de/ellpeck/prettypipes/entities/PipeFrameEntity.java b/src/main/java/de/ellpeck/prettypipes/entities/PipeFrameEntity.java new file mode 100644 index 0000000..88b6e8b --- /dev/null +++ b/src/main/java/de/ellpeck/prettypipes/entities/PipeFrameEntity.java @@ -0,0 +1,66 @@ +package de.ellpeck.prettypipes.entities; + +import de.ellpeck.prettypipes.network.NetworkLocation; +import de.ellpeck.prettypipes.network.PipeNetwork; +import de.ellpeck.prettypipes.pipe.PipeTileEntity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.item.ItemFrameEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.network.IPacket; +import net.minecraft.network.datasync.DataParameter; +import net.minecraft.network.datasync.DataSerializers; +import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.fml.network.NetworkHooks; + +import java.util.List; + +public class PipeFrameEntity extends ItemFrameEntity { + + private static final DataParameter AMOUNT = EntityDataManager.createKey(PipeFrameEntity.class, DataSerializers.VARINT); + + public PipeFrameEntity(EntityType type, World world) { + super(type, world); + } + + public PipeFrameEntity(EntityType type, World world, BlockPos pos, Direction dir) { + this(type, world); + this.hangingPosition = pos; + this.updateFacingWithBoundingBox(dir); + } + + @Override + protected void registerData() { + super.registerData(); + this.dataManager.register(AMOUNT, 0); + } + + @Override + public void tick() { + super.tick(); + if (this.world.isRemote) + return; + if (this.ticksExisted % 40 != 0) + return; + PipeNetwork network = PipeNetwork.get(this.world); + BlockPos hangingPos = this.getHangingPosition().offset(this.getHorizontalFacing().getOpposite()); + BlockPos node = network.getNodeFromPipe(hangingPos); + if (node == null) + return; + ItemStack stack = this.getDisplayedItem(); + List items = network.getOrderedNetworkItems(node); + int amount = items.stream().mapToInt(i -> i.getItemAmount(stack)).sum(); + this.dataManager.set(AMOUNT, amount); + } + + public int getAmount() { + return this.dataManager.get(AMOUNT); + } + + @Override + public IPacket createSpawnPacket() { + return NetworkHooks.getEntitySpawningPacket(this); + } +} diff --git a/src/main/java/de/ellpeck/prettypipes/entities/PipeFrameRenderer.java b/src/main/java/de/ellpeck/prettypipes/entities/PipeFrameRenderer.java new file mode 100644 index 0000000..453608d --- /dev/null +++ b/src/main/java/de/ellpeck/prettypipes/entities/PipeFrameRenderer.java @@ -0,0 +1,45 @@ +package de.ellpeck.prettypipes.entities; + +import com.mojang.blaze3d.matrix.MatrixStack; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.renderer.IRenderTypeBuffer; +import net.minecraft.client.renderer.ItemRenderer; +import net.minecraft.client.renderer.Matrix4f; +import net.minecraft.client.renderer.Vector3f; +import net.minecraft.client.renderer.entity.EntityRendererManager; +import net.minecraft.client.renderer.entity.ItemFrameRenderer; +import net.minecraft.entity.item.ItemFrameEntity; +import net.minecraft.util.Direction; +import net.minecraft.util.math.Vec3d; + +public class PipeFrameRenderer extends ItemFrameRenderer { + public PipeFrameRenderer(EntityRendererManager renderManagerIn) { + super(renderManagerIn, Minecraft.getInstance().getItemRenderer()); + } + + @Override + public void render(ItemFrameEntity entityIn, float entityYaw, float partialTicks, MatrixStack matrixStackIn, IRenderTypeBuffer bufferIn, int packedLightIn) { + super.render(entityIn, entityYaw, partialTicks, matrixStackIn, bufferIn, packedLightIn); + if (entityIn.getDisplayedItem().isEmpty()) + return; + matrixStackIn.push(); + Direction direction = entityIn.getHorizontalFacing(); + Vec3d vec3d = this.getRenderOffset(entityIn, partialTicks); + matrixStackIn.translate(-vec3d.getX(), -vec3d.getY(), -vec3d.getZ()); + matrixStackIn.translate(direction.getXOffset() * 0.05, direction.getYOffset() * 0.05, direction.getZOffset() * 0.05); + matrixStackIn.rotate(Vector3f.XP.rotationDegrees(entityIn.rotationPitch)); + matrixStackIn.rotate(Vector3f.YP.rotationDegrees(180.0F - entityIn.rotationYaw)); + + FontRenderer font = this.getFontRendererFromRenderManager(); + int amount = ((PipeFrameEntity) entityIn).getAmount(); + String ammountStrg = String.valueOf(amount); + float x = 0.5F - font.getStringWidth(ammountStrg) / 2F; + Matrix4f matrix4f = matrixStackIn.getLast().getPositionMatrix(); + matrixStackIn.translate(0, -0.13F, 0); + matrixStackIn.scale(-0.02F, -0.02F, 0.02F); + font.renderString(ammountStrg, x, 0, 0xFFFFFF, true, matrix4f, bufferIn, false, 0, packedLightIn); + + matrixStackIn.pop(); + } +} diff --git a/src/main/java/de/ellpeck/prettypipes/items/PipeFrameItem.java b/src/main/java/de/ellpeck/prettypipes/items/PipeFrameItem.java new file mode 100644 index 0000000..fa3a767 --- /dev/null +++ b/src/main/java/de/ellpeck/prettypipes/items/PipeFrameItem.java @@ -0,0 +1,65 @@ +package de.ellpeck.prettypipes.items; + +import de.ellpeck.prettypipes.Registry; +import de.ellpeck.prettypipes.entities.PipeFrameEntity; +import de.ellpeck.prettypipes.pipe.PipeBlock; +import net.minecraft.block.BlockState; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.item.HangingEntity; +import net.minecraft.entity.item.ItemFrameEntity; +import net.minecraft.entity.item.PaintingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemUseContext; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.ActionResultType; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class PipeFrameItem extends Item { + public PipeFrameItem() { + super(new Properties().group(Registry.GROUP)); + } + + // HangingEntityItem copypasta mostly, since it hardcodes the entities bleh + @Override + public ActionResultType onItemUse(ItemUseContext context) { + BlockPos blockpos = context.getPos(); + Direction direction = context.getFace(); + BlockPos blockpos1 = blockpos.offset(direction); + PlayerEntity playerentity = context.getPlayer(); + ItemStack itemstack = context.getItem(); + if (playerentity != null && !this.canPlace(playerentity, direction, itemstack, blockpos1)) { + return ActionResultType.FAIL; + } else { + World world = context.getWorld(); + HangingEntity hangingentity = new PipeFrameEntity(Registry.pipeFrameEntity, world, blockpos1, direction); + + CompoundNBT compoundnbt = itemstack.getTag(); + if (compoundnbt != null) { + EntityType.applyItemNBT(world, playerentity, hangingentity, compoundnbt); + } + + if (hangingentity.onValidSurface()) { + if (!world.isRemote) { + hangingentity.playPlaceSound(); + world.addEntity(hangingentity); + } + + itemstack.shrink(1); + return ActionResultType.SUCCESS; + } else { + return ActionResultType.CONSUME; + } + } + } + + protected boolean canPlace(PlayerEntity playerIn, Direction directionIn, ItemStack itemStackIn, BlockPos posIn) { + BlockState state = playerIn.world.getBlockState(posIn.offset(directionIn.getOpposite())); + if (!(state.getBlock() instanceof PipeBlock)) + return false; + return !directionIn.getAxis().isVertical() && playerIn.canPlayerEdit(posIn, directionIn, itemStackIn); + } +} diff --git a/src/main/java/de/ellpeck/prettypipes/network/NetworkEdge.java b/src/main/java/de/ellpeck/prettypipes/network/NetworkEdge.java index e643383..74ee0df 100644 --- a/src/main/java/de/ellpeck/prettypipes/network/NetworkEdge.java +++ b/src/main/java/de/ellpeck/prettypipes/network/NetworkEdge.java @@ -13,8 +13,6 @@ import java.util.List; public class NetworkEdge extends DefaultWeightedEdge implements INBTSerializable { - public BlockPos startPipe; - public BlockPos endPipe; public final List pipes = new ArrayList<>(); public NetworkEdge() { @@ -24,11 +22,17 @@ public class NetworkEdge extends DefaultWeightedEdge implements INBTSerializable this.deserializeNBT(nbt); } + public BlockPos getStartPipe() { + return this.pipes.get(0); + } + + public BlockPos getEndPipe() { + return this.pipes.get(this.pipes.size() - 1); + } + @Override public CompoundNBT serializeNBT() { CompoundNBT nbt = new CompoundNBT(); - nbt.put("start", NBTUtil.writeBlockPos(this.startPipe)); - nbt.put("end", NBTUtil.writeBlockPos(this.endPipe)); ListNBT list = new ListNBT(); for (BlockPos pos : this.pipes) list.add(NBTUtil.writeBlockPos(pos)); @@ -38,8 +42,6 @@ public class NetworkEdge extends DefaultWeightedEdge implements INBTSerializable @Override public void deserializeNBT(CompoundNBT nbt) { - this.startPipe = NBTUtil.readBlockPos(nbt.getCompound("start")); - this.endPipe = NBTUtil.readBlockPos(nbt.getCompound("end")); this.pipes.clear(); ListNBT list = nbt.getList("pipes", Constants.NBT.TAG_COMPOUND); for (int i = 0; i < list.size(); i++) diff --git a/src/main/java/de/ellpeck/prettypipes/network/NetworkLocation.java b/src/main/java/de/ellpeck/prettypipes/network/NetworkLocation.java index 2d87376..4a66b3d 100644 --- a/src/main/java/de/ellpeck/prettypipes/network/NetworkLocation.java +++ b/src/main/java/de/ellpeck/prettypipes/network/NetworkLocation.java @@ -44,6 +44,12 @@ public class NetworkLocation { return -1; } + public int getItemAmount(ItemStack stack) { + return this.items.values().stream() + .filter(i -> i.isItemEqual(stack)) + .mapToInt(ItemStack::getCount).sum(); + } + public boolean isEmpty() { return this.items == null || this.items.isEmpty(); } diff --git a/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java b/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java index 3e349be..a0fb1b6 100644 --- a/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java +++ b/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java @@ -115,8 +115,10 @@ public class PipeNetwork implements ICapabilitySerializable, GraphL // if we only have one neighbor, then there can't be any new connections if (neighbors.size() <= 1 && !this.isNode(pos)) return; - for (NetworkEdge edge : neighbors) - this.refreshNode(edge.endPipe, this.world.getBlockState(edge.endPipe)); + for (NetworkEdge edge : neighbors) { + BlockPos end = edge.getEndPipe(); + this.refreshNode(end, this.world.getBlockState(end)); + } } public boolean tryInsertItem(BlockPos startPipePos, BlockPos startInventory, ItemStack stack, boolean preventOversending) { @@ -132,7 +134,9 @@ public class PipeNetwork implements ICapabilitySerializable, GraphL if (startPipe == null) return false; this.startProfile("find_destination"); - for (BlockPos pipePos : this.getOrderedDestinations(startPipePos)) { + for (BlockPos pipePos : this.getOrderedNetworkNodes(startPipePos)) { + if (pipePos.equals(startPipePos)) + continue; PipeTileEntity pipe = this.getPipe(pipePos); BlockPos dest = pipe.getAvailableDestination(stack, false, preventOversending); if (dest != null) { @@ -179,7 +183,7 @@ public class PipeNetwork implements ICapabilitySerializable, GraphL return Collections.emptyList(); this.startProfile("get_network_items"); List info = new ArrayList<>(); - for (BlockPos dest : this.getOrderedDestinations(node)) { + for (BlockPos dest : this.getOrderedNetworkNodes(node)) { PipeTileEntity pipe = this.getPipe(dest); if (!pipe.canNetworkSee()) continue; @@ -214,16 +218,30 @@ public class PipeNetwork implements ICapabilitySerializable, GraphL } private void addEdge(NetworkEdge edge) { - this.graph.addEdge(edge.startPipe, edge.endPipe, edge); + this.graph.addEdge(edge.getStartPipe(), edge.getEndPipe(), edge); // only use size - 1 so that nodes aren't counted twice for multi-edge paths this.graph.setEdgeWeight(edge, edge.pipes.size() - 1); } - private List createAllEdges(BlockPos pos, BlockState state, boolean allAround) { + public BlockPos getNodeFromPipe(BlockPos pos) { + if (this.isNode(pos)) + return pos; + BlockState state = this.world.getBlockState(pos); + if (!(state.getBlock() instanceof PipeBlock)) + return null; + for (Direction dir : Direction.values()) { + NetworkEdge edge = this.createEdge(pos, state, dir, false); + if (edge != null) + return edge.getEndPipe(); + } + return null; + } + + private List createAllEdges(BlockPos pos, BlockState state, boolean ignoreCurrBlocked) { this.startProfile("create_all_edges"); List edges = new ArrayList<>(); for (Direction dir : Direction.values()) { - NetworkEdge edge = this.createEdge(pos, state, dir, allAround); + NetworkEdge edge = this.createEdge(pos, state, dir, ignoreCurrBlocked); if (edge != null) edges.add(edge); } @@ -231,8 +249,8 @@ public class PipeNetwork implements ICapabilitySerializable, GraphL return edges; } - private NetworkEdge createEdge(BlockPos pos, BlockState state, Direction dir, boolean allAround) { - if (!allAround && !state.get(PipeBlock.DIRECTIONS.get(dir)).isConnected()) + private NetworkEdge createEdge(BlockPos pos, BlockState state, Direction dir, boolean ignoreCurrBlocked) { + if (!ignoreCurrBlocked && !state.get(PipeBlock.DIRECTIONS.get(dir)).isConnected()) return null; BlockPos currPos = pos.offset(dir); BlockState currState = this.world.getBlockState(currPos); @@ -240,7 +258,6 @@ public class PipeNetwork implements ICapabilitySerializable, GraphL return null; this.startProfile("create_edge"); NetworkEdge edge = new NetworkEdge(); - edge.startPipe = pos; edge.pipes.add(pos); edge.pipes.add(currPos); @@ -248,7 +265,6 @@ public class PipeNetwork implements ICapabilitySerializable, GraphL // if we found a vertex, we can stop since that's the next node // we do this here since the first offset pipe also needs to check this if (this.isNode(currPos)) { - edge.endPipe = edge.pipes.get(edge.pipes.size() - 1); this.endProfile(); return edge; } @@ -276,7 +292,7 @@ public class PipeNetwork implements ICapabilitySerializable, GraphL return null; } - private List getOrderedDestinations(BlockPos node) { + private List getOrderedNetworkNodes(BlockPos node) { List ret = this.nodeToConnectedNodes.get(node); if (ret == null) { this.startProfile("compile_connected_nodes"); @@ -284,7 +300,6 @@ public class PipeNetwork implements ICapabilitySerializable, 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 -> !p.equals(node)) .sorted(Comparator.comparingInt(p -> this.getPipe(p).getPriority()).reversed().thenComparing(paths::getWeight)) .collect(Collectors.toList()); this.nodeToConnectedNodes.put(node, ret); @@ -296,9 +311,9 @@ public class PipeNetwork implements ICapabilitySerializable, GraphL public void clearDestinationCache(BlockPos... nodes) { this.startProfile("clear_node_cache"); // remove caches for the nodes - for(BlockPos node : nodes) + for (BlockPos node : nodes) this.nodeToConnectedNodes.keySet().remove(node); - // remove caches that contain the node as a destination + // remove caches that contain the nodes as a destination this.nodeToConnectedNodes.values().removeIf(cached -> Arrays.stream(nodes).anyMatch(cached::contains)); this.endProfile(); } diff --git a/src/main/java/de/ellpeck/prettypipes/pipe/modules/retrieval/RetrievalModuleItem.java b/src/main/java/de/ellpeck/prettypipes/pipe/modules/retrieval/RetrievalModuleItem.java index f9767da..db7b5fd 100644 --- a/src/main/java/de/ellpeck/prettypipes/pipe/modules/retrieval/RetrievalModuleItem.java +++ b/src/main/java/de/ellpeck/prettypipes/pipe/modules/retrieval/RetrievalModuleItem.java @@ -57,6 +57,8 @@ public class RetrievalModuleItem extends ModuleItem { if (locations == null) locations = network.getOrderedNetworkItems(tile.getPos()); for (NetworkLocation location : locations) { + if (location.pipePos.equals(tile.getPos())) + continue; int slot = location.getStackSlot(filtered); if (slot < 0) continue;