Jump to content

Understanding IGrowable


ianc1215

Recommended Posts

I was wondering if someone knows what the real names for the method in IGrowable are.

 

public interface IGrowable
{
    boolean func_149851_a(World p_149851_1_, int p_149851_2_, int p_149851_3_, int p_149851_4_, boolean p_149851_5_);

    boolean func_149852_a(World p_149852_1_, Random p_149852_2_, int p_149852_3_, int p_149852_4_, int p_149852_5_);

    void func_149853_b(World p_149853_1_, Random p_149853_2_, int p_149853_3_, int p_149853_4_, int p_149853_5_);
}

 

I am not really sure how to implement them since the stuff is hard to read in its "native" form. I am not sure what these methods are supposed to do and I don't using code I dont understand because it makes debugging impossible. If anyone could shed some light on this... thanks.

“Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program.” - Linus Torvalds

Link to comment
Share on other sites

Based on my own quick guess at reverse engineering (read it yourself to see if my explanations seem to be correct)

 

func_149851_a is basically a stillGrowing() method.  It returns (or should return) true if the growth stage is less than the max growth stage.

 

func_149852_a is basically a canBoneMealSpeedUpGrowth() method.  I usually just return true, but depends on your crop.

 

func_149853_b is basically an incrementGrowthStage() method.  In vanilla crops the growth stage is stored in metadata so then in this method you would increment it if it wasn't already at maximum and store back in metadata.

 

If you want to see an example of one of my blocks that implements IGrowable, see here: https://github.com/jabelar/RecipesPlus-1.7.10/blob/master/src/main/java/com/blogspot/jabelarminecraft/recipesplus/blocks/RecipeBlockCrops.java

 

 

 

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

The first one is canBonemeal. If returning true then the second gets called. It's shouldBonemeal. This is where you should put your check if you want it to actually grow (like I believe saplings use return random.nextFloat()< 0.6;). If that returns true then the void method gets called. I call it doGrow. Here is where you ACTUALLY grow your plant. Saplings calls it's markForGrowthAndGrow method from there.

 

Now I'm not sure what the Boolean in the first method is, but from my testing it seems to be true on the client and false on the server. Not sure what use it has (since you can straight up do world.isRemote)

 

 

BEFORE ASKING FOR HELP READ THE EAQ!

 

I'll help if I can. Apologies if I do something obviously stupid. :D

 

If you don't know basic Java yet, go and follow these tutorials.

Link to comment
Share on other sites

The first one is canBonemeal. If returning true then the second gets called.

 

Are you sure it isn't the other way around?  In most IGrowable implementing classes in Minecraft the second function doesn't do any real work but just seems to either return true or return some other fairly simple boolean expression. 

 

Actually, looking at it closer, I think they can be used either way.  Both methods are only used in the applyBoneMeal() method and they are part of a nested if-statement that makes them interchangeable -- basically both have to be true (and must be on server side) to apply bonemeal.

 

Here is the code from the applyBoneMeal() method:

        if (block instanceof IGrowable)
        {
            IGrowable igrowable = (IGrowable)block;

            if (igrowable.func_149851_a(p_150919_1_, p_150919_2_, p_150919_3_, p_150919_4_, p_150919_1_.isRemote))
            {
                if (!p_150919_1_.isRemote)
                {
                    if (igrowable.func_149852_a(p_150919_1_, p_150919_1_.rand, p_150919_2_, p_150919_3_, p_150919_4_))
                    {
                        igrowable.func_149853_b(p_150919_1_, p_150919_1_.rand, p_150919_2_, p_150919_3_, p_150919_4_);
                    }

                    --p_150919_0_.stackSize;
                }

                return true;
            }
        }

 

So it seems to me, unless my brain is fried from being up late, that you can use them interchangeably.  Certainly looking at various Minecraft classes that implement the interface, it seems to be inconsistent. 

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

Actually, looking at it closer, I think they can be used either way.  Both methods are only used in the applyBoneMeal() method and they are part of a nested if-statement that makes them interchangeable -- basically both have to be true (and must be on server side) to apply bonemeal.

 

Here is the code from the applyBoneMeal() method:

        if (block instanceof IGrowable)
        {
            IGrowable igrowable = (IGrowable)block;

            if (igrowable.func_149851_a(p_150919_1_, p_150919_2_, p_150919_3_, p_150919_4_, p_150919_1_.isRemote))
            {
                if (!p_150919_1_.isRemote)
                {
                    if (igrowable.func_149852_a(p_150919_1_, p_150919_1_.rand, p_150919_2_, p_150919_3_, p_150919_4_))
                    {
                        igrowable.func_149853_b(p_150919_1_, p_150919_1_.rand, p_150919_2_, p_150919_3_, p_150919_4_);
                    }

                    --p_150919_0_.stackSize;
                }

                return true;
            }
        }

 

So it seems to me, unless my brain is fried from being up late, that you can use them interchangeably.  Certainly looking at various Minecraft classes that implement the interface, it seems to be inconsistent. 

 

Looking at the code you provided I would agree with shieldbug and assume func_149851_a is canBonemeal() which returns true if you can use bonemeal and func_149852_a is doBonemeal() and returns true at the same time the bonemeal is used since underneath it the stacksize is unconditionally decremented, and I assume the itemstack is a valid stack of bonemeal. Plus the second and third functions are only done server-side (I think? isRemote confuses me at times) and it would make sense to only do growth logic server-side but check if something can grow on both sides. This is all just based off the snippet you posted. I haven't actually looked into IGrowable lately.

 

EDIT: After actually looking at IGrowable and it's implementations; If I had to guess, these would be my guesses:

 

func_149851_a = canBonemeal(worldProvider, posX, posY, posZ, defaultValue): returns true if bonemeal is allowed or defaultValue (true on client, false on server).

func_149852_a = doBonemeal(worldProvider, randomProvider, posX, posY, posZ): returns true at the same time bonemeal is used if conditions for a growth-tick are acceptable.

func_149853_b = doGrowthTick(worldProvider, randomProvider, posX, posY, posZ): processes the actual growth-tick logic, which is usually increasing metadata or replacing the block.

Link to comment
Share on other sites

I think for first one we're saying the same thing, what I called "stillGrowing" is logically same as "canBoneMeal" for those classes that allow bone meal (since bone meal can only be effective on something not fully grown), but I agree canBoneMeal is better name generally (to cover cases where bone meal is never supposed to work).

 

The second class still seems a bit inconsistent in the vanilla crops, or at least it is weird that it returns a boolean at all.  Like BlockCrops itself only does a return true in what you're saying is the doBoneMeal().  Not sure why that function would be tested at all, rather than just performed void. 

 

But yeah, no objection to shieldbug's interpretation, does seem a bit more sound.

 

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

The second class still seems a bit inconsistent in the vanilla crops, or at least it is weird that it returns a boolean at all.  Like BlockCrops itself only does a return true in what you're saying is the doBoneMeal().  Not sure why that function would be tested at all, rather than just performed void. 

Because well coded java means you will always preform all 3 functions in any classes implementing the methods. Here is a more in-depth look at the functions and why they differ in each class.

 

BlockCrops checks the block metadata in canBonemeal() and returns false if the crop is fully grown because the 2nd method is called at the same time the bonemeal is used. This makes it impossible to reach the 2nd method and waste bonemeal on a fully grown crop. BlockCrops always returns true in doBonemeal() because every time a bonemeal is used there will always be growth. BlockCrops then calls another function inside of doGrowthTick() which is essentially growRandomAmount() and changes the block's metadata to a random metadata that is between 2 to 5 higher than the current and lower than or equal to the max.

 

BlockSapling on the other hand will always return true in canBonemeal() because there is never a time you can't use bonemeal on a sapling. BlockSapling's doBonemeal() returns true only if rand.nextFloat() < 0.45D. This means bonemeal is always consumed but growth is only occurring randomly at a 45% chance. Finally, in doGrowthTick() BlockSapling increases it's metadata by 1 until it reaches 8 growth ticks (or a metatdata bitwise-similar to 8 ticks) and then uses the metadata to grow a tree depending on the sapling type.

 

The reason most blocks return true in canBonemeal() is because they will always allow bonemeal to be used. Once growth has completed the block is generally replaced with another block type which either doesn't implement IGrowable or disallows the use of bonemeal. BlockCrops is different in this sense because even fully grown it is still an instance of BlockCrops and is never replaced, so it uses this method to check if it is fully grown and if it is it then returns false.

 

The reason most blocks return true in doBonemeal() is because growth-ticks are generally 1:1 with bonemeal usage. Each time a bonemeal is used you will always get some sort of growth. BlockSapling and BlockMushroom are the only 2 vanilla classes that ever return false here because they only have a random chance (45%) for growth to occur, but still bonemeal is always consumed so the 1st method returns true.

 

Sorry about the long-winded post. I tried to format and color code it to be more readable. Pretty sure I failed. But, I hope that clears up a bit of confusion though. :)

Link to comment
Share on other sites

Sorry about the long-winded post. I tried to format and color code it to be more readable. Pretty sure I failed. But, I hope that clears up a bit of confusion though. :)

 

That actually was perfect, it makes every sense.

So basically, one should return a boolean in the first method to whether the block can use bonemeal but doesn't consume it. However, if he wants it to consume bonemeal anyway but grow the block by chance he should return true or false in the second method.

 

It makes a lot of sense now, I'm sure Mojang did this code in 1.5 in the huge bonemeal nerf after they wanted their saplings to grow by chance because it was too OP when it grew fully every time.

Link to comment
Share on other sites

That actually was perfect, it makes every sense.

So basically, one should return a boolean in the first method to whether the block can use bonemeal but doesn't consume it. However, if he wants it to consume bonemeal anyway but grow the block by chance he should return true or false in the second method.

Close. You're right on the 2nd bit, but the first method will always return true so long as you're allowed to use bonemeal on the block. There should never a time when you use bonemeal without consuming it. If you were using your own custom item instead of bonemeal, you would ignore the first method completely (and possibly the 2nd method if you always wanted a 100% growth chance).

Link to comment
Share on other sites

Great analysis.

 

I still think you could achieve the same thing without the second method at all.  Basically you would have a canBoneMeal() that would return false either if crop doesn't ever use it or if crop is finished (fully grown) growing.  Then you call an implementGrowth() method that could either progress randomly or not.  I'm probably still missing something but I don't see why you really two seperate methods to cover the "can" part -- the logic is pretty simple to combine into single method.  These methods aren't called anywhere except the applyBoneMeal() method and they are contained within the crop class itself so no need to generalize -- so why not just test conditions and if you want grow steadily or randomly proceed to do so?

 

I'm all for breaking code into small, purposeful methods, but I think it can get silly once you get into methods that only require a one-line return statement.

 

Anyway, it is what is it -- that's modding!  Good stuff.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

The reasoning for two separate methods is clear as day right there in your code snippet from ItemDye's applyBonemeal(). The methods are split because of the split between logic on the server and client side. The 2nd and 3rd methods are called only on the server side while the 1st is called on both sides.

 

I'm guessing the boolean in the first method was used back when the client and server were separated and is now leftover because of that. All the logic might have originally been in a single method (or two: canBonemeal & doGrowth), but because of the (semi-)recent changes to the client/server and how bonemeal works the methods were split for a cleaner, more side-dependent code.

 

Just guesses, since this is how I would do it and I wager Mojang knows quite a bit more about java coding than I do.

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.