Jump to content

1.8 Pipes


Noxyper

Recommended Posts

Alrighty then!

So, for my mod I want to implement pipes to carry steam from a boiler to other machines. But I'm having some trouble. Maybe I am jumping straight into this, but I'm an okay programmer. I can the basics: armor, items, guis, mobs. But now I want to move on to the more advanced stuff! So, here's the deal. I'll be posting on this post everytime I run into trouble and maybe you guys could help? So my first problem is how to make these pipes connect to one another. I looked at the redstone code, but that just messes with my head.

So, just to recap, my first problem is how do I make my pipes change state and connect to each other?

 

Thank you!

Hyp3r

Link to comment
Share on other sites

I have a basic implementation of a pipe-like block here (blockstates file).

 

I use some Java 8 features like streams and lambdas in the code, you'll have to adapt it if you're not compiling against Java 8.

 

I haven't got the bounding boxes working, but the pipes know they're connected to each other and render the model properly.

 

Since your pipe block will likely have a

TileEntity

, you may want to cache the connection state in it (like BuildCraft pipes do) and make

Block#getActualState

return the values from that rather than checking each neighbouring block.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

Just so that I haven't wasted my time, I have to make .json files for all the different ways the pipe can connect, right? But 1 blockstate. That is how Mojang did with the redstone so I'm just assuming...

 

Look at the blockstates file I linked. It only uses two models (centre cube + side connection) but uses Forge's blockstates format to combine them as needed.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

So how do I go about making the pipe part? How would that look like?

 

You mean actually moving the steam from point A to point B? I can't really help you much there apart from suggesting you look at open source mods that do this kind of thing, e.g. BuildCraft or Power Advantage.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

No, how would that model look like? If it is going to be universal, how would I go about modelling it? Or am I just misunderstanding your posts?

 

The models for the pipe are in the same repository I linked before: pipe_centre, pipe_part.

 

All pipes with the same shape can use these models, the textures are controlled by the blockstates file for each pipe block.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

So, now I've messed around a bit with what you said and I got the bounding boxes to work, but the pipes don't connect.

 

I attached a picture so that you could see what I mean:

fEBpM2r.png

 

And here is the .json files:

iron_pipe_side.json (model)

{
    "textures": {
        "particle": "#side"
    },
    "elements": [
        {
            "name": "Cube",
            "from": [ 5.0, 5.0, 5.0 ], 
            "to": [ 11.0, 11.0, 11.0 ], 
            "faces": {
                "north": { "texture": "#side" },
                "east": { "texture": "#side" },
                "south": { "texture": "#side" },
                "west": { "texture": "#side" },
                "up": { "texture": "#side" },
                "down": { "texture": "#side" }
            }
        }
    ]
}

 

iron_pipe_centre.json (model)

{
    "textures": {
        "particle": "#centre"
    },
    "elements": [
        {
            "name": "Cube",
            "from": [ 5.0, 5.0, 5.0 ], 
            "to": [ 11.0, 11.0, 11.0 ], 
            "faces": {
                "north": { "texture": "#centre" },
                "east": { "texture": "#centre" },
                "south": { "texture": "#centre" },
                "west": { "texture": "#centre" },
                "up": { "texture": "#centre" },
                "down": { "texture": "#centre" }
            }
        }
    ]
}

 

iron_pipe.json (blockstate)

{
    "forge_marker": 1,
    "defaults": {
        "textures": {
            "centre": "blocks/iron_block",
            "side": "blocks/iron_block"
        },
        "model": "gnc:iron_pipe_centre",
        "uvlock": true
    },
    "variants": {
        "down": {
            "true": {
                "submodel": {
                    "pipe_down": {
                        "model": "gnc:iron_pipe_side",
                        "x": 90,
                        "uvlock": true
                    }
                }
            },
            "false": { }
        },
        "up": {
            "true": {
                "submodel": {
                    "pipe_up": {
                        "model": "gnc:iron_pipe_side",
                        "x": -90,
                        "uvlock": true
                    }
                }
            },
            "false": { }
        },
        "north": {
            "true": {
                "submodel": {
                    "pipe_north": {
                        "model": "gnc:iron_pipe_side",
                        "uvlock": true
                    }
                }
            },
            "false": { }
        },
        "south": {
            "true": {
                "submodel": {
                    "pipe_south": {
                        "model": "gnc:iron_pipe_side",
                        "y": 180,
                        "uvlock": true
                    }
                }
            },
            "false": { }
        },
        "east": {
            "true": {
                "submodel": {
                    "pipe_east": {
                        "model": "gnc:iron_pipe_side",
                        "y": 90,
                        "uvlock": true
                    }
                }
            },
            "false": { }
        },
        "west": {
            "true": {
                "submodel": {
                    "pipe_west": {
                        "model": "gnc:iron_pipe_side",
                        "y": -90,
                        "uvlock": true
                    }
                }
            },
            "false": { }
        }
    }
}

Link to comment
Share on other sites

Here's the getActualState code

	@Override
public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos) {
	for (EnumFacing facing : EnumFacing.VALUES) {
		state = state.withProperty(CONNECTED_PROPERTIES.get(facing.getIndex()), canConnectTo(state, world, pos, facing));
	}

	return state;
}

Link to comment
Share on other sites

Yes, posting the full code on Gist or Pastebin would help. Could you also take a screenshot of the debug information while looking at a pipe with at least one pipe next to it and post that?

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

You've messed up the dimensions of the side model. You've made them identical to the centre model, so each connected side is just rendering the centre cube again.

 

My side model's element goes from 4,4,0 to 12,12,4 (note the unique z coordinates); yours goes from 5,5,5 to 11,11,11.

 

Edit: On a side note, why is your GitHub repository a bunch of unorganised files without file extensions? Why not use your mod's actual working directory as the repository?

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

So now that THAT problem is fixed, I have another issue. Choonster touched upon this briefly and it is the transport of steam power through the pipes. I would rather not use any API, but if I have to, it is fine.

 

As this is a bit bigger, I'll need help with a few different things. First off, where in the BlockPipeBase.java code can I add in the possibility of my pipes connecting to the machines? The code is posted below:

 

BlockPipeBase.java

package com.minenemo.gearsandclouds.blocks;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.Vec3i;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;

import com.google.common.collect.ImmutableList;

public abstract class BlockPipeBase extends Block {
public static final float PIPE_MIN_POS = 0.3125f;
public static final float PIPE_MAX_POS = 0.6875f;

public static final ImmutableList<IProperty> CONNECTED_PROPERTIES = ImmutableList.copyOf(Stream.of(EnumFacing.VALUES).map(facing -> PropertyBool.create(facing.getName())).collect(Collectors.toList()));

public static final ImmutableList<AxisAlignedBB> CONNECTED_BOUNDING_BOXES = ImmutableList.copyOf(Stream.of(EnumFacing.VALUES).map(facing -> {Vec3i directionVec = facing.getDirectionVec();return new AxisAlignedBB(getMinBound(directionVec.getX()), getMinBound(directionVec.getY()), getMinBound(directionVec.getZ()),getMaxBound(directionVec.getX()), getMaxBound(directionVec.getY()), getMaxBound(directionVec.getZ()));}).collect(Collectors.toList()));

private static float getMinBound(int dir) {
	return dir == -1 ? 0 : PIPE_MIN_POS;
}

private static float getMaxBound(int dir) {
	return dir == 1 ? 1 : PIPE_MAX_POS;
}

protected BlockPipeBase(Material material) {
	super(material);
}

@Override
public boolean isOpaqueCube() {
	return false;
}

protected boolean isValidPipe(IBlockState ownState, IBlockState neighbourState, IBlockAccess world, BlockPos ownPos, EnumFacing neighbourDirection) {
	return neighbourState.getBlock() instanceof BlockPipeBase;
}

private boolean canConnectTo(IBlockState ownState, IBlockAccess worldIn, BlockPos ownPos, EnumFacing neighbourDirection) {
	BlockPos neighbourPos = ownPos.offset(neighbourDirection);
	IBlockState neighbourState = worldIn.getBlockState(neighbourPos);
	Block neighbourBlock = neighbourState.getBlock();

	boolean neighbourIsValidForThis = isValidPipe(ownState, neighbourState, worldIn, ownPos, neighbourDirection);
	boolean thisIsValidForNeighbour = neighbourBlock instanceof BlockPipeBase && ((BlockPipeBase) neighbourBlock).isValidPipe(neighbourState, ownState, worldIn, neighbourPos, neighbourDirection.getOpposite());

	return neighbourIsValidForThis && thisIsValidForNeighbour;
}

@Override
public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos) {
	for (EnumFacing facing : EnumFacing.VALUES) {
		state = state.withProperty(CONNECTED_PROPERTIES.get(facing.getIndex()), canConnectTo(state, world, pos, facing));
	}

	return state;
}

public final boolean isConnected(IBlockState state, EnumFacing facing) {
	return (boolean) state.getValue(CONNECTED_PROPERTIES.get(facing.getIndex()));
}

public void setBlockBounds(AxisAlignedBB bb) {
	setBlockBounds((float) bb.minX, (float) bb.minY, (float) bb.minZ, (float) bb.maxX, (float) bb.maxY, (float) bb.maxZ);
}

@Override
public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List list, Entity collidingEntity) {
	setBlockBounds(PIPE_MIN_POS, PIPE_MIN_POS, PIPE_MIN_POS, PIPE_MAX_POS, PIPE_MAX_POS, PIPE_MAX_POS);
	super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);

	state = getActualState(state, worldIn, pos);

	for (EnumFacing facing : EnumFacing.VALUES) {
		if (isConnected(state, facing)) {
			AxisAlignedBB axisAlignedBB = CONNECTED_BOUNDING_BOXES.get(facing.getIndex());
			setBlockBounds(axisAlignedBB);
			super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
		}
	}

	setBlockBounds(PIPE_MIN_POS, PIPE_MIN_POS, PIPE_MIN_POS, PIPE_MAX_POS, PIPE_MAX_POS, PIPE_MAX_POS);
}
}

 

I'd assume it is in the canConnectTo() method (and maybe the isValidPipe() method) but I don't know how I'm supposed to go about this. I only want the pipe to be able to connect to the boiler from above.

Link to comment
Share on other sites

Wow, now I feel stupid. Well, that's programming. Something that easy is always hiding under your nose.

Thanks!

 

There are 4 major bugs in programming:

  • Syntax errors
  • Logic errors
  • And off by 1 errors

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

One problem done, next one! I've messed around with my code to try to connect these pipes to a machine, such as my boiler. The problem here though, is that it isn't working. At all.

Here's the code:

package com.minenemo.gearsandclouds.blocks;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.Vec3i;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;

import com.google.common.collect.ImmutableList;

public abstract class BlockPipeBase extends Block {
public static final float PIPE_MIN_POS = 0.3125f;
public static final float PIPE_MAX_POS = 0.6875f;

public static final ImmutableList<IProperty> CONNECTED_PROPERTIES = ImmutableList.copyOf(Stream.of(EnumFacing.VALUES).map(facing -> PropertyBool.create(facing.getName())).collect(Collectors.toList()));

public static final ImmutableList<AxisAlignedBB> CONNECTED_BOUNDING_BOXES = ImmutableList.copyOf(Stream.of(EnumFacing.VALUES).map(facing -> {Vec3i directionVec = facing.getDirectionVec();return new AxisAlignedBB(getMinBound(directionVec.getX()), getMinBound(directionVec.getY()), getMinBound(directionVec.getZ()),getMaxBound(directionVec.getX()), getMaxBound(directionVec.getY()), getMaxBound(directionVec.getZ()));}).collect(Collectors.toList()));

private static float getMinBound(int dir) {
	return dir == -1 ? 0 : PIPE_MIN_POS;
}

private static float getMaxBound(int dir) {
	return dir == 1 ? 1 : PIPE_MAX_POS;
}

protected BlockPipeBase(Material material) {
	super(material);
}

@Override
public boolean isOpaqueCube() {
	return false;
}

protected boolean isValidPipe(IBlockState ownState, IBlockState neighbourState, IBlockAccess world, BlockPos ownPos, EnumFacing neighbourDirection) {
	return neighbourState.getBlock() instanceof BlockPipeBase || neighbourState.getBlock() instanceof BlockBoiler || neighbourState.getBlock() instanceof BlockRuster;
}

private boolean canConnectTo(IBlockState ownState, IBlockAccess worldIn, BlockPos ownPos, EnumFacing neighbourDirection) {
	BlockPos neighbourPos = ownPos.offset(neighbourDirection);
	IBlockState neighbourState = worldIn.getBlockState(neighbourPos);
	Block neighbourBlock = neighbourState.getBlock();

	boolean neighbourIsValidForThis = isValidPipe(ownState, neighbourState, worldIn, ownPos, neighbourDirection);
	boolean pipeIsValidForNeighbour = neighbourBlock instanceof BlockPipeBase && ((BlockPipeBase) neighbourBlock).isValidPipe(neighbourState, ownState, worldIn, neighbourPos, neighbourDirection.getOpposite());
	boolean rusterIsValidForNeighbour = neighbourBlock instanceof BlockRuster && ((BlockRuster) neighbourBlock).isValidPipe(neighbourState, ownState, worldIn, neighbourPos, neighbourDirection.getOpposite());
	boolean boilerIsValidForNeighbour = neighbourBlock instanceof BlockBoiler && ((BlockBoiler) neighbourBlock).isValidPipe(neighbourState, ownState, worldIn, neighbourPos, neighbourDirection.getOpposite());
	boolean thisIsValidForNeighbour = pipeIsValidForNeighbour || rusterIsValidForNeighbour || boilerIsValidForNeighbour;

	return neighbourIsValidForThis && thisIsValidForNeighbour;
}

@Override
public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos) {
	for (EnumFacing facing : EnumFacing.VALUES) {
		state = state.withProperty(CONNECTED_PROPERTIES.get(facing.getIndex()), canConnectTo(state, world, pos, facing));
	}

	return state;
}

public final boolean isConnected(IBlockState state, EnumFacing facing) {
	return (boolean) state.getValue(CONNECTED_PROPERTIES.get(facing.getIndex()));
}

public void setBlockBounds(AxisAlignedBB bb) {
	setBlockBounds((float) bb.minX, (float) bb.minY, (float) bb.minZ, (float) bb.maxX, (float) bb.maxY, (float) bb.maxZ);
}

@Override
public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List list, Entity collidingEntity) {
	setBlockBounds(PIPE_MIN_POS, PIPE_MIN_POS, PIPE_MIN_POS, PIPE_MAX_POS, PIPE_MAX_POS, PIPE_MAX_POS);
	super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);

	state = getActualState(state, worldIn, pos);

	for (EnumFacing facing : EnumFacing.VALUES) {
		if (isConnected(state, facing)) {
			AxisAlignedBB axisAlignedBB = CONNECTED_BOUNDING_BOXES.get(facing.getIndex());
			setBlockBounds(axisAlignedBB);
			super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
		}
	}

	setBlockBounds(PIPE_MIN_POS, PIPE_MIN_POS, PIPE_MIN_POS, PIPE_MAX_POS, PIPE_MAX_POS, PIPE_MAX_POS);
}
}

Link to comment
Share on other sites

You'll want to change

canConnectTo

to allow valid connections even if the neighbouring block isn't another pipe and then override

isValidPipe

in the sub class to check if the neighbouring block is a pipe or something that can handle steam. I'd recommend implementing a common interface in your steam-handling blocks or TileEntities and checking for this interface in

isValidPipe

.

 

If your steam is implemented as a Forge

Fluid

, your steam-handling TileEntities should implement

IFluidHandler

and you should check for this interface in

isValidPipe

.

 

You can see my implementation of a fluid pipe block (plus the changes to

BlockPipeBase

) here. Note that this just connects visually to any block with a

IFluidHandler

TileEntity, it doesn't actually move fluid anywhere.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

Alright, that should be working now, but one last question before I move on to the brain of this problem. How can I make my iron pipe and brass pipe not connect?

 

Override

isValidPipe

/

isValidConnection

to check if the neighbouring block is an instance of a specific pipe class instead of just

BlockPipeBase

.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

Thanks!

Now, lastly, how can I make these pipes transport steam from one block to the other? I understand how I can start it off, by adding like a if(world.getBlockState(pos.up) == GnCBlocks.iron_pipe) { but it is in here I'm having troubles. } What do I put in that if-statement?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.