Jump to content

Item dupe when tossed while tag is beeing updated in inventoryTick (Ghost Item)


HDainester

Recommended Posts

What is my goal:

Create an item with forge energy support. It is supposed to switch between an active and inactive state. It should drain energy while active and inside the inventory of an entity.

What I am currently doing to archieve this:

I have an override of inventoryTick() in my item class which currently looks like this:

@Override
public void inventoryTick(ItemStack stack, Level level, Entity entity, int i, boolean bl) {
    if(!level.isClientSide) {
        if(updateActive(stack, level, entity)) { // checks state of the 'active' boolean tag
            for(var stackInHand : entity.getHandSlots()) {
                if(stackInHand == stack) {
                    updateLight(level, entity); // might cause some lag (places light blocks)
                    break;
                }
            }

            // extractEnergy(stack, 1);
            stack.getOrCreateTag().putInt("energy", 1000 + (int)(1000*Math.random())); // for testing
        }
    }
}

Apart from that I have defined an ICapabilityProvider which provides an instance of my custom IEnergyStorage implementation (stores current energy of an ItemStack in the 'energy' tag). As can be seen in the snippet above I have disabled the the extractEnergy() call and modified the energy tag directly to ensure nothing is wrong with my IEnergyStorage implementation.

What's the issue:

It appears that sometimes when I drop an 'active' item into the world the entity is dropped but a ghost item stays in the slot of the player inventory. I could confirm with debug outputs that inventoryTick() is only called on the client side from that point on (checking the inventory slot with the data command also reports that no item exist in that slot). As soon as I open the inventory the ghost item becomes an actual item (i.e. it is beeing updated on the server side again) effectively duping the item.

I have observed this behaviour only when dropping the item while the game is lagging (sometimes). Though even a minor lag spike could be enough I guess. In my case this can be caused by the updateLight() call (depending on the mod settings and the spec of the machine) since it places light blocks in the world causing some light updates. I use this call with high settings (i.e. placing many light blocks) to forcefully cause some lag to be able to somewhat consistently replicate this issue.

I guess the information that the item was dropped does not reach the client. This is somewhat surprising to me since from my understanding this could happen to any item?! Though I have never observed this kind of bug with any other modded (or vanilla) item from what I can recall.

Whats interesting is that if I remove the randomness from the tag value (e.g. stack.getOrCreateTag().putInt("energy", 1000)) the issue seems to be gone. Hence I came to the conclusion this might be tied to the tag values getting out of sync between the server and client. But I have no idea what to do about this.

Minimal example:

I have set up a project with a minimal example showcasing the bug. The repository can be found here: https://gitlab.com/hd-mc-mods/examples/item-tick-bug

So this makes me wonder:

  • Am I not supposed to change tag values in inventoryTick()?
  • Is there a way to check on the client side if an item stack does not exist anymore on the server side (i.e. remove ghost items)?
  • Is it wrong to put to much load on the server inside inventoryTick() (e.g. updateLight())? What would be alternatives?
  • Am I doing something obviously wrong that is out of my grasp?

I hope somebody with a little more insight can help me with this or point me to good examples matching my usecase.

Edited by HDainester
Removed irrelevant information and added link to example project.
Link to comment
Share on other sites

If you want help with your code, put a reproducable example of your problem on github.

Not random and incomplete code snippets out of context in this forum.

We need to see how the code fits together and maybe try it for ourselves if the problem is not obvious

 

I am not very familiar with inventoryTick and what rules apply to it. It is mostly used by Mojang to do animation related stuff on the client,

But one place they do use it on the server is to update MapItem information. This does not modify the actual item, instead it updates information stored elsewhere.

 

It would not suprise to learn that modifying an ItemStack while the game is iterating over the inventory causes it to become very confused.

The tags on an ItemStack form part of its "identity".

But I don't know that for sure.

Edited by warjort

Boilerplate:

If you don't post your logs/debug.log we can't help you. For curseforge you need to enable the forge debug.log in its minecraft settings. You should also post your crash report if you have one.

If there is no error in the log file and you don't have a crash report then post the launcher_log.txt from the minecraft folder. Again for curseforge this will be in your curseforge/minecraft/Install

Large files should be posted to a file sharing site like https://gist.github.com  You should also read the support forum sticky post.

Link to comment
Share on other sites

A quick search finds this example in the Botania mod updating tags during inventoryTick, so my concern about that is probably not correct?

https://github.com/VazkiiMods/Botania/blob/0568ec726bd42c506cdfa00705159dbe9b897b46/Xplat/src/main/java/vazkii/botania/common/item/equipment/tool/VitreousPickaxeItem.java#L90

Boilerplate:

If you don't post your logs/debug.log we can't help you. For curseforge you need to enable the forge debug.log in its minecraft settings. You should also post your crash report if you have one.

If there is no error in the log file and you don't have a crash report then post the launcher_log.txt from the minecraft folder. Again for curseforge this will be in your curseforge/minecraft/Install

Large files should be posted to a file sharing site like https://gist.github.com  You should also read the support forum sticky post.

Link to comment
Share on other sites

By the way, if you are concerned about lag, you can always throttle your tick handling.

Something like:

if (entity.tickCount % 20 == 0) {
   // do processing once per second
}

 

Edited by warjort

Boilerplate:

If you don't post your logs/debug.log we can't help you. For curseforge you need to enable the forge debug.log in its minecraft settings. You should also post your crash report if you have one.

If there is no error in the log file and you don't have a crash report then post the launcher_log.txt from the minecraft folder. Again for curseforge this will be in your curseforge/minecraft/Install

Large files should be posted to a file sharing site like https://gist.github.com  You should also read the support forum sticky post.

Link to comment
Share on other sites

Posted (edited)

Hi warjort,

thanks for the answer. I have set up a repository with a minimal example illustrating the issue. You can find it here: https://gitlab.com/hd-mc-mods/examples/item-tick-bug . There is also a gif that shows how to reproduce the bug. For now I will look into how Botania did it (though maybe the bug went unnoticed on their side?! I will have to check I guess). Thanks for pointing that out!

14 hours ago, warjort said:

It would not suprise to learn that modifying an ItemStack while the game is iterating over the inventory causes it to become very confused.

I indeed get the hunch that this might be the case considering I had no issues replicating the bug with a minimal example.

Edited by HDainester
Fixed link to repo
Link to comment
Share on other sites

So I have experimented around with the example project and could reproduce this bug with following alternatives:

Changing the tag in an override of onUseTick()

ExampleItem#onUseTick

@Override
public void onUseTick(Level level, LivingEntity living, ItemStack stack, int count) {
    if(!level.isClientSide) {
        stack.getOrCreateTag().putInt("test", (int)(1000*Math.random()));
    }
}

Changing the tag of only the main- or offhand item in an tick event handler for the LivingEntity holding the stack

ExampleItem#update

public static void update(LivingEntity entity) {
    var stack = entity.getMainHandItem();
    var item = ExampleMod.EXAMPLE_ITEM.get();

    if(!stack.is(item)) {
        stack = entity.getOffhandItem();
    }

    if(stack.is(item)) {
        var tag = stack.getOrCreateTag();
        tag.putInt("test", (int)(1000*Math.random()));
    }
}

ExampleMod

// ... (static fields)

public ExampleMod() {
    IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
    ITEMS.register(modEventBus);
    modEventBus.addListener(this::commonSetup);
}

public void commonSetup(FMLCommonSetupEvent evt) {
    IEventBus bus = MinecraftForge.EVENT_BUS;
    bus.addListener((LivingEvent.LivingTickEvent e) -> ExampleItem.update(e.getEntity()));
}

I was also able to reproduce the bug while updating the tag only every 5 ticks, e.g.

ExampleItem#update

public static void update(LivingEntity entity) {
    if(entity.tickCount % 5 == 0) {
    	// ...
    }
}

I could lower the update rate even more but I feel like that this would not be a real soution to the problem but rather making the bug less likely to occour (apart from beeing a little bit inconvenient).

At this point I am really not sure anymore where and how to update tag values properly. It feels like I am missing something crucial. But looking at the source code of other mods (e.g. Botania or MiningGadgets) I cannot seem to figure out what they are doing different.

Link to comment
Share on other sites

Posted (edited)

So, I seem to have found a fix/workaround for this bug.

Simply override Item#onDroppedByPlayer (requires forge) to prevent the original drop action, and instead remove and drop the item from there. Here an example:

@Override
public boolean onDroppedByPlayer(ItemStack stack, Player player) {
    player.getInventory().removeItem(stack);
    player.drop(stack, true);
    return false; // prevents original drop action
}

I am still not really sure why this is necessary (I have my assumptions) or if I am updating my item tags wrong but for now I am happy that I got it to work. Still, I wouldn't mind of somebody with more insight would shed some light into the situation.

Edited by HDainester
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.



×
×
  • Create New...

Important Information

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