Jump to content

[1.10.2] Changing Item compound tag removes damage?


IceMetalPunk

Recommended Posts

I have an item which is just supposed to store coordinates. It can also take damage when those coordinates are used by another block. This all works fine...except for some reason, when it stores new coordinates, it also removes all damage done to it before. Which is not intended behavior.

 

Here's the relevant code in the item's class:

 

	@Override
public ActionResult<ItemStack> onItemRightClick(ItemStack stack, World worldIn, EntityPlayer playerIn,
		EnumHand hand) {
	if (worldIn.isRemote) {
		return new ActionResult(EnumActionResult.SUCCESS, stack);
	}
	NBTTagCompound current = new NBTTagCompound();
	if (stack.hasTagCompound()) {
		current = stack.getTagCompound();
	}
	BlockPos pos = playerIn.getPosition();
	int x = pos.getX(), y = pos.getY(), z = pos.getZ();
	current.setInteger("linkX", x);
	current.setInteger("linkY", y);
	current.setInteger("linkZ", z);
	playerIn.addChatComponentMessage(new TextComponentString("Portkey linked to (" + x + ", " + y + ", " + z + ")"));
	stack.setTagCompound(current);
	return new ActionResult(EnumActionResult.SUCCESS, stack);
}

 

Especially since I'm making modifications to the existing stack's tags and not starting a new one, I don't understand why the damage is getting reset when this code fires?

Whatever Minecraft needs, it is most likely not yet another tool tier.

Link to comment
Share on other sites

How are you damaging the item?

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

 

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

 

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

Link to comment
Share on other sites

On collision with a block, if the item is being held, I'm calling player.getHeldItemMainHand().damageItem to take 1 durability from it. The damage is being applied correctly, and it's only resetting when I trigger the item use method above.

 

*EDIT* Okay, so I seem to have solved the problem by changing the early return statement from if (world.isRemote){} to if (!world.isRemote){}... unless I'm getting it backwards (which is possible, since I often mix it up in my head), world.isRemote is true if it's the client, so by returning when it's false, I'm actually running the code on the client only, yes? I don't understand why that would be the solution (or why running it on the server-side would cause the problem), but that's good, I guess.

 

However, there's another problem: the item damage isn't being saved with the world. I thought that was something which occurred naturally? The ItemStack#writeToNBT method seems to write the damage automatically, and I'm not overriding anything in there; and the damageItem() call isn't in any kind of side-checked conditional (i.e. it runs on both server and client), so what other reasons would there be for the damage to be unsaved?

 

If it helps, here's the item's code:

 

package com.IceMetalPunk.amethystic.AmethysticItems;

import com.IceMetalPunk.amethystic.Amethystic;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.world.World;

public class ItemPortkey extends Item {
public ItemPortkey() {
	super();
	this.setMaxStackSize(1);
	this.setMaxDamage(100);
	this.setUnlocalizedName("portkey").setRegistryName(Amethystic.MODID, "portkey");
	this.setCreativeTab(Amethystic.AMETHYSTIC_TAB);
}

// Link portkey to current position when used
@Override
public ActionResult<ItemStack> onItemRightClick(ItemStack stack, World worldIn, EntityPlayer playerIn,
		EnumHand hand) {
	if (!worldIn.isRemote) {
		return new ActionResult(EnumActionResult.SUCCESS, stack);
	}
	stack = playerIn.getHeldItem(hand);
	NBTTagCompound current = new NBTTagCompound();
	if (stack.hasTagCompound()) {
		current = stack.getTagCompound();
	}
	BlockPos pos = playerIn.getPosition();
	int x = pos.getX(), y = pos.getY(), z = pos.getZ();
	current.setInteger("linkX", x);
	current.setInteger("linkY", y);
	current.setInteger("linkZ", z);
	playerIn.addChatComponentMessage(new TextComponentString("Portkey linked to (" + x + ", " + y + ", " + z + ")"));
	stack.setTagCompound(current);
	return new ActionResult(EnumActionResult.SUCCESS, stack);
}
}

 

And the damage is being done in a block's collision method, using this code:

 

	@Override
public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity) {
	System.out.println("Collision occured!");
	if (entity instanceof EntityPlayer) {
		System.out.println("Player collided!");
		EntityPlayer player = (EntityPlayer) entity;
		ItemStack mainItem = player.getHeldItemMainhand();
		ItemStack offItem = player.getHeldItemOffhand();
		if (mainItem != null && mainItem.getItem() == Amethystic.items.PORTKEY && mainItem.hasTagCompound()) {
			NBTTagCompound tag = mainItem.getTagCompound();
			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");
			mainItem.damageItem(1, player);
			player.setPositionAndUpdate(x, y, z);
			player.setFire(1);
			System.out.println("Has portkey in main hand linked to (" + x + ", " + y + ", " + z + ")!");
			// TODO: Portkey teleportation sound
		}
		else if (offItem != null && offItem.getItem() == Amethystic.items.PORTKEY && offItem.hasTagCompound()) {

			NBTTagCompound tag = offItem.getTagCompound();
			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");
			offItem.damageItem(1, player);
			player.setPositionAndUpdate(x, y, z);
			player.setFire(1);
			// TODO: Portkey teleportation sound
			System.out.println("Has portkey in offhand linked to (" + x + ", " + y + ", " + z + ")!");
		}
		else {
			System.out.println("No portkey!");
		}
	}
}

 

Any ideas?

Whatever Minecraft needs, it is most likely not yet another tool tier.

Link to comment
Share on other sites

You need to damage the item on the server, otherwise it won't persist.

I am...of you look at the code, the damageItem call doesn't check for remoteness, and so it should be called on both client and server, right?

 

*EDIT* In fact, a quick test suggests that it's only called on the server, not on the client.

Whatever Minecraft needs, it is most likely not yet another tool tier.

Link to comment
Share on other sites

Your post made me think of something, so I did a little more debugging, and I'm getting close to finding the problem. In the onItemRightClick method (where the tags are changed), I put a little debug message; the result was that I saw the damage on the server stay at 0 even while the damage on the client increased.

 

Which is very confusing to me, because if I output player.isServerWorld() in the block collision code (where the item is damaged), it always outputs true. So if the damaging it being done only on the server, then how is the damage only actually applying on the client?

Whatever Minecraft needs, it is most likely not yet another tool tier.

Link to comment
Share on other sites

"Damage not being saved" and "stays 0 on the server" means you're only damaging the item on the client side.

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

 

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

 

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

Link to comment
Share on other sites

"Damage not being saved" and "stays 0 on the server" means you're only damaging the item on the client side.

I know...which is what I don't understand. As I said, the method that calls the item damaging has a debug output that prints player.isServerWorld(). Every output from that is true; i.e. it's only being called on the server (or at least it *is* being called on the server). And yet everything else regarding the damage suggests it's only being called on the client. How is it possible for isServerWorld (which just wraps

!world.isRemote

anyway) to return true but the next line of code to run only on the client?

Whatever Minecraft needs, it is most likely not yet another tool tier.

Link to comment
Share on other sites

"Damage not being saved" and "stays 0 on the server" means you're only damaging the item on the client side.

I know...which is what I don't understand. As I said, the method that calls the item damaging has a debug output that prints player.isServerWorld(). Every output from that is true; i.e. it's only being called on the server (or at least it *is* being called on the server). And yet everything else regarding the damage suggests it's only being called on the client. How is it possible for isServerWorld (which just wraps

!world.isRemote

anyway) to return true but the next line of code to run only on the client?

Just to clear it up !world.isRemote is server side and world.isRemote is client side. In the code above you return if !world.isRemote without doing anything in Item#onItemRightClick. And for the player.isServerWorld() println how are we supposed to know with out seeing updated code.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

"Damage not being saved" and "stays 0 on the server" means you're only damaging the item on the client side.

I know...which is what I don't understand. As I said, the method that calls the item damaging has a debug output that prints player.isServerWorld(). Every output from that is true; i.e. it's only being called on the server (or at least it *is* being called on the server). And yet everything else regarding the damage suggests it's only being called on the client. How is it possible for isServerWorld (which just wraps

!world.isRemote

anyway) to return true but the next line of code to run only on the client?

Just to clear it up !world.isRemote is server side and world.isRemote is client side. In the code above you return if !world.isRemote without doing anything in Item#onItemRightClick. And for the player.isServerWorld() println how are we supposed to know with out seeing updated code.

 

Yes, I know; originally, I had it return if world.isRemote was true, but that was giving me the initial problem of removing all the damage from the item; after changing it to return is world.isRemote was false, that problem stopped happening, although now the damage isn't saving; since it didn't actually fix the problem completely, I've since changed it back.

 

But the code that actually damages the item isn't doing any checks for server/client side at all; it should be running whenever the block collision method is called. The "updated code" is exactly the same as above with an added console log, but here it is again:

 

	@Override
public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity) {
	if (entity instanceof EntityPlayer) {
		EntityPlayer player = (EntityPlayer) entity;

		ItemStack mainItem = player.getHeldItemMainhand();
		ItemStack offItem = player.getHeldItemOffhand();
		if (mainItem != null && mainItem.getItem() == Amethystic.items.PORTKEY && mainItem.hasTagCompound()) {
			NBTTagCompound tag = mainItem.getTagCompound();
			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");

			System.out.println("Player collided! On server?: " + player.isServerWorld());

			mainItem.damageItem(1, player); // FIXME: Damage only occurs on
											// client, not server?
			player.setPositionAndUpdate(x, y, z);
			player.setFire(1);
		}
		else if (offItem != null && offItem.getItem() == Amethystic.items.PORTKEY && offItem.hasTagCompound()) {

			NBTTagCompound tag = offItem.getTagCompound();
			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");
			offItem.damageItem(1, player);
			player.setPositionAndUpdate(x, y, z);
			player.setFire(1);
		}
	}
}

 

That println() with the player.isServerWorld() call is always printing true, meaning it's only running on the server, correct? And yet, as I mentioned, when right-clicking the item, other debug outputs show the server version of the item has 0 damage at all times, while the client version has the proper amount of damage.

Whatever Minecraft needs, it is most likely not yet another tool tier.

Link to comment
Share on other sites

Entity#isServerWorld

returns the inverse of

World#isRemote

by default, but

EntityPlayerSP

overrides it to return

true

.

EntityLiving

also overrides it to only return

true

if the entity's AI is enabled.

 

If you want to check whether you're on the server, use

World#isRemote

directly.

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

Link to comment
Share on other sites

Entity#isServerWorld

returns the inverse of

World#isRemote

by default, but

EntityPlayerSP

overrides it to return

true

.

EntityLiving

also overrides it to only return

true

if the entity's AI is enabled.

 

If you want to check whether you're on the server, use

World#isRemote

directly.

 

...d'oh! >_< Of course there would be overrides that I didn't see in my examination of things... I bet I'd have seen that if I'd added a breakpoint and stepped through the method... thanks.

 

So now that brings me to the true nature of this problem: the collision method is, in fact, only being called on the client after all, not on the server. So how would I detect a collision on the server? There doesn't even seem to be an event to hook into for that (presumably because this method is the preferred way to handle collisions), so how would I damage the item on the server when collisions seem to only be handled on the client?

Whatever Minecraft needs, it is most likely not yet another tool tier.

Link to comment
Share on other sites

Block#onEntityCollidedWithBlock

is called on both the client and the server. Are you sure your override isn't being called on the server?

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

Link to comment
Share on other sites

Block#onEntityCollidedWithBlock

is called on both the client and the server. Are you sure your override isn't being called on the server?

100%. Below is the full code of the block class, and the only output I'm getting in the console is "Player collided! On server?: false" (and only once per collision, not twice like you'd expect from a server and client execution).

 

package com.IceMetalPunk.amethystic.AmethysticBlocks;

import java.util.Random;

import com.IceMetalPunk.amethystic.Amethystic;

import net.minecraft.block.Block;
import net.minecraft.block.BlockFire;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;

public class BlockEnderFlame extends BlockFire {
public BlockEnderFlame() {
	super();
	this.setUnlocalizedName("ender_flame").setRegistryName(Amethystic.MODID, "ender_flame");
}

@Override
public int tickRate(World worldIn) {
	return 10;
}

@Override
public MapColor getMapColor(IBlockState state) {
	return MapColor.CYAN;
}

@Override // So the Ender Flame doesn't spread
public void updateTick(World world, BlockPos pos, IBlockState state, Random rand) {
	Block block = world.getBlockState(pos.down()).getBlock();
	int i = ((Integer) state.getValue(AGE)).intValue();
	boolean flag = block.isFireSource(world, pos.down(), EnumFacing.UP);

	if (!flag && world.isRaining() && this.canDie(world, pos) && rand.nextFloat() < 0.2F + (float) i * 0.03F) {
		world.setBlockToAir(pos);
	}
	else if (i < 15) {
		state = state.withProperty(AGE, Integer.valueOf(i + 1));
		world.setBlockState(pos, state, 4);
		world.scheduleUpdate(pos, this, this.tickRate(world) + rand.nextInt(10));
	}
	else {
		world.setBlockToAir(pos);
	}
}

// Teleport the player when they walk through the flames with a linked
// portkey
@Override
public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity) {
	if (entity instanceof EntityPlayer) {
		EntityPlayer player = (EntityPlayer) entity;

		ItemStack mainItem = player.getHeldItemMainhand();
		ItemStack offItem = player.getHeldItemOffhand();
		if (mainItem != null && mainItem.getItem() == Amethystic.items.PORTKEY && mainItem.hasTagCompound()) {
			NBTTagCompound tag = mainItem.getTagCompound();
			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");

			System.out.println("Player collided! On server?: " + !player.worldObj.isRemote);

			mainItem.damageItem(1, player); // FIXME: Damage only occurs on
											// client, not server?
			player.setPositionAndUpdate(x, y, z);
			player.setFire(1);
		}
		else if (offItem != null && offItem.getItem() == Amethystic.items.PORTKEY && offItem.hasTagCompound()) {

			NBTTagCompound tag = offItem.getTagCompound();
			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");
			offItem.damageItem(1, player);
			player.setPositionAndUpdate(x, y, z);
			player.setFire(1);
		}
	}
}
}

Whatever Minecraft needs, it is most likely not yet another tool tier.

Link to comment
Share on other sites

Block#onEntityCollidedWithBlock

is called on both the client and the server. Are you sure your override isn't being called on the server?

100%. Below is the full code of the block class, and the only output I'm getting in the console is "Player collided! On server?: false" (and only once per collision, not twice like you'd expect from a server and client execution).

 

package com.IceMetalPunk.amethystic.AmethysticBlocks;

import java.util.Random;

import com.IceMetalPunk.amethystic.Amethystic;

import net.minecraft.block.Block;
import net.minecraft.block.BlockFire;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;

public class BlockEnderFlame extends BlockFire {
public BlockEnderFlame() {
	super();
	this.setUnlocalizedName("ender_flame").setRegistryName(Amethystic.MODID, "ender_flame");
}

@Override
public int tickRate(World worldIn) {
	return 10;
}

@Override
public MapColor getMapColor(IBlockState state) {
	return MapColor.CYAN;
}

@Override // So the Ender Flame doesn't spread
public void updateTick(World world, BlockPos pos, IBlockState state, Random rand) {
	Block block = world.getBlockState(pos.down()).getBlock();
	int i = ((Integer) state.getValue(AGE)).intValue();
	boolean flag = block.isFireSource(world, pos.down(), EnumFacing.UP);

	if (!flag && world.isRaining() && this.canDie(world, pos) && rand.nextFloat() < 0.2F + (float) i * 0.03F) {
		world.setBlockToAir(pos);
	}
	else if (i < 15) {
		state = state.withProperty(AGE, Integer.valueOf(i + 1));
		world.setBlockState(pos, state, 4);
		world.scheduleUpdate(pos, this, this.tickRate(world) + rand.nextInt(10));
	}
	else {
		world.setBlockToAir(pos);
	}
}

// Teleport the player when they walk through the flames with a linked
// portkey
@Override
public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity) {
	if (entity instanceof EntityPlayer) {
		EntityPlayer player = (EntityPlayer) entity;

		ItemStack mainItem = player.getHeldItemMainhand();
		ItemStack offItem = player.getHeldItemOffhand();
		if (mainItem != null && mainItem.getItem() == Amethystic.items.PORTKEY && mainItem.hasTagCompound()) {
			NBTTagCompound tag = mainItem.getTagCompound();
			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");

			System.out.println("Player collided! On server?: " + !player.worldObj.isRemote);

			mainItem.damageItem(1, player); // FIXME: Damage only occurs on
											// client, not server?
			player.setPositionAndUpdate(x, y, z);
			player.setFire(1);
		}
		else if (offItem != null && offItem.getItem() == Amethystic.items.PORTKEY && offItem.hasTagCompound()) {

			NBTTagCompound tag = offItem.getTagCompound();
			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");
			offItem.damageItem(1, player);
			player.setPositionAndUpdate(x, y, z);
			player.setFire(1);
		}
	}
}
}

Put a println before you do any if checks and just a suggestion use the logger system as it tells you whether it is server or client. Maybe the println does that but i dont remember it that way.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

I didn't even know Forge had its own logger O_o . I learn something new every day xD But yeah, System.out.println does actually show whether the message is coming from the client thread or the server thread.

 

As I was testing with the new logger, I think I've stumbled (accidentally) upon a key component of this problem. As you can see from the code, it teleports the player away if they're holding a linked item. During testing, I accidentally walked into the block without holding the proper item...and suddenly, I started getting console output from both the client and the server.

 

So I'm going to assume that before, the client was teleporting the player away before the server registered a collision, causing the code to only run on the client side. Now that I've simply altered the code so it will only run on the server, everything is fixed.

 

I guess I should take this as a lesson: just because something strange is happening doesn't mean your own code isn't to blame xD

 

I'm now getting another bug related to the teleportation, but it's not related to the item data, so I'll make a new topic for it.

 

Thanks, everyone who helped! :D

Whatever Minecraft needs, it is most likely not yet another tool tier.

Link to comment
Share on other sites

Join the conversation

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

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

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

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

×   Your previous content has been restored.   Clear editor

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

Announcements



×
×
  • Create New...

Important Information

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