Jump to content

[SOLVED] [1.8] Strange behavior when custom arrow/projectile is in ground.


Recommended Posts

Posted

I have a spear that acts like an arrow, but when i shoot it in a block it has a strange behavior. It "moves" up after it hit the block. Here is a video to show you what i mean

https://www.youtube.com/watch?v=RUIcAT-zqTc&feature=youtu.be

 

This is my entity class

package blaze.entities;

import blaze.core.BLItems;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.projectile.EntityArrow;
import net.minecraft.item.ItemStack;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;

public class EntityOniceSpear extends EntityArrow implements IEntityAdditionalSpawnData 
{
    private boolean inGround;

    public EntityOniceSpear(World worldIn)
    {
        super(worldIn);
    }

    public EntityOniceSpear(World worldIn, EntityLivingBase shooter, EntityLivingBase par3, float par4, float par5)
    {
        super(worldIn, shooter, par3, par4, par5);
    }
    
    public EntityOniceSpear(World worldIn, EntityLivingBase shooter, float par3)
    {
        super(worldIn, shooter, par3);
    }

/**
     * Called by a player entity when they collide with an entity
     */
    @Override
    public void onCollideWithPlayer(EntityPlayer entityIn)
    {
        if (!this.worldObj.isRemote && this.inGround && this.arrowShake <= 0)
        {
            boolean flag = this.canBePickedUp == 1 || this.canBePickedUp == 2 && entityIn.capabilities.isCreativeMode;

            if (this.canBePickedUp == 1 && !entityIn.inventory.addItemStackToInventory(new ItemStack(BLItems.onice_spear, 1)))
            {
                flag = false;
            }

            if (flag)
            {
                this.playSound("random.pop", 0.2F, ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F);
                entityIn.onItemPickup(this, 1);
                this.setDead();
            }
        }
    }

    @Override
public void writeSpawnData(ByteBuf buffer) {
	buffer.writeInt(shootingEntity != null ? shootingEntity.getEntityId() : -1);
}

@Override
public void readSpawnData(ByteBuf buffer) {
	Entity shooter = worldObj.getEntityByID(buffer.readInt());
	if (shooter instanceof EntityLivingBase) {
		shootingEntity = (EntityLivingBase) shooter;
	}
}
}

 

And this is how i register the entity

EntityRegistry.registerModEntity(EntityMalachiteSpear.class, "malachiteSpear", 2 , BL.instance, 64, 20, true);

The value 64 and 20 has been taken from the registration of the EntityArrow class in EntityTracker (here it also set false the last value, but if i do the arrow doesn't do the moving animation)

 

So why has this behavior? :/ Thanks in advance to all who will help me :)

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

Usually that is caused by the position being out of sync between server and client, and the 'movement' is caused by the client getting the real position from the server.

 

I remember having a similar issue long ago and thought it was solved by using the same tracking values as a real EntityArrow, but you already do, so I'm not too sure.

 

There is one thing that your entity will not have that arrows do, however, and that is the initial spawn packet of arrows sends information about the shooter to the client. This happens in NetHandlerPlayClient#handleSpawnObject, which you can replicate by implementing IEntityAdditionalSpawnData for your spear:

@Override
public void writeSpawnData(ByteBuf buffer) {
	buffer.writeInt(shootingEntity != null ? shootingEntity.getEntityId() : -1);
}

@Override
public void readSpawnData(ByteBuf buffer) {
	Entity shooter = worldObj.getEntityByID(buffer.readInt());
	if (shooter instanceof EntityLivingBase) {
		shootingEntity = (EntityLivingBase) shooter;
	}
}

I doubt that will fix your issue directly, but it will at least make your spear more closely mimic vanilla arrows.

Posted

Thanks for your reply :) I've added these methods but now the entity is not rendering ad eclipse gives me this error :/

[22:27:36] [Client thread/ERROR] [FML]: A severe problem occurred during the spawning of an entity at ( 558.0625,5.5, -549.84375)
java.lang.NoSuchMethodException: blaze.entities.EntityOniceSpear.<init>(net.minecraft.world.World)
at java.lang.Class.getConstructor0(Unknown Source) ~[?:1.8.0_51]
at java.lang.Class.getConstructor(Unknown Source) ~[?:1.8.0_51]
at net.minecraftforge.fml.common.network.internal.EntitySpawnHandler.spawnEntity(EntitySpawnHandler.java:98) [EntitySpawnHandler.class:?]
at net.minecraftforge.fml.common.network.internal.EntitySpawnHandler.process(EntitySpawnHandler.java:56) [EntitySpawnHandler.class:?]
at net.minecraftforge.fml.common.network.internal.EntitySpawnHandler.access$000(EntitySpawnHandler.java:31) [EntitySpawnHandler.class:?]
at net.minecraftforge.fml.common.network.internal.EntitySpawnHandler$1.run(EntitySpawnHandler.java:46) [EntitySpawnHandler$1.class:?]
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) [?:1.8.0_51]
at java.util.concurrent.FutureTask.run(Unknown Source) [?:1.8.0_51]
at net.minecraftforge.fml.common.FMLCommonHandler.callFuture(FMLCommonHandler.java:709) [FMLCommonHandler.class:?]
at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:1070) [Minecraft.class:?]
at net.minecraft.client.Minecraft.run(Minecraft.java:376) [Minecraft.class:?]
at net.minecraft.client.main.Main.main(Main.java:117) [Main.class:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_51]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_51]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_51]
at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_51]
at net.minecraft.launchwrapper.Launch.launch(Launch.java:135) [launchwrapper-1.11.jar:?]
at net.minecraft.launchwrapper.Launch.main(Launch.java:28) [launchwrapper-1.11.jar:?]
at net.minecraftforge.gradle.GradleStartCommon.launch(Unknown Source) [start/:?]
at GradleStart.main(Unknown Source) [start/:?]
[22:27:36] [Client thread/FATAL] [FML]: Exception caught executing FutureTask: java.util.concurrent.ExecutionException: java.lang.RuntimeException: java.lang.NoSuchMethodException: blaze.entities.EntityOniceSpear.<init>(net.minecraft.world.World)
java.util.concurrent.ExecutionException: java.lang.RuntimeException: java.lang.NoSuchMethodException: blaze.entities.EntityOniceSpear.<init>(net.minecraft.world.World)
at java.util.concurrent.FutureTask.report(Unknown Source) ~[?:1.8.0_51]
at java.util.concurrent.FutureTask.get(Unknown Source) ~[?:1.8.0_51]
at net.minecraftforge.fml.common.FMLCommonHandler.callFuture(FMLCommonHandler.java:710) [FMLCommonHandler.class:?]
at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:1070) [Minecraft.class:?]
at net.minecraft.client.Minecraft.run(Minecraft.java:376) [Minecraft.class:?]
at net.minecraft.client.main.Main.main(Main.java:117) [Main.class:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_51]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_51]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_51]
at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_51]
at net.minecraft.launchwrapper.Launch.launch(Launch.java:135) [launchwrapper-1.11.jar:?]
at net.minecraft.launchwrapper.Launch.main(Launch.java:28) [launchwrapper-1.11.jar:?]
at net.minecraftforge.gradle.GradleStartCommon.launch(Unknown Source) [start/:?]
at GradleStart.main(Unknown Source) [start/:?]
Caused by: java.lang.RuntimeException: java.lang.NoSuchMethodException: blaze.entities.EntityOniceSpear.<init>(net.minecraft.world.World)
at com.google.common.base.Throwables.propagate(Throwables.java:160) ~[guava-17.0.jar:?]
at net.minecraftforge.fml.common.network.internal.EntitySpawnHandler.spawnEntity(EntitySpawnHandler.java:147) ~[EntitySpawnHandler.class:?]
at net.minecraftforge.fml.common.network.internal.EntitySpawnHandler.process(EntitySpawnHandler.java:56) ~[EntitySpawnHandler.class:?]
at net.minecraftforge.fml.common.network.internal.EntitySpawnHandler.access$000(EntitySpawnHandler.java:31) ~[EntitySpawnHandler.class:?]
at net.minecraftforge.fml.common.network.internal.EntitySpawnHandler$1.run(EntitySpawnHandler.java:46) ~[EntitySpawnHandler$1.class:?]
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) ~[?:1.8.0_51]
at java.util.concurrent.FutureTask.run(Unknown Source) ~[?:1.8.0_51]
at net.minecraftforge.fml.common.FMLCommonHandler.callFuture(FMLCommonHandler.java:709) ~[FMLCommonHandler.class:?]
... 11 more
Caused by: java.lang.NoSuchMethodException: blaze.entities.EntityOniceSpear.<init>(net.minecraft.world.World)
at java.lang.Class.getConstructor0(Unknown Source) ~[?:1.8.0_51]
at java.lang.Class.getConstructor(Unknown Source) ~[?:1.8.0_51]
at net.minecraftforge.fml.common.network.internal.EntitySpawnHandler.spawnEntity(EntitySpawnHandler.java:98) ~[EntitySpawnHandler.class:?]
at net.minecraftforge.fml.common.network.internal.EntitySpawnHandler.process(EntitySpawnHandler.java:56) ~[EntitySpawnHandler.class:?]
at net.minecraftforge.fml.common.network.internal.EntitySpawnHandler.access$000(EntitySpawnHandler.java:31) ~[EntitySpawnHandler.class:?]
at net.minecraftforge.fml.common.network.internal.EntitySpawnHandler$1.run(EntitySpawnHandler.java:46) ~[EntitySpawnHandler$1.class:?]
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) ~[?:1.8.0_51]
at java.util.concurrent.FutureTask.run(Unknown Source) ~[?:1.8.0_51]
at net.minecraftforge.fml.common.FMLCommonHandler.callFuture(FMLCommonHandler.java:709) ~[FMLCommonHandler.class:?]
... 11 more

 

QUICK UPDATE: i've solved that problem by adding the other constructors, but now i can't pickup the spear when on ground

public EntityOniceSpear(World worldIn)
    {
        super(worldIn);
    }

    public EntityOniceSpear(World worldIn, double x, double y, double z)
    {
        super(worldIn);
    }

    public EntityOniceSpear(World worldIn, EntityLivingBase shooter, EntityLivingBase p_i1755_3_, float p_i1755_4_, float p_i1755_5_)
    {
        super(worldIn);
    }

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

yes i was able to pickup before, and i didn't change anything about that part (the function onCollideWithPlayer is the same)

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

Oh, I see why (at least if you are using this constructor):

public EntityOniceSpear(World worldIn, EntityLivingBase shooter, EntityLivingBase p_i1755_3_, float p_i1755_4_, float p_i1755_5_)
    {
        super(worldIn);
    }

You are calling the wrong super method - all of the extra processing that gets done when there is a shooter is not being done.

 

Again, can you show the code that actually spawns the entity? E.g. in your Item class #onItemRightClick or wherever?

Posted

This is the item class from where i shoot the spear

package blaze.items;

import blaze.core.BLTabs;
import blaze.entities.EntityMalachiteSpear;
import blaze.entities.EntityOniceSpear;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBow;
import net.minecraft.item.ItemStack;
import net.minecraft.stats.StatList;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

public class ItemSpear extends ItemBow
{

private int type;

public ItemSpear(int type)
{
	this.setMaxDamage(0);
	this.setMaxStackSize(16);
	this.type = type;
	this.setCreativeTab(BLTabs.tabCombat);
}

/**
     * Called whenever this item is equipped and the right mouse button is pressed. Args: itemStack, world, entityPlayer
     */
@Override
    public ItemStack onItemRightClick(ItemStack itemStackIn, World worldIn, EntityPlayer playerIn)
    {
        net.minecraftforge.event.entity.player.ArrowNockEvent event = new net.minecraftforge.event.entity.player.ArrowNockEvent(playerIn, itemStackIn);
        if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(event)) return event.result;
        playerIn.setItemInUse(itemStackIn, this.getMaxItemUseDuration(itemStackIn));

        return itemStackIn;
    }

/**
 * How long it takes to use or consume an item
 */
@Override
public int getMaxItemUseDuration(ItemStack stack)
{
	return 72000;
}

/**
 * Called when the player stops using an Item (stops holding the right mouse button).
 *  
 * @param timeLeft The amount of ticks left before the using would have been complete
 */
@Override
public void onPlayerStoppedUsing(ItemStack stack, World worldIn, EntityPlayer playerIn, int timeLeft)
{
	int j = this.getMaxItemUseDuration(stack) - timeLeft;
	net.minecraftforge.event.entity.player.ArrowLooseEvent event = new net.minecraftforge.event.entity.player.ArrowLooseEvent(playerIn, stack, j);
	if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(event)) return;
	j = event.charge;

	boolean flag = playerIn.capabilities.isCreativeMode;
		float f = (float)j / 20.0F;
		f = (f * f + f * 2.0F) / 3.0F;

		if ((double)f < 0.1D)
		{
			return;
		}

		if (f > 1.0F)
		{
			f = 1.0F;
		}

		if(this.type == 0)
		{
			EntityOniceSpear entityspear = new EntityOniceSpear(worldIn, playerIn, f * 2.0F);
			if (flag)
			{
				entityspear.canBePickedUp = 2;
			}
			else
			{
				playerIn.inventory.consumeInventoryItem(this);
			}

			playerIn.triggerAchievement(StatList.objectUseStats[item.getIdFromItem(this)]);

			if (!worldIn.isRemote)
			{
				worldIn.spawnEntityInWorld(entityspear);
			}
		}
		else
		{
			EntityMalachiteSpear entityspear = new EntityMalachiteSpear(worldIn, playerIn, f * 2.0F);
			if (flag)
			{
				entityspear.canBePickedUp = 2;
			}
			else
			{
				playerIn.inventory.consumeInventoryItem(this);
			}

			playerIn.triggerAchievement(StatList.objectUseStats[item.getIdFromItem(this)]);

			if (!worldIn.isRemote)
			{
				worldIn.spawnEntityInWorld(entityspear);
			}
		}

}

@Override
@SideOnly(Side.CLIENT)
public boolean isFull3D()
{
	return true;
}
}

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

Of course, here is the entity class :)

package blaze.entities;

import blaze.core.BLItems;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.projectile.EntityArrow;
import net.minecraft.item.ItemStack;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;

public class EntityOniceSpear extends EntityArrow implements IEntityAdditionalSpawnData 
{
    private boolean inGround;

    public EntityOniceSpear(World worldIn)
    {
        super(worldIn);
    }

    public EntityOniceSpear(World worldIn, EntityLivingBase shooter, EntityLivingBase par3, float par4, float par5)
    {
        super(worldIn, shooter, par3, par4, par5);
    }
    
    public EntityOniceSpear(World worldIn, EntityLivingBase shooter, float par3)
    {
        super(worldIn, shooter, par3);
    }

/**
     * Called by a player entity when they collide with an entity
     */
    @Override
    public void onCollideWithPlayer(EntityPlayer entityIn)
    {
        if (!this.worldObj.isRemote && this.inGround && this.arrowShake <= 0)
        {
            boolean flag = this.canBePickedUp == 1 || this.canBePickedUp == 2 && entityIn.capabilities.isCreativeMode;

            if (this.canBePickedUp == 1 && !entityIn.inventory.addItemStackToInventory(new ItemStack(BLItems.onice_spear, 1)))
            {
                flag = false;
            }

            if (flag)
            {
                this.playSound("random.pop", 0.2F, ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F);
                entityIn.onItemPickup(this, 1);
                this.setDead();
            }
        }
    }

    @Override
public void writeSpawnData(ByteBuf buffer) {
	buffer.writeInt(shootingEntity != null ? shootingEntity.getEntityId() : -1);
}

@Override
public void readSpawnData(ByteBuf buffer) {
	Entity shooter = worldObj.getEntityByID(buffer.readInt());
	if (shooter instanceof EntityLivingBase) {
		shootingEntity = (EntityLivingBase) shooter;
	}
}
}

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

I have that constructor, is here

public EntityOniceSpear(World worldIn)
    {
        super(worldIn);
    }

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

It is in the new code. Anyway i've updated the original post so you should see it now

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

I see your problem now - you have duplicated the EntityArrow#onGround field, but you can't override class fields like that. EntityArrow's onGround is the one being used in all of the logic, and yours just sits there doing nothing.

 

Since it is private in 1.8, you can use reflection (recommended course of action) or find some other way to determine if it is in the ground (e.g. current velocity < 0.1 or something). You could even use ASM to change the vanilla code so that the field is public, if you wanted, but that's probably more work than it's worth.

Posted

I think i'll try the "velocity way". Unfortunately i don't know how to use asm (any link to a tutorial will be very appreciated) and for reflection i'm still learning how it works :) I'll let you know if checking it's velocity solve the problem ;)

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

Alright, solved by doing this

public boolean isInGround()
    {
    	double x = this.posX - this.lastTickPosX;
    	double y = this.posY - this.lastTickPosY;
    	double z = this.posZ - this.lastTickPosZ;
    	
    	return (x == 0.0D && y == 0.0D && z == 0.0D);
    }
    
/**
     * Called by a player entity when they collide with an entity
     */
    @Override
    public void onCollideWithPlayer(EntityPlayer entityIn)
    {
        if (!this.worldObj.isRemote && this.isInGround() && this.arrowShake <= 0)
        {
            boolean flag = this.canBePickedUp == 1 || this.canBePickedUp == 2 && entityIn.capabilities.isCreativeMode;

            if (this.canBePickedUp == 1 && !entityIn.inventory.addItemStackToInventory(new ItemStack(BLItems.onice_spear, 1)))
            {
                flag = false;
            }

            if (flag)
            {
                this.playSound("random.pop", 0.2F, ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F);
                entityIn.onItemPickup(this, 1);
                this.setDead();
            }
        }
    }

 

Thanks for the help you gave me :D

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

Nice, though double precision doesn't usually lend itself well to direct equality comparisons, it can still work out for the most part :P

 

If you wanted to go the Reflection route, Forge provides the handy ReflectionHelper class. Class fields are indexed starting from 0, so just count the fields in EntityArrow until you reach 'inGround' and you will have the correct index:

// you want field 5 (i.e. 'inGround' from the EntityArrow class) and using the current instance [the spear entity] to retrieve the value
Object o = ReflectionHelper.getPrivateValue(EntityArrow.class, this, 5);

// should print out 'true' when your spear is in the ground and 'false' otherwise
System.out.println("Value for field 5: " + o);

Pretty straightforward, for the most part, though that just scratches the surface ;)

Posted

Using reflection i think is the best way, so if i want to use it where should i put that code (i mean where should i set the onGround value)?

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

I just use it when I want to get the value.

 

The ReflectionHelper sets the field to be accessible when you access it, and if it's already been made accessible I don't think that it has to do that step again so you shouldn't be incurring any extra performance penalty, but I haven't delved too deeply into the details of exactly how Reflection works so take all that with a grain of salt.

 

At any rate, since you will only be using Reflection in onCollideWithPlayer, even if it wasn't the most performance-friendly solution no one would ever notice because it is usually only called once ;)

Posted

Thank you, i've change the isInGround function to this

public boolean isInGround()
    {
    	return ReflectionHelper.getPrivateValue(EntityArrow.class, this, 5);
    }

and it's still working, so i think i'll leave this ;) Thanks really much for your help and explanations, i only i could i'll give you more than one thank you :D

Don't blame me if i always ask for your help. I just want to learn to be better :)

Guest
This topic is now closed to further replies.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • 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.