Jump to content

[1.12.2][SOLVED] Issue with update() in tile entity


Recommended Posts

Posted (edited)

-------------------------------------------------------------------------------------------------------------------

SOLVED:

Long story short, I messed up accessing the Tile Entity Pos for each instance.

The solution was to scrap the setPos() method (which I first of all should have renamed), and use this.getPos() whenever I needed the Tile Entity pos.

I already knew about getPos() but it seems I implemented it wrong, cause it always returned BlockPos(0,0,0).

 

In Block I removed everything inside onBlockPlacedBy

In Tile Entity I removed the setPos() method and the posB  field.

And as example I now use: mapQuarry(5, this.getPos())

 

---------------------------------------------------------------------------------------------------------------------

Hi!

I'm very new to modding in Minecraft. I got stuck when changing from calling TE-method from the Block's "onBlockActivated", to using update() instead.

The goal is to "world.destroyBlock" at BlockPos specified in AREA<BlockPos> every 20 ticks. This worked if I just called a TE-method from onBlockActivated and looped through AREA (although this happens instantly).

I'm obviously missing something, as update() runs pretty randomly when i restart the game. 

 

I'll be happy if i just get the world.destroyBlock to start removing blocks in update().

 

Block class:

 

package com.jolge.derpworld.block;

import java.util.Random;

import com.jolge.derpworld.DerpWorld;
import com.jolge.derpworld.init.BlockInit;
import com.jolge.derpworld.init.ItemInit;
import com.jolge.derpworld.tileentity.JdwOSDigger;
import com.jolge.derpworld.util.IHasModel;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.stats.StatList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityFurnace;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;

public class JdwBlockBitumen extends Block implements IHasModel{
	
	public JdwBlockBitumen(String name, Material material) 
	{
		super(material);
		setUnlocalizedName(name);
		setRegistryName(name);
		setCreativeTab(CreativeTabs.MISC);
		setHarvestLevel("pick_axe", 1);
		
		BlockInit.BLOCKS.add(this);
		
		ItemInit.ITEMS.add(new ItemBlock(this).setRegistryName(this.getRegistryName()));
	}
	
	@Override
	public boolean hasTileEntity(IBlockState state) {
		return true;
	}
	
	@Override
	public TileEntity createTileEntity(final World worldIn, final IBlockState state) {
		return new JdwOSDigger();
	}
	
	@Override
	public void onBlockAdded(World worldIn, BlockPos pos, IBlockState state) {
		
	}
	
	@Override
	public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
    {
        if (!worldIn.isRemote )
        {
            final JdwOSDigger tileEntity = (JdwOSDigger) worldIn.getTileEntity(pos);  
            if(tileEntity != null) {
            	 
            	tileEntity.toggleActive();
              	
            }      
        }
            return true;
    }
  
	@Override
	public void onBlockPlacedBy(World worldIn, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack) {
			
			TileEntity tileentity = worldIn.getTileEntity(pos);

			if (tileentity instanceof JdwOSDigger)
			{
				
				((JdwOSDigger)tileentity).setPos(pos);	
			}	
	}
	
	@Override
	public void registerModels() 
	{
		DerpWorld.proxy.registerItemRenderer(Item.getItemFromBlock(this), 0, "inventory");
	}

}

 

 

Tile Entity class:

 

package com.jolge.derpworld.tileentity;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import net.minecraft.block.Block;
import net.minecraft.block.BlockDirt;
import net.minecraft.block.BlockGrass;
import net.minecraft.block.state.IBlockState;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ITickable;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
import scala.actors.threadpool.Arrays;

public class JdwOSDigger extends TileEntity implements ITickable{

	
	public boolean isActivated;
	public BlockPos posB;
	public List<BlockPos> AREA = new ArrayList<BlockPos>();
	public int radi = 0;
	public int index = 0;
	public int tickCounter = 0;
	
	public JdwOSDigger() {
		super();
	}
	
	public void setPos(BlockPos pos) {
		this.posB = pos;
	}
	
	
	@Override
	public void update() 
	{
		if (!world.isRemote){
			if(isActivated)  {
				tickCounter++;
				if (tickCounter==20) {
					world.destroyBlock(AREA.get(index), false);
					System.out.println("Destroyed block at:  " + AREA.get(index) );
					index++;
					tickCounter = 0;
				}
			}
		}
	}
	
	
	public void toggleActive() {
		if (this.isActivated) {
			this.isActivated = false;
		}
		else {
			this.isActivated = true;
			mapQuarry(5, this.posB);
		}
	}
	
	public void mapQuarry (int radi, BlockPos pos) {
	
		List<BlockPos> l = new ArrayList<BlockPos>();
		int[] squares = new int[radi];
		BlockPos qC = null; //Quadrant Center
		int bx, by, bz = 0;
			
		for(int k = 0; k<4; k++) {
			
			switch(k){
			case 0: qC = pos.add(radi+1, 0, radi+1); //+X+Z = NorthEast = 1
					break;
			case 1: qC = pos.add(-(radi+1), 0, radi+1); //-X+Z = NorthWest = 2
					break;
			case 2: qC = pos.add(radi+1, 0, -(radi+1)); //+X-Z = SouthEast = 3
					break;
			case 3: qC = pos.add(-(radi+1), 0, -(radi+1)); //-X-Z = SouthWest = 4
					break;
			}
			
			for(int e = 0; e<radi/2; e++) {
				for(int i = -radi+e; i <= radi-e; i++) {
					for (int j = -radi+e; j<=radi-e; j++) {
						bx = qC.getX() + i;
						bz = qC.getZ() + j;
						by = qC.getY() - 1 - e;
						
						BlockPos pos2 = new BlockPos( bx , by, bz );								
						l.add(pos2);
					}
				}
			}
		}
		AREA.addAll(l);	
	}	
}

 

Edited by uncleofbob
Posted

You don't implement writeToNBT and readFromNBT.

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.

Posted

I have done some tests with the NBT methods. update() still doesn't seem to run.

Should NBT be the case when I create a new world and update() doesn't work?

 

	public void readFromNBT(NBTTagCompound compound)
    {
        super.readFromNBT(compound);
        //his.note = compound.getByte("note");
       this.isActivated = compound.getBoolean("Status");
       this.tickCounter = compound.getInteger("count");
       this.index = compound.getInteger("indexz");
    }
	
    public NBTTagCompound writeToNBT(NBTTagCompound compound)
    {
        super.writeToNBT(compound);
        //compound.setByte();
       compound.setInteger("indexz", this.index);
       compound.setInteger("count", this.tickCounter);
       compound.setBoolean("Status", this.isActivated);
        return compound;
    }

 

Posted

Thanks for the quick replies! I think the tile entity gets registered. I can call methods from onBlockActivated which works fine.

Is there something wrong with the tile entity constructor maybe?

 

My RegistryHandler.class : Should I register right after Blocks instead?

package com.jolge.derpworld.util;

import com.jolge.derpworld.init.BlockInit;
import com.jolge.derpworld.init.ItemInit;
import com.jolge.derpworld.tileentity.JdwOSDigger;

import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.registry.GameRegistry;
import net.minecraftforge.registries.IForgeRegistry;

@EventBusSubscriber
public class RegistryHandler {
	
@SubscribeEvent
public static void onBlockRegister(RegistryEvent.Register<Block> event)
{
	event.getRegistry().registerAll(BlockInit.BLOCKS.toArray(new Block[0]));
				
}
  
@SubscribeEvent
public static void onItemRegister(RegistryEvent.Register<Item> event)
{
	event.getRegistry().registerAll(ItemInit.ITEMS.toArray(new Item[0]));
	registerTileEntities();
}

@SubscribeEvent
public static void onModelRegister(ModelRegistryEvent event)
{
	for(Item item : ItemInit.ITEMS)
	{
		if(item instanceof IHasModel)
		{
			((IHasModel)item).registerModels();
		}
	}
		
	for(Block block : BlockInit.BLOCKS)
	{
		if(block instanceof IHasModel)
		{
			((IHasModel)block).registerModels();
		}
	}
}

public static void registerTileEntities() {
		
	GameRegistry.registerTileEntity(JdwOSDigger.class, Reference.MODID + "oil_sand_digger");
	System.out.println("Registered: oil_sand_digger");
}

 

Posted

Now I'm doing it like this:

 

@EventHandler
public void preinit(FMLPreInitializationEvent event) {
		GameRegistry.registerTileEntity(JdwOSDigger.class, Reference.MODID + "oilsanddigger");
}

 

Posted (edited)
1 hour ago, uncleofbob said:

I sort of copied the way its done in TestMod3:

 

https://github.com/Choonster-Minecraft-Mods/TestMod3/blob/1.12.2/src/main/java/choonster/testmod3/init/ModBlocks.java

 

Changed to preInit now, but still no update() running.

 

That's actually a mistake, I intended to do that in RegistryEvent.Register<Block> rather RegistryEvent.Register<Item>. LexManos recommended this here.

 

The two events are fired at roughly the same time, but it makes more sense to register TileEntities with Blocks rather than with Items.

 

Edit: Fixed.

Edited by Choonster

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

Digging through the Forge and Minecraft sourcecode, registering a tile entity does not automatically register tickers when registering through the registerTileEntity. I find no other references to the list. 

 

public static void registerTileEntity(Class<? extends TileEntity> tileEntityClass, String key)
    {
        TileEntity.register(key, tileEntityClass);
    }
public static void register(String id, Class <? extends TileEntity > clazz)
    {
        REGISTRY.putObject(new ResourceLocation(id), clazz);
    }
public void putObject(K key, V value)
    {
        Validate.notNull(key);
        Validate.notNull(value);
        this.values = null;

        if (this.registryObjects.containsKey(key))
        {
            LOGGER.debug("Adding duplicate key '{}' to registry", key);
        }

        this.registryObjects.put(key, value);
    }

 

So far I cannot find when they are registered for the vanilla items. I'm digging into the source code to see if I can find where they are registered as tickables because it isn't happening there.

Posted

From what I see, the problem is that the vanilla engine is not recognizing the modded tickable blocks until they are effected by certain events. The ITickable interface isn't checked until chunk loading, and then added to a special list. But those changes do not seem to take effect immediately after chunk loading for the version I am compiling against either. The only work around I can see is by using scheduleBlockUpdate. If the delay is high enough though, I see no issues that could hurt the mod in any way.

 

  • Like 1
Posted (edited)

World#updateEntities() runs every tick and iterates through the entire tickableTileEntitiesList and calls their update() method. So the update() method should be called EVERY tick for any ITickable tile entity (provided it is in the tickableTileEntitiesList).

 

To get on the tickableTileEntitiesList, that happens in the World#addTileEntity() method so long as it is in the loadedTileEntityList and also instanceof ITickable.

 

I suppose it is possilble that there is some cases where the loadedTileEntity list isn't updated by the time the addTileEntity() method is called.  The updateEntities() method goes through these general steps in this exact order:

1. Iterates through tileEntitiesToBeRemovedList and runs onChunkUnload() method, removes them from the tickableTileEntitiesList and then removes them from the loadedTileEntitiesList.

2. Iterates through tickableTileEntitiesList and if the tile entity is valid and hasWorld() are true, checks that the block in that position is loaded and inside the world border. If the tile entity is invalid it is removed from the loadeTileEntitiesList and also from the chunk's tile entity association to that BlockPos. Interestingly it is not removed from the tickableTileEntitiesList at this time.

3. Iterates through the addedTileEntitiesList. I find it interesting that it does the adding at the end, but probably an attempt to make sure all these lists are in sync. But it means that there is  one tick between adding the tile entity and the first time it is ticked. Basically the tile entity is checked to be valid and then added to the loadedTileEntitiesList. Then it separately checks if the block is loaded and associates it to the chunk's block position including calling a notifyBlockUpdate().

 

Basically, there are a lot of things that need to be synced and in common Minecraft fashion the coding isn't super organized leaving suspicion related to logical holes. In fact there are comments already in this code such as:  //Forge: Bugfix: If we set the tile entity it immediately sets it in the chunk, so we could be desynced

 

That's about all the effort I'm interested in putting into investigating at this time, but hopefully gives you some ideas on how to debug. I would use the debugger and set breakpoints throughout the World#UpdateEntities() method and watch the order in which your tile entity is added to each list, and when it gets the update() calls.

 

However, my main point is that the intention seems to be that within one tick from a block with tile entity being added, it should be on the loadedTileEntitiesList and if it is also ITickable should run update() every tick from then on. If yours is not, then it is a bug somewhere.

 

 

Edited by jabelar
  • Like 1

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted

That's some nice info jabelar, but it's not working like that. The list loaded during chunk loading does not appear to be realized until after certain events occur, one such event is GUI interaction. I am hoping someone can track down why this is the case, but for now scheduling infrequent tick updates is the only thing that works 100% of the time in all cases.

 

On 1/13/2018 at 7:50 AM, uncleofbob said:

Thx ! So its schedualBlockUpdate in onBlockAdded, and override the updateTick method? Currently Im testing with every 20 ticks, but it may even be slower. Thx again!

Basically when the block is placed or the entity is loaded. onLoad for the entity, onBlockPlacedBy for the block class just because all you need is the block instance since you do have to extract the entity from the world. Which is why I wish the update calling was more reliable. This does explain a lot about Mekanisms and why that mod breaks so much, especially during multiplayer. Thankfully most of what I am doing doesn't require constant tick checks.

  • Like 1
Posted (edited)

[IGNORE THIS POST!!]]

 

Finally! It seems i've found the issue in my code.

@diesieben07 was right stating that the tile entity didn't register properly

 

Se comments below

 

// I think the registered tile entity excluded ITickable when writing <?extends TileEntity> 

public static void registerTileEntity(final Class<?extends TileEntity> tileEntityClass, final String name) {
				
			GameRegistry.registerTileEntity(tileEntityClass, name);
		}

// when I removed it, it seems to run as I want. I assume it just takes whatever the class extends and implements. 

public static void registerTileEntity(final Class tileEntityClass, final String name) {
				
			GameRegistry.registerTileEntity(tileEntityClass, name);
		}

 

Edited by uncleofbob
Did not solve the problem!
  • Like 1
Posted
51 minutes ago, uncleofbob said:

Finally! It seems i've found the issue in my code.

@diesieben07 was right stating that the tile entity didn't register properly

 

Se comments below

 


// I think the registered tile entity excluded ITickable when writing <?extends TileEntity> 

public static void registerTileEntity(final Class<?extends TileEntity> tileEntityClass, final String name) {
				
			GameRegistry.registerTileEntity(tileEntityClass, name);
		}

// when I removed it, it seems to run as I want. I assume it just takes whatever the class extends and implements. 

public static void registerTileEntity(final Class tileEntityClass, final String name) {
				
			GameRegistry.registerTileEntity(tileEntityClass, name);
		}

 

Great, that means more manual typing of things for me. I was using a registry enum for simpler registration so if I ever need the update method I'll have to actually register them out of the loop. At least you did find a solution, kudos on sticking with it.

Posted

And again..

There must be something wrong with my Modding environment.

I restarted the game 10 times to verify that update() worked..

Only to find out it stopped working after a while...

  • Like 1
Posted (edited)

I've now checked the lists: "tickableTileEntities" and "loadedTileEntityList" while right clicking on block. In combination I ran the Chunk.getTileEntityMap() to see if its found in the chunk. The number (id?) after the tile entity also changes when I destroy the block and place a new, which I assume states that it detects a new instance of my JdwOSDigger tile entity.

 

[12:52:18] [..onBlockActivated:86]: from Chunk: {BlockPos{x=-125, y=66, z=224}=com.jolge.derpworld.tileentity.JdwOSDigger@af23ab1}

[12:52:18] [.....onBlockActivated:90]: loadedTileEntityList[21]: com.jolge.derpworld.tileentity.JdwOSDigger@af23ab1

[12:52:18] [....onBlockActivated:97]: tickableTileEntities[21]: com.jolge.derpworld.tileentity.JdwOSDigger@af23ab1


[12:52:33] [....onBlockActivated:86]: from Chunk: {BlockPos{x=-125, y=66, z=224}=com.jolge.derpworld.tileentity.JdwOSDigger@2979b61f}

[12:52:33] [....onBlockActivated:90]: loadedTileEntityList[21]: com.jolge.derpworld.tileentity.JdwOSDigger@2979b61f

[12:52:33]  [....onBlockActivated:97]: tickableTileEntities[21]: com.jolge.derpworld.tileentity.JdwOSDigger@2979b61f

 

I'll import my mod to a new eclipse environment later, to see if it helps.

 

Edited by uncleofbob
Posted (edited)

I've added "-clean" at the first line in eclipse.ini. This wipes out some eclipse runtime cache which seem to cause the problem.

I'll put the thread to [SOLVED] when I'm 100% sure.

 

Edited by uncleofbob
Nothing relevant
Posted (edited)

--------------------------------------------

Not a solution!

---------------------------------------------

 

 

So I've done like @KittenKodersuggested: 

 

Is this cheat ? 

 

@Override
public void onBlockPlacedBy(World worldIn, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack) {
	
	TileEntity tileentity = worldIn.getTileEntity(pos);
	if (tileentity instanceof JdwOSDigger)
	{
		((JdwOSDigger)tileentity).setPos(pos);
		worldIn.scheduleBlockUpdate(pos, this, 20, 1); // schedule first update
	}
}
	
@Override
public void updateTick(World worldIn, BlockPos pos, IBlockState state, Random rand)
{	
	if(!worldIn.isRemote) {
		TileEntity tileentity = worldIn.getTileEntity(pos);
		if (tileentity instanceof JdwOSDigger){
			((JdwOSDigger)tileentity).jdwUpdate();      //call jdwupDate in tile entity
			worldIn.scheduleBlockUpdate(pos, this, 20, 1);   //schedule next update. 
		}
    }	
}

 

Edited by uncleofbob
  • Like 1
Posted

Well it is a "cheat" in the sense that ITickable should not need any such silliness. Think about it -- a furnace happily cooks something as an ITickable and any animated tile entity is happily updated every tick. You should really find the root cause of the problem. But if it works I guess use it. Something is still wrong with your implementation in my opinion though and it might bite you later.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted
5 hours ago, jabelar said:

Well it is a "cheat" in the sense that ITickable should not need any such silliness. Think about it -- a furnace happily cooks something as an ITickable and any animated tile entity is happily updated every tick. You should really find the root cause of the problem. But if it works I guess use it. Something is still wrong with your implementation in my opinion though and it might bite you later.

A furnace is also recognized by vanilla code. The point at which ITickable is added to a special list is on chunk loading, it looks like it should all work, but the fact is that it doesn't work. 

Posted
9 hours ago, uncleofbob said:

So I've done like @KittenKodersuggested: 

 

Is this cheat ? 

 


@Override
public void onBlockPlacedBy(World worldIn, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack) {
	
	TileEntity tileentity = worldIn.getTileEntity(pos);
	if (tileentity instanceof JdwOSDigger)
	{
		((JdwOSDigger)tileentity).setPos(pos);
		worldIn.scheduleBlockUpdate(pos, this, 20, 1); // schedule first update
	}
}
	
@Override
public void updateTick(World worldIn, BlockPos pos, IBlockState state, Random rand)
{	
	if(!worldIn.isRemote) {
		TileEntity tileentity = worldIn.getTileEntity(pos);
		if (tileentity instanceof JdwOSDigger){
			((JdwOSDigger)tileentity).jdwUpdate();      //call jdwupDate in tile entity
			worldIn.scheduleBlockUpdate(pos, this, 20, 1);   //schedule next update. 
		}
    }	
}

 

Don't forget the schedule update for the entity's onLoad. That is what initiates it on chunk loading.

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.