#[Solved] Questions about multipart block

88 messages ยท Page 1 of 1 (latest)

plucky cloud
#
  1. How do I ensure that I can't place my multipart block into itself like this? I tried this:
    protected boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) {
        BlockPos blockPos = pos.down();
        Direction dir = state.get(FACING).rotateYClockwise();
        BlockPos underBlock1 = blockPos.offset(dir);
        BlockPos underBlock2 = blockPos.offset(dir.getOpposite());
        BlockPos Block1 = underBlock1.up();
        BlockPos Block2 = underBlock2.up();
        Boolean a = this.canPlaceAbove(world,blockPos, world.getBlockState(blockPos));
        Boolean b = this.canPlaceAbove(world,underBlock1, world.getBlockState(underBlock1));
        Boolean c = this.canPlaceAbove(world,underBlock2, world.getBlockState(underBlock2));
        Boolean d = world.getBlockState(Block1).isAir();
        Boolean e = world.getBlockState(Block2).isAir();
        return (a && b && c && d && e);```
But Boolean d and e ensures the middle part of the block popped off on placement, but without them, it can be placed into itself like on the picture.

2. how do I make my multipart block drop an item when broken in survival? The usual json doesn't work, so I'm missing something, but idk what.
plucky cloud
#

[Unsolved] Questions about multipart block

plucky cloud
#

I haven't solved this yet :,)

plucky cloud
#

I need help ๐Ÿ˜ญ

carmine nova
#

json works fine, be sure its in the right place

#

not sure what the intent is, tbh. i would try to help but its late and trying to decifer your variable names is more than i am willing to offer

How do I ensure that I can't place my multipart block into itself like this?
like what? exactly? which part is not ok?

describe your invalid states in sentences
if more specific, describe your valid states in sentences

plucky cloud
# carmine nova not sure what the intent is, tbh. i would try to help but its late and trying to...

My bad, I'll try to be more specific now:

Is there something else messing with the block drops? I've placed the json file for this block in the same location as for my other blocks, and they drop just fine, but this one doesn't.

My block is seen as it is supposed to be on the left of my image! When placed, the part with the plus (so, the middle part) is placed, and then the other parts are placed to the left and right of it. I want my block to only be placeable when:

  • there are 3 blocks underneath it to support it (that's what booleans a, b and c are for)
  • the side blocks wouldn't override an existing block (that's what d and e we're supposed to be for, but they're the problem rn).

The block is currently not able to be placed in an area that doesn't have a 3 wide area (so that works as intended), but on the image you see that I placed 2 of my blocks into each other, as on the right, my block's left part has overridden the middle part of another block. I've also noticed that when placed, my block would replace other blocks too, like a grass block!

carmine nova
#

The block is currently not able to be placed in an area that doesn't have a 3 wide area

this does not appear to be working as intended if it can place while you have one of those 3 blocks as something other than air, right?

#

in the right image, you placed 1 block and it put the 3 blocks down, but you were able to place it again in a space that did not have 3 blocks free

plucky cloud
#

Yes, I want my block to be placeable if there are 3 blocks underneath it to support it, similar to how a redstone repeater needs a block under itself to support it, and I want the blocks next to the placement location (to the right and left) to be air, so I can't place it into itself like shown in the image

carmine nova
#

i would say the canPlaceAt should check for supporting block while the getPlacementState should check for the air blocks of the side as you dont have directionality in the canPlaceAt method, right?

plucky cloud
#

I do think there's directionality, that's how I get the bottom supporting blocks, including the side blocks. But if I check for the side blocks being air, then the middle pops off as soon as the blocks are placed, presumably because its own side blocks are not air. But if I make an exception for itself, then I can place it into itself again.

#

And if I check in getPlacementState for if the side blocks should be placed, then I could end up with an accidental 2 wide block because one side couldn't be placed. That wouldn't be right either

carmine nova
#

canPlaceAt should check if the block can be supported
getPlacementState should check if the side blocks can be placed (including a check for canPlace at for the sides) and if not return null
onPlaced should set the side blocks
getStateForNeighborUpdate should check if it is in a valid configuration and if not should pop off

plucky cloud
#

Wouldn't returning null in getPlacementState cause the block to not be placed 3 wide like it should, but leave away a part of the block? Or am I misunderstanding what you mean?

carmine nova
#

returning null will cause it to not be placed. if it cannot place 3 wide, it should not place

plucky cloud
#

Ohh, okay! I'll try it out!

carmine nova
#

remember, the side blocks are placed in the onPlaced for the center block
the center block is the controlling block here, right?

plucky cloud
#

So you're saying I make CanPlace check for only 1 of the parts at a time, then within getPlacementState, I do
if (!(canPlace(middle) && canPlace(left) && canPlace(right)) return null;

#

Is that correct?

plucky cloud
carmine nova
#

when a blockitem is used, it checks canPlace
then if it can place, it calls getPlacementState

so you only need to check left and right's canPlace in the getPlacementState

#

if all 3 can be placed, you set the blockstate of left and right and then return the state of the middle

if the return from getPlacementState is null, the blockitem's usage fails
if the return is a blockstate, it calls world.setBlockState with that state

plucky cloud
#

If in getPlacementState I call CanPlace for left and right, and I realise that the block couldn't be placed, returning null would make the middle block not placed either? Or does that leave the middle block as placed?

carmine nova
#

correct. returning null from getPlacementState causes the BlockItem's usage to return FAIL and not place

#

this is how doors work, by the way

plucky cloud
#

Interesting! I'll implement it soon and I'll let you know if it worked :p

plucky cloud
#
    public BlockState getPlacementState(ItemPlacementContext ctx)
    {
        BlockPos midPos = ctx.getBlockPos();
        BlockState midState = ctx.getWorld().getBlockState(midPos);
        Direction topDir = midState.get(FACING).rotateYClockwise();
        BlockPos topPos = midPos.offset(topDir);
        BlockPos bottomPos = midPos.offset(topDir.getOpposite());
        Boolean top = canPlaceAt(ctx.getWorld().getBlockState(topPos),ctx.getWorld(),topPos);
        Boolean bottom = canPlaceAt(ctx.getWorld().getBlockState(bottomPos),ctx.getWorld(),bottomPos);
        if (!(top && bottom)){
            return null;
        }
        BlockState state = getDefaultState();
        state = state.with(FACING, ctx.getHorizontalPlayerFacing().getOpposite());
        return state;
    }``` This is now what I've done. CanPlaceAt() looks like this:
```@Override
    protected boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) {
        BlockPos blockPos = pos.down();
        Boolean a = this.canPlaceAbove(world,blockPos,world.getBlockState(blockPos));
        Boolean b = world.getBlockState(pos).isAir();
        return (a && b);
    }```
carmine nova
#

without knowing the crash its hard to say.
Boolean top = canPlaceAt(ctx.getWorld().getBlockState(topPos),ctx.getWorld(),topPos);

this appears that you are getting the current blockstate that exists in the top position and checking if that blockstate can be placed. I would suggest getting the blockstate you want to place and calling canPlaceAt on that:

World world = ctx.getWorld();
...
BlockPos topPos = midPos.offset(topDir);
BlockState topBlockState = ModBlocks.YOUR_TOP_BLOCK.getDefaultState().with(SomeProperty, SomeValue);
boolean canPlaceTopBlock = topBlockState.canPlaceAt(world, topPos);
...
if (!canPlaceTopBlock || !canPlaceBottomBlock) return null;
return blockState;
...
public void onPlaced((...) {
...
  BlockPos topPos = pos.offset(topDir);
BlockState topBlockState = ModBlocks.YOUR_TOP_BLOCK.getDefaultState().with(SomeProperty, SomeValue);
  world.setBlockState(topPos, topBlockState, Block.NOTIFY_ALL);
}

something like this

plucky cloud
#

Oh, I'm taking the block state of the previously existing block that isn't yet my block? I'll fix that next time I get the chance :p

plucky cloud
#
    public BlockState getPlacementState(ItemPlacementContext ctx)
    {
        World world = ctx.getWorld();
        BlockPos midPos = ctx.getBlockPos();
        BlockState midState = world.getBlockState(midPos);

        Direction topDir = midState.get(FACING).rotateYClockwise();
        BlockPos topPos = midPos.offset(topDir);
        BlockState topState = this.getDefaultState().with(FACING, midState.get(FACING));
        Boolean top = topState.canPlaceAt(world,topPos);

        BlockPos bottomPos = midPos.offset(topDir.getOpposite());
        BlockState bottomState = this.getDefaultState().with(FACING, midState.get(FACING));
        Boolean bottom = bottomState.canPlaceAt(world,bottomPos);
        if (!top || !bottom){
            return null;
        }
        BlockState state = getDefaultState();
        state = state.with(FACING, ctx.getHorizontalPlayerFacing().getOpposite());
        return state;
    }```

I did this, but it's still not working? The game still crashes as soon as I try to place my block
#

and my CanPlaceAt() is now:

    protected boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) {
        BlockPos bottomPos = pos.down();
        Boolean a = this.canPlaceAbove(world,bottomPos,world.getBlockState(bottomPos));
        Boolean b = world.getBlockState(pos).isAir();
        return (a && b);
    }```

I feel like boolean b here is not right?
plucky cloud
#

Removed it, only returning a, and the game still crashes when I place it

plucky cloud
#

So something is wrong with my getPlacementState()

carmine nova
#
        BlockPos midPos = ctx.getBlockPos();
        BlockState midState = world.getBlockState(midPos);

what is this getting, exactly?

#

once you have that figured out
Direction topDir = midState.get(FACING).rotateYClockwise();
what do you think this will do when you call get?

plucky cloud
#

It was so I had a reference for a block state so I could get a default state for the top and bottom to refer to

#

... is that wrong?

plucky cloud
#

And I want the location of the top and bottom blocks, so I used mid's facing, rotated to get the direction to the top part, and then getOpposite() to get the position of the bottom

#

If I have the direction, I know what position to call canPlaceAt for

#

That was my train of thought

plucky cloud
#

That would make sense, air doesn't have a facing, so the game would panic

plucky cloud
#
    public BlockState getPlacementState(ItemPlacementContext ctx)
    {
        World world = ctx.getWorld();
        BlockPos midPos = ctx.getBlockPos();
        BlockState midState = this.getDefaultState().with(FACING, ctx.getHorizontalPlayerFacing().getOpposite());

        Direction topDir = midState.get(FACING).rotateYClockwise();
        BlockPos topPos = midPos.offset(topDir);
        BlockState topState = this.getDefaultState().with(FACING, midState.get(FACING));
        Boolean top = topState.canPlaceAt(world,topPos) && world.getBlockState(topPos).canReplace(ctx);

        BlockPos bottomPos = midPos.offset(topDir.getOpposite());
        BlockState bottomState = this.getDefaultState().with(FACING, midState.get(FACING));
        Boolean bottom = bottomState.canPlaceAt(world,bottomPos) && world.getBlockState(bottomPos).canReplace(ctx);
        if (!top || !bottom){
            return null;
        }
        else {
            BlockState state = getDefaultState();
            state = state.with(FACING, ctx.getHorizontalPlayerFacing().getOpposite());
            return state;
        }
    }```

IT WORKS!!!
#

Thank you for helping out!

#

Finally, last one, my block doesn't drop when broken in survival

plucky cloud
#

Oh, and I need to make getStateForNeighborUpdate function. I'm clueless as to how to write that function

#

And it appears that the block does drop as an item if the block underneath is broken (but only that part, not the entire block).

So the problems are now:

  • Block does not drop when broken by the player
  • Block does not fully break when any block underneath it is broken, but the item does drop (technically a duplication glitch now)
carmine nova
#

move the air check (or rather, the canReplace check) into your getStateForPlacement
make the canPlace check only check whether the block can be supported by the block below it

in the getStateForNeighborUpdate, check if the block can still be supported by the block it is on by calling canPlace
check if the side blocks are still the side blocks
if either of those are false return air

by returning air, it should trigger a getStateForNeighborUpdate on the side blocks which can do a similar check (make sure they are next to the middle blockstate, make sure they can be supported, or return air)

when getStateForNeighborUpdate returns Blocks.AIR.getDefaultState(), it breaks the block and normally drops as though it was broken with no itemstack and no player context

#

so, after reviewing the mc code, it appears it looks like this:

canPlaceAt (called when placing from a blockitem or enderman): does the block below support this block. can this block survive here

onBreak - if side blocks should not drop items and all 3 blocks are the same block, this can be used to break the side blocks without dropping

getPlacementState - is there enough space to place the other blocks. can the new blocks replace the existing blocks (air, grass, etc, from a canReplace call on the existing blockstate)

onPlaced - place the side blocks

getStateForNeighborUpdate - is the block still supported (canPlaceAt called), did someone break a required side block

carmine nova
#

some notes on code style:

if you are getting the facing value more than once, set it to a variable and reuse it.

you are using the same state for top, mid and bottom for checking placement. you shouldnt need to set them to variables if they are the same blockstate. if they are not supposed to be the same blockstate, you should fix that

if you dont need a nullable value, use boolean not Boolean. Boolean is a wrapper around the native boolean that allows null.

fail early checks do not need an else clause:

        if (!top || !bottom) return null;

        BlockState state = getDefaultState();
        state = state.with(FACING, ctx.getHorizontalPlayerFacing().getOpposite());
        return state;

to optimize a little more, you can do the !top directly instead of waiting until the bottom is also checked:

        if ( !topState.canPlaceAt(world,topPos) ||
              world.getBlockState(topPos).canReplace(ctx))
            ) {
              return null;
            }
plucky cloud
#

My getStateForNeighbor currently only works for the block that had its supporting block broken, it doesn't break the other blocks. How do I get that to happen?

#

And I think something is wrong in my onBreak function, because it's causing the item to not drop

#

Or at least, I think so, since breaking the support blocks does make them drop as items, so my json file should be correct

carmine nova
#

if the side blocks are not supposed to drop items but the mid block is, and if they are different blockstates then you can tell the side blocks to break and not drop using world.setBlockState and using the Block.SKIP_DROPS flag along with the Block.NOTIFY_ALL flag

#

if they are supposed to drop, you can just use world.breakBlock

#

if using the same block for each, there should be a block property for which of the blocks of the 3 that it is.

if one of the blocks breaks, it should trigger a getStateForNeighborUpdate call on the blocks next to it. be sure the getStateForNeighborUpdate method is checking the side blocks

plucky cloud
#

So onBreak should call getStateForNeighborUpdate() for its existing neighbors?

carmine nova
#

no. onBreak is called when the block is broken
the change in blockstate (from block to air) which happens when a block is broken will cause the game to call getStateForNeighborUpdate

plucky cloud
#

onBreak gets called when a part is broken. This function does what then? if the game calls getStateForNeighborUpdate() by itself, what do I need to do?

carmine nova
#

if you need to break blocks without dropping items

#

if you place 1 blockitem and it places 3 blocks, then onBreak should break the other 2 blocks without drops

plucky cloud
#

So if I break the top block, it'll break middle and bottom

#

and if the middle one is broken, it'll break the top and bottom

#

and if the bottom one is broken, it'll break the middle and top

#

right?

carmine nova
#

yep

#

(appears it might only need to be specific to creative mode if your loot table handles blockstates for sides)

plucky cloud
#

So getStateForNeighborUpdate() is for returning air if not canPlaceAt()? And first checking if the side block still exists?

carmine nova
#

yeah, getStateForNeighborUpdate is basically

a block next to this one changed. make sure the blocks around this one are valid:
call canPlaceAt to see if the block below this one is still valid
check side blocks to see if those still exist

plucky cloud
#
    public BlockState onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player){
        Direction direction = state.get(FACING);
        BlockPos topPos = null;
        BlockPos bottomPos = null;
        BlockPos midPos = null;
        if (state.get(PART) == BLOCK_PART.MIDDLE){
            topPos = pos.offset(state.get(FACING).rotateYClockwise());
            bottomPos = pos.offset(state.get(FACING).rotateYCounterclockwise());
            midPos = pos;
        }
        else if (state.get(PART) == BLOCK_PART.TOP){
            topPos = pos;
            bottomPos = pos.offset(direction.rotateYCounterclockwise(), 2);
            midPos = pos.offset(direction.rotateYClockwise());
        }
        else{
            topPos = pos.offset(direction.rotateYClockwise(), 2);
            bottomPos = pos;
            midPos = pos.offset(direction.rotateYClockwise());
        }
        world.breakBlock(midPos,true,player,1);
        world.breakBlock(topPos,false,player,1);
        world.breakBlock(bottomPos,false,player,1);
        return super.onBreak(world,pos,state,player);
    }```

So I've made my onBreak here, but now the middle item drops even in creative. How do I change the `world.breakBlock(midPos,*true*,player, 1)` to make sure it only drops when not in creative?
carmine nova
#

you can check if the player is in creative with the player object provided there

plucky cloud
# carmine nova yeah, getStateForNeighborUpdate is basically a block next to this one changed....

I didn't get this to work, it's not getting an update from the side. What's not working here?

    protected BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) {

        boolean validPlace = !canPlaceAt(state,world, pos);
        boolean sideExists;
        Direction dir = state.get(FACING);
        if (state.get(PART) == BLOCK_PART.MIDDLE){
            BlockState top = world.getBlockState(pos.offset(dir.rotateYClockwise()));
            BlockState bottom = world.getBlockState(pos.offset(dir.rotateYCounterclockwise()));
            sideExists = top.isOf(this) && bottom.isOf(this);
        }
        else if (state.get(PART) == BLOCK_PART.TOP){
            BlockState mid = world.getBlockState(pos.offset(dir.rotateYCounterclockwise()));
            sideExists = mid.isOf(this);
        }
        else {
            BlockState mid = world.getBlockState(pos.offset(dir.rotateYClockwise()));
            sideExists = mid.isOf(this);
        }
        if (sideExists && validPlace){
            return Blocks.AIR.getDefaultState();
        }
        return super.getStateForNeighborUpdate(state,direction,neighborState,world,pos,neighborPos);
    }```
#

Is there no documentation or tutorial on how to make these blocks? when I google, all I find is machines built up from multiple blocks, not one block that when placed takes up multiple spaces

river snow
plucky cloud
#

Due to not being multiple blocks? ๐Ÿ˜‚

plucky cloud
# river snow The bed block is probably your only consistent solution, most other blocks don't...
    protected BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) {
        Direction dir = state.get(FACING);
        if (direction == dir.rotateYCounterclockwise() && state.get(PART) == BLOCK_PART.MIDDLE){
            return neighborState.isOf(this) ? state : Blocks.AIR.getDefaultState();
        }
        else if (direction == dir.rotateYClockwise() && state.get(PART) == BLOCK_PART.MIDDLE){
            return neighborState.isOf(this) ? state : Blocks.AIR.getDefaultState();
        }
        else if (state.get(PART) == BLOCK_PART.TOP && direction == dir.rotateYCounterclockwise()){
            return neighborState.isOf(this) ? state : Blocks.AIR.getDefaultState();
        }
        else if (state.get(PART) == BLOCK_PART.BOTTOM && direction == dir.rotateYClockwise()){
            return neighborState.isOf(this) ? state : Blocks.AIR.getDefaultState();
        }
        return super.getStateForNeighborUpdate(state,direction,neighborState,world,pos,neighborPos);
    }```

Got this from looking at the bed! But now it drops an item for every block broken, while I want it to drop itself once. How do I fix that?
carmine nova
#

which part do you want to drop the part?

#

do the check in the loot table for that blockstate

plucky cloud
#

How do I check for the state in the loot table?

plucky cloud
carmine nova
#

check the door loot table

#

using datagen, seems super easy:

dropsWithProperty(block, PROPERTY, VALUE);

plucky cloud
#

Ah, I just edited the json file ๐Ÿ˜‚

#

and it works now!

#

Thank you both so much!

#

This is now fully working as I want it to