Jump to content

Recommended Posts

Posted

Here is my hot tub. As you can see, I can hop inside of it. It has walls which work properly so that I can't just walk out of it.

xiFvECV.png

But here's the major problem. When I make the hot tub larger, it doesn't get rid of the collision box for the wall that is no longer there. So because of that, I can't move around in the hot tub (move to the other hot tub part that I placed down). It's only meant to have collision boxes on the faces that are false.

 

Just realised my problem but don't know how to fix it. The addCollisionBoxes method only gets called on once - when it's first placed down. So when the blockstate properties change, the collision boxes don't get updated. How do I update the collision boxes?

 

Block Class:

 

public class BlockHotTub extends Block {

 

public static final PropertyBool BACK = PropertyBool.create("back");

public static final PropertyBool FORWARD = PropertyBool.create("forward");

public static final PropertyBool LEFT = PropertyBool.create("left");

public static final PropertyBool RIGHT = PropertyBool.create("right");

 

public BlockHotTub(Material materialIn) {

super(materialIn);

this.setCreativeTab(OddmentsMod.tabOddments);

this.setDefaultState(this.blockState.getBaseState().withProperty(BACK, false).withProperty(FORWARD, false).withProperty(LEFT, false).withProperty(RIGHT, false));

}

 

@Override

public boolean isOpaqueCube() {

return false;

}

 

@Override

public boolean isFullCube() {

return false;

}

 

@Override

public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos) {

boolean forward = world.getBlockState(pos.north()).getBlock() == this;

boolean back = world.getBlockState(pos.south()).getBlock() == this;

boolean left = world.getBlockState(pos.west()).getBlock() == this;

boolean right = world.getBlockState(pos.east()).getBlock() == this;

return state.withProperty(BACK, back).withProperty(FORWARD, forward).withProperty(LEFT, left).withProperty(RIGHT, right);

}

 

@Override

protected BlockState createBlockState() {

return new BlockState(this, new IProperty[] {BACK, FORWARD, LEFT, RIGHT});

}

 

@Override

public int getMetaFromState(IBlockState state) {

return  0;

}

 

@Override

public IBlockState getStateFromMeta(int meta) {

return this.getDefaultState();

}

 

// Not working but this method is meant to update the value of the boolean properties whenever a hot tub is connected to the current one

@Override

public void onNeighborBlockChange(World worldIn, BlockPos pos, IBlockState state, Block neighborBlock) {

getActualState(state, worldIn, pos);

addCollisionBoxesToList(worldIn, pos, state, null, null, null);

    }

 

public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List<AxisAlignedBB> list, Entity collidingEntity) {

if (state.getValue(BACK) == false) {

this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 0.125F);

super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);

}

if (state.getValue(FORWARD) == false) {

this.setBlockBounds(0.0F, 0.0F, 0.875F, 1.0F, 1.0F, 1.0F);

super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);

}

if (state.getValue(LEFT) == false) {

this.setBlockBounds(0.0F, 0.0F, 0.0F, 0.125F, 1.0F, 1.0F);

super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);

}

if  (state.getValue(RIGHT) == false) {

this.setBlockBounds(0.875F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);

super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);

}

this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.1875F, 1.0F); // the default bounding box at the end of the method?

super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);

    }

}

 

Posted

The state being passed in is the state you should compute the collision boxes for. So you need to analyze the state and then return different collision boxes based on that.

 

Great, so I just need to check the value of the state passed through using an if statement in this method and add the collision box accordingly?

Posted

Sorry, I thought I understood what to do but apparently not. I have one blockstate for the hot tub and in there I have this:

{
    "variants": {
        "normal": {"model": "odm:hot_tub_empty"},
        "back=false,forward=false,left=false,right=false": {"model": "odm:hot_tub_empty"},
        "back=true,forward=true,left=true,right=true": {"model": "odm:hot_tub_center_empty"},
        "back=true,forward=false,left=false,right=false": {"model": "odm:hot_tub_side_empty"},
        "back=false,forward=true,left=false,right=false": {"model": "odm:hot_tub_side_empty", "y": 180},
        "back=false,forward=false,left=true,right=false": {"model": "odm:hot_tub_side_empty", "y": 270},
        "back=false,forward=false,left=false,right=true": {"model": "odm:hot_tub_side_empty", "y": 90},
        "back=true,forward=true,left=false,right=false": {"model": "odm:hot_tub_sides_two_empty", "y": 90},
        "back=false,forward=false,left=true,right=true": {"model": "odm:hot_tub_sides_two_empty"},
        "back=true,forward=false,left=true,right=false": {"model": "odm:hot_tub_corner_empty", "y": 270},
        "back=true,forward=false,left=false,right=true": {"model": "odm:hot_tub_corner_empty"},
        "back=false,forward=true,left=true,right=false": {"model": "odm:hot_tub_corner_empty", "y": 180},
        "back=true,forward=false,left=true,right=false": {"model": "odm:hot_tub_corner_empty", "y": 270},
        "back=true,forward=true,left=false,right=true": {"model": "odm:hot_tub_sides_three_empty"},
        "back=false,forward=true,left=true,right=true": {"model": "odm:hot_tub_sides_three_empty", "y": 90},
        "back=true,forward=true,left=true,right=false": {"model": "odm:hot_tub_sides_three_empty", "y": 190},
        "back=true,forward=false,left=true,right=true": {"model": "odm:hot_tub_sides_three_empty", "y": 270},
    }
}

 

And in the hot tub's class I have set the blockstate properties:

public static final PropertyBool BACK = PropertyBool.create("back");
public static final PropertyBool FORWARD = PropertyBool.create("forward");
public static final PropertyBool LEFT = PropertyBool.create("left");
public static final PropertyBool RIGHT = PropertyBool.create("right");

 

So under the addCollisionBoxesToList method, what exactly should I be checking for the value of in the if statements?

Posted

You get the values for those properties out of the block state using

IBlockState::getValue

.

 

Sorry again but I'm still struggling to get this to work.

 

I'm checking the value of one of the properties to see if it is true, then if it is I alter the collision box. But when I test this in-game, the block just doesn't have a collision box at all (no matter whether the back is true or false).

 

public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List<AxisAlignedBB> list, Entity collidingEntity) {
	if (state.getValue(BACK) == true) {
		this.setBlockBounds(0.5F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
        super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
    }

Posted

Don't call setBlockBounds like that, you have to actually add the collision box.

 

Do you mean by calling setBlockBounds in the constructor? Because when I do that, I still get this problem where my collision boxes aren't all adding together but instead it's just taking the last block bounds that I set in the addCollisionBoxes method. 

 

I want the hot tub to have a regular 1x1x1 bound box which you look at to select the block and break/interact with it. But I want the actual collision of the hot tub to vary depending on what type of hot tub it is (if it's facing left it's missing its left wall or if it's in the corner it's missing both its right and left wall etc.)

I have looked at the BrewingStand code as Choonster pointed out to be a good reference for collision boxes, but I can't find anything in there which uses some other function to add the collision box.

Posted

Which version of Minecraft are you using? My advice in your previous thread assumed you were using 1.9+, but it looks like you're using 1.8.9 (where the collision box logic is slightly different).

 

In 1.8.x, you override

Block#addCollisionBoxesToList

to call

Block#setBlockBounds

followed by the super method for each bounding box, then call

Block#setBlockBounds

to restore the block's default bounding box at the end of the method.

 

To make it easier to update to newer versions, I suggest you store each bounding box in a

static final

field and create a method like

Block#setBlockBounds

that sets

Block#minX/Y/Z

and

Block#maxX/Y/Z

from the coordinates of an

AxisAlignedBoundingBox

. When you update to 1.9+, you can simply replace each pair of calls to this method and the super method with calls to

Block.addCollisionBoxToList

.

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.

Posted

Which version of Minecraft are you using? My advice in your previous thread assumed you were using 1.9+, but it looks like you're using 1.8.9 (where the collision box logic is slightly different).

 

In 1.8.x, you override

Block#addCollisionBoxesToList

to call

Block#setBlockBounds

followed by the super method for each bounding box, then call

Block#setBlockBounds

to restore the block's default bounding box at the end of the method.

 

You are correct, I am using 1.8.9. So here's my current code (I added that last thing you said to it):

 

public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List<AxisAlignedBB> list, Entity collidingEntity) {
	if (state.getValue(BACK) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 0.125F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if (state.getValue(FORWARD) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.875F, 1.0F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if (state.getValue(LEFT) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.0F, 0.125F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if  (state.getValue(RIGHT) == false) {
		this.setBlockBounds(0.875F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F); // the default bounding box at the end of the method?
                super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
    }

 

The default bounding box is the base of the hot tub which is obviously always going to be there regardless of how big the hot tub is.

Posted

Are those properties stored in the metadata or set in the actual state? If it's the latter, you have to call

Block#getActualState

yourself.

 

In 1.11.2, an extra

boolean

parameter was added to

Block#addCollisionBoxToList

indicating whether the actual state should be used (

false

) or not (

true

).

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.

Posted

Are those properties stored in the metadata or set in the actual state? If it's the latter, you have to call

Block#getActualState

yourself.

 

In 1.11.2, an extra

boolean

parameter was added to

Block#addCollisionBoxToList

indicating whether the actual state should be used (

false

) or not (

true

).

 

I believe they are stored in the actual state. But to avoid any confusion, here is the hot tub's class code just in case I'm wrong:

public class BlockHotTub extends Block {

public static final PropertyBool BACK = PropertyBool.create("back");
public static final PropertyBool FORWARD = PropertyBool.create("forward");
public static final PropertyBool LEFT = PropertyBool.create("left");
public static final PropertyBool RIGHT = PropertyBool.create("right");

public BlockHotTub(Material materialIn) {
	super(materialIn);
	this.setCreativeTab(OddmentsMod.tabOddments);
	this.setDefaultState(this.blockState.getBaseState().withProperty(BACK, false).withProperty(FORWARD, false).withProperty(LEFT, false).withProperty(RIGHT, false));
}

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

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

@Override
public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos) {
	boolean forward = world.getBlockState(pos.north()).getBlock() == this;
	boolean back = world.getBlockState(pos.south()).getBlock() == this;
	boolean left = world.getBlockState(pos.west()).getBlock() == this;
	boolean right = world.getBlockState(pos.east()).getBlock() == this;
	return state.withProperty(BACK, back).withProperty(FORWARD, forward).withProperty(LEFT, left).withProperty(RIGHT, right);
}

@Override
protected BlockState createBlockState() {
	return new BlockState(this, new IProperty[] {BACK, FORWARD, LEFT, RIGHT});
}

@Override
public int getMetaFromState(IBlockState state) {
	return  0;
}

@Override
public IBlockState getStateFromMeta(int meta) {
	return this.getDefaultState();
}

// Not working but this method is meant to update the value of the boolean properties whenever a hot tub is connected to the current one
@Override
public void onNeighborBlockChange(World worldIn, BlockPos pos, IBlockState state, Block neighborBlock) {
	getActualState(state, worldIn, pos);
    }

public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List<AxisAlignedBB> list, Entity collidingEntity) {
	if (state.getValue(BACK) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 0.125F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if (state.getValue(FORWARD) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.875F, 1.0F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if (state.getValue(LEFT) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.0F, 0.125F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if  (state.getValue(RIGHT) == false) {
		this.setBlockBounds(0.875F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.1875F, 1.0F); // the default bounding box at the end of the method?
	super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
    }
}

 

Just realised my problem but don't know how to fix it. The addCollisionBoxes method only gets called on once - when it's first placed down. So when the blockstate properties change, the collision boxes don't get updated. How do I update the collision boxes?

Posted

Since the properties are stored in the actual state, you need to call

Block#getActualState

and get the values from the

IBlockState

returned by it.

 

Block#addCollisionBoxesToList

is called whenever an entity moves into the block space and in various other places. You don't need to manually update it.

 

Side note: Always annotate override methods with

@Override

.

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.

Posted

Since the properties are stored in the actual state, you need to call

Block#getActualState

and get the values from the

IBlockState

returned by it.

 

Block#addCollisionBoxesToList

is called whenever an entity moves into the block space and in various other places. You don't need to manually update it.

 

Side note: Always annotate override methods with

@Override

.

 

Okay, I'm slightly confused as to how to call it. Should I just be calling getActualState within the addCollisionBoxes method like so?

@Override
public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List<AxisAlignedBB> list, Entity collidingEntity) {
	getActualState(state, worldIn, pos);
	if (state.getValue(BACK) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 0.125F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if (state.getValue(FORWARD) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.875F, 1.0F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if (state.getValue(LEFT) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.0F, 0.125F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if  (state.getValue(RIGHT) == false) {
		this.setBlockBounds(0.875F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.1875F, 1.0F); // the default bounding box at the end of the method?
	super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
    }

Posted

getActualState

doesn't modify its arguments (and it can't, since

IBlockState

is immutable), it returns the actual state.

 

You need to get the values from the

IBlockState

returned by

getActualState

instead of getting them from the

IBlockState

argument.

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.

Posted

You need to get the values from the

IBlockState

returned by

getActualState

 

I can tell you're spelling the answer out for me but I'm just not getting it :-\

 

The addCollisionBoxesToList method needs to constantly know what BACK, FORWARD, LEFT and RIGHT are equal to. And to do this, you're saying that I need to call the getActualState method whenever addCollisionBoxesToList is called on.

 

This makes complete sense to me so far, but the matter of calling the method properly is what I don't understand.

 

This is my understanding of it:

The getActualState method sets the boolean fields: forward, back, left and right and then gives those values to my PropertyBools using the return keyword followed by state.withProperty(BACK, back), state.withProperty(FORWARD, forward), state.withProperty(LEFT, left) and state.withProperty(RIGHT, right). When the addCollisionBoxesToList method checks what, for example, BACK is equal to, shouldn't I call everything in the getActualState method?

Posted

Your

getActualState

method checks each of the adjacent blocks to see if they're hot tubs and then returns an

IBlockState

with the

BACK

,

FORWARD

,

LEFT

and

RIGHT

properties set to the appropriate values.

 

Your

addCollisionBoxesToList

method needs to call

getActualState

and store the

IBlockState

returned by it in a variable. It then needs to call

IBlockState#getValue

on this

IBlockState

value instead of the

IBlockState

argument.

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.

Posted

Your

getActualState

method checks each of the adjacent blocks to see if they're hot tubs and then returns an

IBlockState

with the

BACK

,

FORWARD

,

LEFT

and

RIGHT

properties set to the appropriate values.

 

I completely understand this and it serves as a much better explanation of the code than my attempt. 

 

Your

addCollisionBoxesToList

method needs to call

getActualState

Got it.

public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List<AxisAlignedBB> list, Entity collidingEntity) {
	getActualState(state, worldIn, pos);

 

and store the

IBlockState

returned by it in a variable.

This is where you lose me. What exactly is it that I'm storing in this variable? And are you referring to each of the four possible properties? Are these what I need to be storing in variables?

 

It then needs to call

IBlockState#getValue

on this

IBlockState

value instead of the

IBlockState

argument.

So here you mean instead of having state.getValue(BACK) I should be putting state.getValue(variable that stores the state.withProperty(BACK, back)?

 

It's 3AM and I should probably spare you anymore trouble. I feel like a pest at this point. Maybe going over the forge documentation a few more times and I'll eventually get it :'(

 

 

Posted

and store the

IBlockState

returned by it in a variable.

This is where you lose me. What exactly is it that I'm storing in this variable? And are you referring to each of the four possible properties? Are these what I need to be storing in variables?

 

getActualState

returns a value of type

IBlockState

, you need to store this value in a variable.

 

 

 

It then needs to call

IBlockState#getValue

on this

IBlockState

value instead of the

IBlockState

argument.

So here you mean instead of having state.getValue(BACK) I should be putting state.getValue(variable that stores the state.withProperty(BACK, back)?

 

Instead of calling

state.getValue(...)

, call

actualState.getValue(...)

; where

actualState

is the variable storing the value returned by

getActualState

.

  • Like 1

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.

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



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • So me and a couple of friends are playing with a shitpost mod pack and one of the mods in the pack is corail tombstone and for some reason there is a problem with it, where on death to fire the player will get kicked out of the server and the tombstone will not spawn basically deleting an entire inventory, it doesn't matter what type of fire it is, whether it's from vanilla fire/lava, or from modded fire like ice&fire/lycanites and it's common enough to where everyone on the server has experienced at least once or twice and it doesn't give any crash log. a solution to this would be much appreciated thank you!
    • It is 1.12.2 - I have no idea if there is a 1.12 pack
    • Okay, but does the modpack works with 1.12 or just with 1.12.2, because I need the Forge client specifically for Minecraft 1.12, not 1.12.2
    • Version 1.19 - Forge 41.0.63 I want to create a wolf entity that I can ride, so far it seems to be working, but the problem is that when I get on the wolf, I can’t control it. I then discovered that the issue is that the server doesn’t detect that I’m riding the wolf, so I’m struggling with synchronization. However, it seems to not be working properly. As I understand it, the server receives the packet but doesn’t register it correctly. I’m a bit new to Java, and I’ll try to provide all the relevant code and prints *The comments and prints are translated by chatgpt since they were originally in Spanish* Thank you very much in advance No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. MountableWolfEntity package com.vals.valscraft.entity; import com.vals.valscraft.network.MountSyncPacket; import com.vals.valscraft.network.NetworkHandler; import net.minecraft.client.Minecraft; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.Entity; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.network.PacketDistributor; public class MountableWolfEntity extends Wolf { private boolean hasSaddle; private static final EntityDataAccessor<Byte> DATA_ID_FLAGS = SynchedEntityData.defineId(MountableWolfEntity.class, EntityDataSerializers.BYTE); public MountableWolfEntity(EntityType<? extends Wolf> type, Level level) { super(type, level); this.hasSaddle = false; } @Override protected void defineSynchedData() { super.defineSynchedData(); this.entityData.define(DATA_ID_FLAGS, (byte)0); } public static AttributeSupplier.Builder createAttributes() { return Wolf.createAttributes() .add(Attributes.MAX_HEALTH, 20.0) .add(Attributes.MOVEMENT_SPEED, 0.3); } @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack itemstack = player.getItemInHand(hand); if (itemstack.getItem() == Items.SADDLE && !this.hasSaddle()) { if (!player.isCreative()) { itemstack.shrink(1); } this.setSaddle(true); return InteractionResult.SUCCESS; } else if (!level.isClientSide && this.hasSaddle()) { player.startRiding(this); MountSyncPacket packet = new MountSyncPacket(true); // 'true' means the player is mounted NetworkHandler.CHANNEL.sendToServer(packet); // Ensure the server handles the packet return InteractionResult.SUCCESS; } return InteractionResult.PASS; } @Override public void travel(Vec3 travelVector) { if (this.isVehicle() && this.getControllingPassenger() instanceof Player) { System.out.println("The wolf has a passenger."); System.out.println("The passenger is a player."); Player player = (Player) this.getControllingPassenger(); // Ensure the player is the controller this.setYRot(player.getYRot()); this.yRotO = this.getYRot(); this.setXRot(player.getXRot() * 0.5F); this.setRot(this.getYRot(), this.getXRot()); this.yBodyRot = this.getYRot(); this.yHeadRot = this.yBodyRot; float forward = player.zza; float strafe = player.xxa; if (forward <= 0.0F) { forward *= 0.25F; } this.flyingSpeed = this.getSpeed() * 0.1F; this.setSpeed((float) this.getAttributeValue(Attributes.MOVEMENT_SPEED) * 1.5F); this.setDeltaMovement(new Vec3(strafe, travelVector.y, forward).scale(this.getSpeed())); this.calculateEntityAnimation(this, false); } else { // The wolf does not have a passenger or the passenger is not a player System.out.println("No player is mounted, or the passenger is not a player."); super.travel(travelVector); } } public boolean hasSaddle() { return this.hasSaddle; } public void setSaddle(boolean hasSaddle) { this.hasSaddle = hasSaddle; } @Override protected void dropEquipment() { super.dropEquipment(); if (this.hasSaddle()) { this.spawnAtLocation(Items.SADDLE); this.setSaddle(false); } } @SubscribeEvent public static void onServerTick(TickEvent.ServerTickEvent event) { if (event.phase == TickEvent.Phase.START) { MinecraftServer server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(); if (server != null) { for (ServerPlayer player : server.getPlayerList().getPlayers()) { if (player.isPassenger() && player.getVehicle() instanceof MountableWolfEntity) { MountableWolfEntity wolf = (MountableWolfEntity) player.getVehicle(); System.out.println("Tick: " + player.getName().getString() + " is correctly mounted on " + wolf); } } } } } private boolean lastMountedState = false; @Override public void tick() { super.tick(); if (!this.level.isClientSide) { // Only on the server boolean isMounted = this.isVehicle() && this.getControllingPassenger() instanceof Player; // Only print if the state changed if (isMounted != lastMountedState) { if (isMounted) { Player player = (Player) this.getControllingPassenger(); // Verify the passenger is a player System.out.println("Server: Player " + player.getName().getString() + " is now mounted."); } else { System.out.println("Server: The wolf no longer has a passenger."); } lastMountedState = isMounted; } } } @Override public void addPassenger(Entity passenger) { super.addPassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(true)); } } } @Override public void removePassenger(Entity passenger) { super.removePassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is no longer mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(false)); } } } @Override public boolean isControlledByLocalInstance() { Entity entity = this.getControllingPassenger(); return entity instanceof Player; } @Override public void positionRider(Entity passenger) { if (this.hasPassenger(passenger)) { double xOffset = Math.cos(Math.toRadians(this.getYRot() + 90)) * 0.4; double zOffset = Math.sin(Math.toRadians(this.getYRot() + 90)) * 0.4; passenger.setPos(this.getX() + xOffset, this.getY() + this.getPassengersRidingOffset() + passenger.getMyRidingOffset(), this.getZ() + zOffset); } } } MountSyncPacket package com.vals.valscraft.network; import com.vals.valscraft.entity.MountableWolfEntity; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class MountSyncPacket { private final boolean isMounted; public MountSyncPacket(boolean isMounted) { this.isMounted = isMounted; } public void encode(FriendlyByteBuf buffer) { buffer.writeBoolean(isMounted); } public static MountSyncPacket decode(FriendlyByteBuf buffer) { return new MountSyncPacket(buffer.readBoolean()); } public void handle(NetworkEvent.Context context) { context.enqueueWork(() -> { ServerPlayer player = context.getSender(); // Get the player from the context if (player != null) { // Verifies if the player has dismounted if (!isMounted) { Entity vehicle = player.getVehicle(); if (vehicle instanceof MountableWolfEntity wolf) { // Logic to remove the player as a passenger wolf.removePassenger(player); System.out.println("Server: Player " + player.getName().getString() + " is no longer mounted."); } } } }); context.setPacketHandled(true); // Marks the packet as handled } } networkHandler package com.vals.valscraft.network; import com.vals.valscraft.valscraft; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class NetworkHandler { private static final String PROTOCOL_VERSION = "1"; public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel( new ResourceLocation(valscraft.MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals ); public static void init() { int packetId = 0; // Register the mount synchronization packet CHANNEL.registerMessage( packetId++, MountSyncPacket.class, MountSyncPacket::encode, MountSyncPacket::decode, (msg, context) -> msg.handle(context.get()) // Get the context with context.get() ); } }  
  • Topics

×
×
  • Create New...

Important Information

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