Making other blocks spread like fire and mushrooms

Discussion in 'Plugin Development' started by Father Of Time, Aug 13, 2013.

Thread Status:
Not open for further replies.
  1. Offline

    Father Of Time

    Good evening everyone,

    I was wondering if anyone knew what controls block spreading in craftbukkit? There is a block spread event, meaning somewhere there is code that handled block spreading that creates and triggers this event.

    I ask because I want to make glowstone spread under certain conditions, just like fire and mushrooms using reflections. I think that if I can just find the code that controls this that I may have a chance of figuring out how to make additional materials spread.

    Thank you in advance for any advice you may have on this subject.
     
  2. Offline

    xTrollxDudex

    Father Of Time
    BlockSpreadEvent :p

    Try calling it with the plugin manager, let me check the constructor
     
  3. Offline

    Kazzababe

  4. Offline

    Father Of Time

    Haha, very nice; funny enough I found it too but in a difference class; in BlockGrass.java. So I made this class for now to experiment, but the question is still, "Using reflections how do I get craftbukkit to use my GlowStoneBlock class just as it does the GrassBlock class"?

    Code:
    import java.util.Random;
     
    import org.bukkit.block.BlockState;
    import org.bukkit.event.block.BlockSpreadEvent;
     
    // CraftBukkit start
    import net.minecraft.server.v1_6_R2.Block;
    import net.minecraft.server.v1_6_R2.CreativeModeTab;
    import net.minecraft.server.v1_6_R2.World;
    // CraftBukkit end
     
    public class GlowStoneBlock extends Block {
     
        protected GlowStoneBlock(int i) {
            super( i, Block.GLOWSTONE.material );
            this.b( true );
            this.a( CreativeModeTab.b );
           
        }
     
        public void a( World world, int i, int j, int k, Random random ){
            if (!world.isStatic) {
                if (world.getLightLevel(i, j + 1, k) >= 9 ) {
                    for (int l = 0; l < 4; ++l) {
                        int i1 = i + random.nextInt(3) - 1;
                        int j1 = j + random.nextInt(5) - 3;
                        int k1 = k + random.nextInt(3) - 1;
                        int l1 = world.getTypeId(i1, j1 + 1, k1);
     
                        if ( world.getTypeId(i1, j1, k1) == Block.WATER.id && world.getLightLevel(i1, j1 + 1, k1) >= 4 && Block.lightBlock[l1] <= 2) {
                            // CraftBukkit start
                            org.bukkit.World bworld = world.getWorld();
                            BlockState blockState = bworld.getBlockAt(i1, j1, k1).getState();
                            blockState.setTypeId(Block.GLOWSTONE.id);
     
                            BlockSpreadEvent event = new BlockSpreadEvent(blockState.getBlock(), bworld.getBlockAt(i, j, k), blockState);
                            world.getServer().getPluginManager().callEvent(event);
     
                            if (!event.isCancelled()) {
                                blockState.update(true);
                            }
                        }
                    }
                }
            }
        }
     
        public int getDropType(int i, Random random, int j) {
            return Block.GLOWSTONE.getDropType(0, random, j);
        }
    }
    Any ideas?
     
  5. Offline

    Kazzababe


    I'm not sure if that's actually possible. I was just suggesting you view through the code and see the code that handles block spreading and then replicate it using your own code in Bukkit.
     
  6. Offline

    xTrollxDudex

  7. Offline

    mollekake

    Why don't you just use a scheduler? make it repeat and check every so often, like every 10 seconds or so, and do a delayed task a random amount of time later.
     
  8. Offline

    Kazzababe

  9. Offline

    Father Of Time

    Okay, so I've made a little bit more progress... I found that in Block.java there are a bunch of static final block variables where block are originally declared, inside that class is the following variable:

    Code:
        public static final Block GLOWSTONE = (new BlockLightStone(89, Material.SHATTERABLE)).c(0.3F).a(m).a(1.0F).c("lightgem").d("glowstone");
    You'll notice that glowstone is actually an instance of the BlockLightStone class. After searching a little deeper I found the specified class:

    Code:
    package net.minecraft.server;
     
    import java.util.Random;
     
    public class BlockLightStone extends Block {
     
        public BlockLightStone(int i, Material material) {
            super(i, material);
            this.a(CreativeModeTab.b);
        }
     
        public int getDropCount(int i, Random random) {
            return MathHelper.a(this.a(random) + random.nextInt(i + 1), 1, 4);
        }
     
        public int a(Random random) {
            return 2 + random.nextInt(3);
        }
     
        public int getDropType(int i, Random random, int j) {
            return Item.GLOWSTONE_DUST.id;
        }
    }
    Now my question is does anyone know what the obfuscated function "a" handles in this class? I ask because in an almost identical class (BlockGrass.java) that has the behavior I want (block spreading) the "a" method is what controls block spreading, as can be seen below:

    Code:
    package net.minecraft.server;
     
    import java.util.Random;
     
    // CraftBukkit start
    import org.bukkit.block.BlockState;
    import org.bukkit.event.block.BlockSpreadEvent;
    import org.bukkit.event.block.BlockFadeEvent;
    // CraftBukkit end
     
    public class BlockGrass extends Block {
     
        protected BlockGrass(int i) {
            super(i, Material.GRASS);
            this.b(true);
            this.a(CreativeModeTab.b);
        }
     
        public void a(World world, int i, int j, int k, Random random) {
            if (!world.isStatic) {
                if (world.getLightLevel(i, j + 1, k) < 4 && Block.lightBlock[world.getTypeId(i, j + 1, k)] > 2) {
                    // CraftBukkit start
                    org.bukkit.World bworld = world.getWorld();
                    BlockState blockState = bworld.getBlockAt(i, j, k).getState();
                    blockState.setTypeId(Block.DIRT.id);
     
                    BlockFadeEvent event = new BlockFadeEvent(blockState.getBlock(), blockState);
                    world.getServer().getPluginManager().callEvent(event);
     
                    if (!event.isCancelled()) {
                        blockState.update(true);
                    }
                    // CraftBukkit end
                } else if (world.getLightLevel(i, j + 1, k) >= 9) {
                    for (int l = 0; l < 4; ++l) {
                        int i1 = i + random.nextInt(3) - 1;
                        int j1 = j + random.nextInt(5) - 3;
                        int k1 = k + random.nextInt(3) - 1;
                        int l1 = world.getTypeId(i1, j1 + 1, k1);
     
                        if (world.getTypeId(i1, j1, k1) == Block.DIRT.id && world.getLightLevel(i1, j1 + 1, k1) >= 4 && Block.lightBlock[l1] <= 2) {
                            // CraftBukkit start
                            org.bukkit.World bworld = world.getWorld();
                            BlockState blockState = bworld.getBlockAt(i1, j1, k1).getState();
                            blockState.setTypeId(Block.GRASS.id);
     
                            BlockSpreadEvent event = new BlockSpreadEvent(blockState.getBlock(), bworld.getBlockAt(i, j, k), blockState);
                            world.getServer().getPluginManager().callEvent(event);
     
                            if (!event.isCancelled()) {
                                blockState.update(true);
                            }
                            // CraftBukkit end
                        }
                    }
                }
            }
        }
     
        public int getDropType(int i, Random random, int j) {
            return Block.DIRT.getDropType(0, random, j);
        }
    }
    Now even though the "a" method exist in both classes, I don't believe they are the same method because they accept different arguments:

    BlockGrass.java
    Code:
    public void a(World world, int i, int j, int k, Random random) {
    BlockLightStone.java
    Code:
    public int a(Random random) {
    If the "a" method of BlockLightStone is utilized for block spreading then I can simply alter it's functionality using reflections, but sadly if they aren't the same method then I am boned because you can't insert a new method into a class using reflections. :(

    Any input on this subject would be appreciated, thank you for your time and attention to this matter.
     
  10. Offline

    herpingdo

    The `a()` method in BlockLightStone.class controls how many glowstone dust are dropped when you break the block :p
     
  11. Offline

    flaaghara

    Father Of Time To me that a() method seems like it controls how many glowstone dust are dropped. If you numerically analyze the method, it returns a value between 2 and 4, which coincides with the Minecraft Wiki on how many glowstone dust are dropped. The method getDropCount() calls that method too so I'm pretty sure it has nothing to do with a() from BlockGrass.java. Just looks like a case of obfuscation gone wild :p If you want to implement your own block spreading consider the a() method in BlockGrass.java.
     
  12. Offline

    taytaydavis88

    You guys are unbelievably good at this ._.
     
  13. Offline

    xTrollxDudex

  14. Offline

    Kazzababe


    I don't understand the question. The BlockSpreadEvent is called right before the actual block spreading occurs. The event just allows players to gather some information about the event before it happens and then cancel it if they see the need to do so.
     
  15. Offline

    flaaghara

    xTrollxDudex This is just a guess here as I have no idea what explicitly calls that method which initiates the spread (something obfuscated in NMS), but it's probably some ChunkUpdater class that calls a() every couple of ticks.
     
  16. Offline

    mncat77 Retired Staff

    I am pretty sure you would just need to override this method in your BlockLightStone class (that you reflect into the static NMS fields) like the other spreading blocks do. You can add all the checks you want there and you also already have examples for this. If you don't know how to reflect it into NMS take a look at this (also you probably shouldn't be messing with that stuff, if you don't know how to).
     

    Attached Files:

  17. Offline

    ZeusAllMighty11 Retired Staff

    mollekake
    using a scheduler is inefficient for what the OP is trying to do, and can be a hassle when there are hundreds of 'clumps'. Using a scheduler would also mean managing enabling/disabling the scheduler accordingly.


    http://www.minecraftwiki.net/wiki/Tutorials/Mushroom_farming#Spread_Mechanics


    I'm going to go check how the minecraft client handles it, then I will edit my post and let you know


    Edit: Here's the client's BlockMushroom class
    Code:
    public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random)
    {
             if (par5Random.nextInt(25) == 0)
             {
                     byte byte0 = 4;
                     int i = 5;
                     for (int j = par2 - byte0; j <= par2 + byte0; j++)
                     {
                             for (int l = par4 - byte0; l <= par4 + byte0; l++)
                             {
                                     for (int j1 = par3 - 1; j1 <= par3 + 1; j1++)
                                     {
                                             if (par1World.getBlockId(j, j1, l) == blockID && --i <= 0)
                                             {
                                                     return;
                                             }
                                     }
                             }
                     }
                     int k = (par2 + par5Random.nextInt(3)) - 1;
                     int i1 = (par3 + par5Random.nextInt(2)) - par5Random.nextInt(2);
                     int k1 = (par4 + par5Random.nextInt(3)) - 1;
                     for (int l1 = 0; l1 < 4; l1++)
                     {
                             if (par1World.isAirBlock(k, i1, k1) && canBlockStay(par1World, k, i1, k1))
                             {
                                     par2 = k;
                                     par3 = i1;
                                     par4 = k1;
                             }
                             k = (par2 + par5Random.nextInt(3)) - 1;
                             i1 = (par3 + par5Random.nextInt(2)) - par5Random.nextInt(2);
                             k1 = (par4 + par5Random.nextInt(3)) - 1;
                     }
                     if (par1World.isAirBlock(k, i1, k1) && canBlockStay(par1World, k, i1, k1))
                     {
                             par1World.setBlockWithNotify(k, i1, k1, blockID);
                     }
             }
    }
    
    Every tick, it's checking for a random integer between 0 and 25. So it has a 1/26 chance to spread (only if the number is 0)


    As for the original question, I guess overriding this method would allow it to do so?
     
    xTrollxDudex likes this.
  18. Offline

    xTrollxDudex

    flaaghara
    The a() method calls BlockSpread, am I correct?
     
  19. Offline

    flaaghara

    xTrollxDudex Yeah it calls that event.

    TheGreenGamerHD 1/26 seemed a bit odd. Looked it up; just a reminder the integer passed to nextInt() is exclusive so it would return 0-24 :p

    Father Of Time I remember reading someone's post a while back about using reflection for CraftBukkit/NMS. From what I got out of that thread, I don't think that's allowed because the Bukkit team wants you to update your code every MC update. If that method name changed (which it commonly does when re-obfuscated to a new version) but the signature didn't, something catastrophic could happen to someone's world. Not entirely sure if that's on point but just letting you know.
     
  20. Offline

    xTrollxDudex

    flaaghara
    So would Kazzababe be correct saying that calling BlockSpread wouldn't work
     
  21. Offline

    flaaghara

    xTrollxDudex From what I get from that BlockMushroom class, BlockSpreadEvent is called after a BlockState object is referenced. Once the logic of probability and entity checks are completed, it fires the event, and as long as the event isn't cancelled, the block is updated to that state. It depends on what OP wants to do. If he truly just wants to just
    then using BlockSpreadEvent is best in this case because you can check light level and other stuff I guess. If he wants to really change the mechanics of block spreading (which could be dangerous if not coded carefully) they he might want to re-code that method.
     
  22. Offline

    Kazzababe

    Again, just calling the event will not actually do anything.
    Proof: https://github.com/Bukkit/Bukkit/bl.../org/bukkit/event/block/BlockSpreadEvent.java
     
  23. Offline

    flaaghara

    Kazzababe Explicitly calling it won't but listening for it certainly will.
     
  24. Offline

    Kazzababe


    No, listening for it will not make a block spread. He wants glowstone to spread and glowstone by default does not spread so listening for a BlockSpreadEvent wouldn't help you at all.
     
  25. Offline

    flaaghara

    Kazzababe Listening for any event will not cause it to happen, but you can modify it. In the case of glowstone spreading because it naturally doesn't you are right in saying that the event won't help. But for other spreading blocks like grass it would. To the OP, I say extend Block and polymorph that method and copy BlockGrass's implementation.
     
  26. Offline

    xTrollxDudex

    Kazzababe
    As if you can't just randomize some block location near the glow stone and setType() to glowstone?
     
  27. Offline

    Kazzababe


    How would that be a good thing though? You can't be very specific there, and on top of that, you would have to have another block there that does naturally spread around.
     
    xTrollxDudex likes this.
Thread Status:
Not open for further replies.

Share This Page