[MC-99914] DataWatcher / EntityMetadata ID is based on the class load order ! Can cause many issues ! Created: 22/Mar/16  Updated: 14/Jun/16  Resolved: 11/Jun/16

Status: Resolved
Project: Minecraft: Java Edition
Component/s: None
Affects Version/s: Minecraft 1.9, Minecraft 1.9.1 Pre-Release 3, Minecraft 1.9.2, Minecraft 16w14a, Minecraft 16w15a
Fix Version/s: Minecraft 1.10.1

Type: Bug
Reporter: Nathan Poirier Assignee: [Mojang] Grum (Erik Broes)
Resolution: Fixed Votes: 6
Labels: None
Environment:

OS: Windows 10 and Debian Jessie
Java: 1.8.0_73


Issue Links:
Duplicate
is duplicated by MC-93454 Dropped Items are shown as stone [Mul... Resolved
Relates
relates to MC-11834 "[SEVERE] Item entity # has no item?"... Reopened
CHK:
Confirmation Status: Community Consensus

 Description   

Example:
All dropped items will display as stone. DataWatcher ID bug.
https://www.youtube.com/watch?v=788153dV8Hk

Reproduce bug:
You just have to throw a splash potion before viewing any ground item (since your Minecraft was started) !

Then, go on a Minecraft Server (Vanilla, Spigot 1.9 or servers with 1.8/1.9 support) and drop an item on ground or throw a potion.
The item will be stone and the potion will display as water without real potion color.

In logs you will see:

  • "Item entity XXX has no item?!"
  • "ThrownPotion entity XXX has no item?!"

Cause:
This bug append when EntityPotion class was loaded before EntityItem class (so, when you see throwed potion before item on ground).
That's because DataWatcher/EntityMetadata ID was based on class load order in some cases.

Code is from Spigot 1.9, but it's the same on 1.9 client :

EntityItem.java
private static final DataWatcherObject<Optional<ItemStack>> c = DataWatcher.a(EntityItem.class, DataWatcherRegistry.f);
EntityPotion.java
private static final DataWatcherObject<Optional<ItemStack>> d = DataWatcher.a(EntityItem.class, DataWatcherRegistry.f);

This two codes call 'DataWatcher.a(EntityItem.class, DataWatcherRegistry.f);':

public static <T> DataWatcherObject<T> a(Class<? extends Entity> oclass, DataWatcherSerializer<T> datawatcherserializer)
  {
    int i;
    int i;
    if (a.containsKey(oclass))
    {
      i = ((Integer)a.get(oclass)).intValue() + 1;
    }
    else
    {
      int j = 0;
      Class oclass1 = oclass;
      while (oclass1 != Entity.class)
      {
        oclass1 = oclass1.getSuperclass();
        if (a.containsKey(oclass1))
        {
          j = ((Integer)a.get(oclass1)).intValue() + 1;
          break;
        }
      }
      i = j;
    }
    if (i > 254) {
      throw new IllegalArgumentException("Data value id is too big with " + i + "! (Max is " + 254 + ")");
    }
    a.put(oclass, Integer.valueOf(i));
    return datawatcherserializer.a(i);
  }

As you can see, variable i (the datawatcher index) is based on previous oclass value + 1.
So, if EntityItem load first, the id will be 5 and then when EntityPotion load the id will be 6.
But when EntityPotion load first the inverse occurs !

And when the inverse occurs the datawatcher ID was inverse and can't be synchronized with Minecraft servers.



 Comments   
Comment by Brian McNamara [ 22/Mar/16 ]

This sounds like the cause of MC-93454

Comment by Jeuv [ 10/Apr/16 ]

Reproduced in 1.9.2. This is honestly one of the most absurd bugs I have seen so far.

Comment by Danny N [ 14/Jun/16 ]

This is a odd bug, well found (and isolated from the sea of other Minecraft weirdness)

Generated at Wed Oct 17 09:16:51 CDT 2018 using Jira 7.11.2#711002-sha1:fdc329dee91471a641faabfe39b5ff8c0a5b3f66.