3

I'm trying to apply a Dye color into an existing ItemStack, how can I do this without using deprecated methods and creating an new stack?

I tried the following code but it's resulting in a normal ink sack.

ps: I'm creating an stack in the first line only as example.

final ItemStack stack = new ItemStack(Material.INK_SACK);
Dye dye = new Dye();
dye.setColor(DyeColor.LIME);
stack.setData(dye);

edit: Added final to stack variable to show that it cannot be replaced by a new stack.

Polyana Fontes
  • 3,156
  • 1
  • 27
  • 41
  • If you really want to use non-deprecated code, I updated my answer with a custom `enum` you could create, although I still don't think its necessary until bukkit implements a new, non-deprecated way to do this, but, if you really want to, you can use the custom `enum`. – Jojodmo Jul 20 '14 at 17:53

4 Answers4

7

I've found a way to do this, but it is nowhere near as efficient as using a deprecated method. Here it is with my own personal thought process along the way.

The problem with your current attempt is the interior of the setData(MaterialData) method.

public void setData(MaterialData data) {
    Material mat = getType();

    if (data == null || mat == null || mat.getData() == null) {
        this.data = data;
    } else {
        if ((data.getClass() == mat.getData()) || (data.getClass() == MaterialData.class)) {
            this.data = data;
        } else {
            throw new IllegalArgumentException("Provided data is not of type " + mat.getData().getName() + ", found " + data.getClass().getName());
        }
    }
}

This method works as a simple setter that handles null values and prevents incorrect data types. The problem here should be quite visible though. Wools and dyes don't store their info in MaterialData, they store it in their durability. The durability of a Colorable object determines its color. Being that this is the case, you will need to modify the durability in order to do anything to the color. The only place you can set the durability outside of the constructor is the setDurability method. This doesn't take a MaterialData, only a short. Thus our options are the following: Construct a new ItemStack, or gain non-deprecated access to a usable short value. By your criteria, we move to the latter.

First point of entry, the DyeColor class. If we can't find the short value here, it will at least be supplied from here to another class. Looking at the source gives us a grim reminder of just how rooted in deprecation the problem is.

There is one hacky solution using DyeColor alone, but it's not version proof incase of changes. The Enum.ordinal() method will return the ordinal value of an enum. This is the numerical value of the order it was defined. Starting from index zero, it will match the wool data values, but needs to be inverted for dyes, something you are trying to make universal. The option is there though.

So DyeColor is out of the question, but it got me thinking. The Dye class as a MaterialData works perfectly fine when using the toItemStack() method. Maybe we could use that. Turns out you can! It will require creating a new Itemstack, but we will use the new ItemStack to access data for the original one. Firstly, we create the MaterialData like you did above.

Dye dye = new Dye();
dye.setColor(DyeColor.LIME);

Next, convert the Dye into an Itemstack using the toItemStack() method.

ItemStack tempStack = dye.toItemStack();

Now hold on, why does this work? Well you see, the Dye.setColor(DyeColor) method has an internal implementation of the following:

public void setColor(DyeColor color) {
    setData(color.getDyeData());
}

It uses a deprecated method, but because it's wrapped in a DyeColor call, it's not deprecated to the plugin user. Please note: THIS IS PERFECTLY ACCEPTABLE! Many of Bukkit's calls using Material as a parameter actually just call the deprecated method with the type id associated to it. It's perfectly valid to call those methods though. Also note that the setData(byte) method is just a simple setter.

Next up, the toItemStack() method.

public ItemStack toItemStack(int amount) {
    return new ItemStack(type, amount, data);
}

We can see here that the DyeColor that was converted to a byte is now sent to the ItemStack constructor ItemStack(Material, int, short) as the durability (it automatically casts to a short). This means, we now have the short we require! It's stored in the durability of tempStack. To top it all off, we do the following.

stack.setDurability(tempStack.getDurability());
stack.setData(dye);

It's over. You have a modified ItemStack with no exposed deprecated methods. You may be asking though why I still call ItemStack.setData(MaterialData). This just ensures that if someone tries to access the DyeColor from the ItemStack's MaterialData it will match up with the durability.

I hope you are satisfied with the fact that it is possible, but I still just recommend using the deprecated methods until they are listed as broken (which usually doesn't happen with Bukkit).

CrypticStorm
  • 560
  • 2
  • 11
1

DyeColor.* actually uses wool colors, and Mojang decided to switch dye colors and wool colors around. So, DyeColor.LIME.getData() returns 5, which is lime wool, but lime dye is 10. So you need to use byte data = (byte) (15 - DyeColor.COLOR.getData());

So, here's what your code should look like:

DyeColor color = DyeColor.LIME //or whatever dye color you want
byte data = (byte) (15 - color.getData()); //use byte data = (byte) (color.getData()) if you're coloring wool

ItemStack itm = new ItemStack(Material.INK_SACK, 1, data);

This would give you an ItemStack with 1 lime dye.

If you would really like to use a non-deprecated method, you could make your own enum with their ID's:

public enum CustomDyeColor{

    BLACK(0), //ink sack
    RED(1),
    GREEN(2),
    BROWN(3),
    BLUE(4), //lapis lazuli
    PURPLE(5),
    CYAN(6),
    LIGHT_GRAY(7),
    GRAY(8),
    PINK(9),
    LIME(10),
    YELLOW(11),
    LIGHT_BLUE(12),
    MAGENTA(13),
    ORANGE(14),
    WHITE(15); //bonemeal

    private byte data;
    CustomDyeColor(int i){
      this.data = (byte) i;
    }

    public byte getData(){
      return this.data;
    }
}

And then you could use your new, non-depricated, custom code to get the data values like this:

CustomDyeColor color = CustomDyeColor.LIME;
byte customData = color.getData();

ItemStack item = new ItemStack(Material.INK_SACK, 1, customData)l
Jojodmo
  • 23,357
  • 13
  • 65
  • 107
  • But [getData() is deprecated](http://jd.bukkit.org/rb/apidocs/org/bukkit/DyeColor.html#getData()) . I could also use [getDyeData()](http://jd.bukkit.org/rb/apidocs/org/bukkit/DyeColor.html#getDyeData()) that uses dye colors but it's also deprecated :/ – Polyana Fontes Jul 14 '14 at 02:39
  • 1
    @JoséRobertoAraújoJúnior Just because it's deprecated doesn't mean it doesn't work... There is still no clear way on how to do this without using deprecated methods – Jojodmo Jul 14 '14 at 02:41
1

You could try using the Dye(Material) constructor, then appying your DyeColor, and converting the Material to an ItemStack.

Dye dye = new Dye(Material.INK_SACK);
dye.setColor(...);
ItemStack coloredInkSack = dye.toItemStack();
  • `toItemStack()` creates a new stack instance, I'm trying to keep reference to the original object – Polyana Fontes Jul 17 '14 at 17:44
  • There is no "original object" before you call toItemStack. It is useless to have an original object anyways, since Dye and MaterialData are like represented ItemStack builders, saving creation for the end. –  Jul 18 '14 at 07:30
0

I did it like so:

new ItemStack(Material.STAINED_GLASS_PANE, 1, DyeColor.BLACK.getData()));

With the 1.8.8 Spigot it actually creates 1 black glass pane, although it says the getData() on the color is deprecated. I think it also works with wool and clay.

David
  • 11,245
  • 3
  • 41
  • 46
writzlpfrimpft
  • 333
  • 3
  • 14