Uploaded image for project: 'Minecraft: Java Edition'
  1. Minecraft: Java Edition
  2. MC-99914

DataWatcher / EntityMetadata ID is based on the class load order ! Can cause many issues !

    Details

    • Type: Bug
    • Status: Resolved
    • Resolution: Fixed
    • 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
    • Labels:
      None
    • Environment:

      OS: Windows 10 and Debian Jessie
      Java: 1.8.0_73

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

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                grum [Mojang] Grum (Erik Broes)
                Reporter:
                nathan818 Nathan Poirier
              • Votes:
                6 Vote for this issue
                Watchers:
                8 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved:
                  CHK: