mojira.dev
MC-112253

Primitive NBT Lists that are not of type int need a type suffix

The bug

Primitive NBT lists that are not of type int need a type suffix. If the list is of type double the values must either have a suffix or be a floating point number.

For instance the command

/summon zombie ~ ~ ~ {Motion:[1d,1d,1d]}

will summon a zombie with motion, but

/summon zombie ~ ~ ~ {Motion:[1,1,1]}

will not.

Possible fix

I was looking at the decompiled source for 1.10.2 using forge and this seems to be caused by the primitive get methods in net.minecraft.nbt.NBTTagList. For instance the Method getDoubleAt(int) checks for an nbt id of 6 in line 223, but if the list is of type int ([1,1,1]) the method returns 0. If the method would work similar to net.minecraft.nbt.NBTTagCompound.getDouble(String) and make use of the class net.minecraft.nbt.NBTPrimitive instead of the concrete subclass net.minecraft.nbt.NBTTagDouble, then it would return the correct value:

Current (1.10.2) implementation of net.minecraft.nbt.NBTTagList.getDoubleAt(int):217

public double getDoubleAt(int i)
    {
        if (i >= 0 && i < this.tagList.size())
        {
            NBTBase nbtbase = (NBTBase)this.tagList.get(i);

            if (nbtbase.getId() == 6)
            {
                return ((NBTTagDouble)nbtbase).getDouble();
            }
        }

        return 0.0D;
    }

Fixed implementation of net.minecraft.nbt.NBTTagList.getDoubleAt(int)

public double getDoubleAt(int i)
    {
        if (i >= 0 && i < this.tagList.size())
        {
            NBTBase nbtbase = (NBTBase)this.tagList.get(i);

            if (nbtbase.getId() >= 1 && nbtbase.getId() <= 6)
            {
                return ((NBTPrimitive)nbtbase).getDouble();
            }
        }

        return 0.0D;
    }

For this bug to be fixed you would also need to change the Method net.minecraft.nbt.NBTTagCompound.getTagList(String, int). This method is for instance called here:

net.minecraft.entity.Entity.readFromNBT(NBTTagCompound):1838

NBTTagList nbttaglist2 = compound.getTagList("Motion", 6);
            NBTTagList nbttaglist3 = compound.getTagList("Rotation", 5);
            this.motionX = nbttaglist2.getDoubleAt(0);
            this.motionY = nbttaglist2.getDoubleAt(1);
            this.motionZ = nbttaglist2.getDoubleAt(2);

Instead of calling getTagList with type 6, the method should support the type 99, similar to net.minecraft.nbt.NBTTagCompound.getDouble(String) wich calles hasKey(key, 99).

net.minecraft.nbt.NBTTagCompound.getDouble(String):324

/**
     * Retrieves a double value using the specified key, or 0 if no such key was stored.
     */
    public double getDouble(String key)
    {
        try
        {
            if (this.hasKey(key, 99))
            {
                return ((NBTPrimitive)this.tagMap.get(key)).getDouble();
            }
        }
        catch (ClassCastException var3)
        {
            ;
        }

        return 0.0D;
    }

net.minecraft.nbt.NBTTagCompound.hasKey(String, int):215

/**
     * Returns whether the given string has been previously stored as a key in this tag compound as a particular type,
     * denoted by a parameter in the form of an ordinal. If the provided ordinal is 99, this method will match tag types
     * representing numbers.
     */
    public boolean hasKey(String key, int type)
    {
        int i = this.getTagId(key);
        return i == type ? true : (type != 99 ? false : i == 1 || i == 2 || i == 3 || i == 4 || i == 5 || i == 6);
    }

The same fix would work for the other primitive types: byte, float, int, long, short.

Linked issues

Comments 2

Can confirm in 21w03a.

Can confirm in 21w06a.

Adrodoc55

(Unassigned)

Confirmed

(Unassigned)

Minecraft 1.11.2, Minecraft 1.12.2, Minecraft 17w50a, Minecraft 18w06a, Minecraft 1.13-pre5, ..., 1.16.3, 21w03a, 21w06a, 1.20.6, 24w19b

Retrieved