Difference between revisions of "Events/BlockEvent"

From Forge Community Wiki
(add client checks)
 
(8 intermediate revisions by the same user not shown)
Line 14: Line 14:
 
public void onBlockBreak(final BlockEvent.BreakEvent event)
 
public void onBlockBreak(final BlockEvent.BreakEvent event)
 
     {
 
     {
         if (!world.isClientSide() && event.getState().is(Tags.Blocks.SAND)) // use the base event to get the state and see what it is
+
        IWorld world = event.getWorld();
 +
         if (!world.isClientSide() && event.getState().is(Blocks.SAND)) // use the base event to get the state and see what it is
 
         {
 
         {
 
             event.setExpToDrop(10); // change the amount of experience we'll be dropping
 
             event.setExpToDrop(10); // change the amount of experience we'll be dropping
 
             PlayerEntity player = event.getPlayer();
 
             PlayerEntity player = event.getPlayer();
             if (player.getUseItem().getItem().is(Tags.Items.SHEARS)) // check if the player is holding shears
+
             if (player.getMainHandItem().getItem().equals(Items.DIAMOND_SHOVEL)) // check what we're holding
 
             {
 
             {
                 ItemHandlerHelper.giveItemToPlayer(player, new ItemStack(Items.BONE)); // give a bone to the player!
+
                 ItemHandlerHelper.giveItemToPlayer(player, new ItemStack(Items.BONE)); // give a bone
 
             }
 
             }
 
         }
 
         }
Line 44: Line 45:
 
Example usage:
 
Example usage:
 
<syntaxhighlight lang="java">
 
<syntaxhighlight lang="java">
public void onEntityPlace(final BlockEvent.BreakEvent event)
+
public void onEntityPlace(final BlockEvent.EntityPlaceEvent event)
 
     {
 
     {
         Entity entity = event.getEntity();
+
         IWorld world = event.getWorld();
         if (!world.isClientSide() && entity != null) // check if the entity is null
+
         if (!world.isClientSide() && event.getEntity() instanceof EndermanEntity)
 
         {
 
         {
             Block block = event.getState().getBlock(); // get the block that was placed
+
             event.setCanceled(true); // prevent enderman placement
            if (entity instanceof PlayerEntity && block.is(Blocks.FROSTED_ICE))
 
            {
 
                event.setCanceled(true); // cancel the placing
 
                event.getWorld().setBlock(event.getPos(), Blocks.COBBLESTONE.defaultBlockState(), 3); // set cobble instead
 
            }
 
 
         }
 
         }
 
     }
 
     }
Line 79: Line 75:
 
Example usage:
 
Example usage:
 
<syntaxhighlight lang="java">
 
<syntaxhighlight lang="java">
public void onNeighborNotify(final BlockEvent.BreakEvent event)
+
public void onNeighborNotify(final BlockEvent.NeighborNotifyEvent event)
 
     {
 
     {
 +
        IWorld world = event.getWorld();
 
         if (world.isClientSide()) return;
 
         if (world.isClientSide()) return;
 
         BlockPos eventPos = event.getPos();
 
         BlockPos eventPos = event.getPos();
         for (Direction direction : event.getNotifiedSides()) // cycle through notified directions
+
         if (event.getState().is(Blocks.REDSTONE_WIRE)) // only do it for redstone
 
         {
 
         {
             BlockPos pos = eventPos.relative(direction); // move to the direction given
+
             for (Direction direction : event.getNotifiedSides()) // cycle through notified directions
             double x = pos.getX() + 0.5D; // move to center of the block
+
             {
            double y = pos.getY() + 0.5D;
+
                BlockPos pos = eventPos.relative(direction); // move to that spot
            double z = pos.getZ() + 0.5D;
+
                if (world.isEmptyBlock(pos))
            // add particle
+
                {
            event.getWorld().addParticle(ParticleTypes.CLOUD, x, y, z, 0.0D, 0.0D, 0.0D);
+
                    world.setBlock(pos, Blocks.GLOWSTONE.defaultBlockState(), 3); // place glowstone
 +
                }
 +
            }
 
         }
 
         }
 
     }
 
     }
Line 99: Line 98:
 
* Making a block update detector block
 
* Making a block update detector block
  
== CreateFluidSourceEvent ==
+
==CreateFluidSourceEvent==
 
This event controls creation of fluid sources. This is different than cancellable events, because it has a <code>Result</code>. Setting it to <code>ALLOW</code> causes a source block to created, even if that's not normally what would happen. Setting it to <code>DENY</code> always prevents source creation.
 
This event controls creation of fluid sources. This is different than cancellable events, because it has a <code>Result</code>. Setting it to <code>ALLOW</code> causes a source block to created, even if that's not normally what would happen. Setting it to <code>DENY</code> always prevents source creation.
  
 
Example usage:
 
Example usage:
 
<syntaxhighlight lang="java">
 
<syntaxhighlight lang="java">
public void onNeighborNotify(final BlockEvent.BreakEvent event)
+
public void onFluidSourceCreate(final BlockEvent.CreateFluidSourceEvent event)
 
     {
 
     {
 +
        IWorldReader world = event.getWorld();
 
         if (!world.isClientSide() && event.getPos().getY() > 100)
 
         if (!world.isClientSide() && event.getPos().getY() > 100)
 
         {
 
         {
Line 126: Line 126:
 
Example usage:
 
Example usage:
 
<syntaxhighlight lang="java">
 
<syntaxhighlight lang="java">
public void onBreak(final BlockEvent.BreakEvent event)
+
public void onFluidPlace(final BlockEvent.FluidPlaceBlockEvent event)
 
     {
 
     {
 +
        IWorld world = event.getWorld();
 
         if (world.isClientSide()) return;
 
         if (world.isClientSide()) return;
 
         BlockState newState = event.getNewState();
 
         BlockState newState = event.getNewState();
         if (newState.is(Blocks.FIRE))
+
         if (newState.is(Blocks.COBBLESTONE) && world.dayTime() > 14000L)
        {
 
            event.setCanceled(true); // prevent fire placing from lava
 
        }
 
        else if (newState.is(Blocks.COBBLESTONE) && event.getWorld().dayTime() > 14000L)
 
 
         {
 
         {
 
             event.setNewState(Blocks.ANDESITE.defaultBlockState()); // change the block if done during nighttime.
 
             event.setNewState(Blocks.ANDESITE.defaultBlockState()); // change the block if done during nighttime.
Line 157: Line 154:
 
public void onCropGrowPre(final BlockEvent.CropGrowEvent.Pre event)
 
public void onCropGrowPre(final BlockEvent.CropGrowEvent.Pre event)
 
     {
 
     {
 +
        IWorld world = event.getWorld();
 
         if (world.isClientSide()) return;
 
         if (world.isClientSide()) return;
 
         BlockState state = event.getState();
 
         BlockState state = event.getState();
Line 165: Line 163:
 
         else if (state.is(Blocks.BAMBOO))
 
         else if (state.is(Blocks.BAMBOO))
 
         {
 
         {
             if (event.getWorld().getRandom().nextFloat() > 0.2F) // take a 4/5 chance
+
             if (world.getRandom().nextFloat() > 0.2F) // take a 4/5 chance
 
             {
 
             {
 
                 event.setResult(Event.Result.DENY); // make bamboo grow less often
 
                 event.setResult(Event.Result.DENY); // make bamboo grow less often
 +
            }
 +
        }
 +
    }
 +
 +
public void onCropsGrowPost(final BlockEvent.CropGrowEvent.Post event)
 +
    {
 +
        IWorld world = event.getWorld();
 +
        if (!world.isClientSide() && event.getState().is(Blocks.WHEAT)) // check wheat
 +
        {
 +
            BlockState newState = event.getState();
 +
            if (newState.is(Blocks.WHEAT)) // defensive check to make sure we can do this
 +
            {
 +
                world.setBlock(event.getPos(), newState.setValue(CropsBlock.AGE, 7), 3); // set max growth wheat
 
             }
 
             }
 
         }
 
         }
Line 189: Line 200:
 
public void onTrample(final BlockEvent.FarmlandTrampleEvent event)
 
public void onTrample(final BlockEvent.FarmlandTrampleEvent event)
 
     {
 
     {
 +
        IWorld world = event.getWorld();
 
         if (!world.isClientSide() && event.getFallDistance() < 10.0F)
 
         if (!world.isClientSide() && event.getFallDistance() < 10.0F)
 
         {
 
         {
Line 202: Line 214:
  
 
This is mostly used for preventing portal spawning in certain dimensions (even the overworld).
 
This is mostly used for preventing portal spawning in certain dimensions (even the overworld).
 +
 +
Example usage:
 +
<syntaxhighlight lang="java">
 +
public void onPortalLight(final BlockEvent.PortalSpawnEvent event)
 +
    {
 +
        event.setCanceled(true);
 +
    }
 +
</syntaxhighlight>
  
 
== BlockToolInteractEvent ==
 
== BlockToolInteractEvent ==
Line 208: Line 228:
 
<code>setFinalState()</code>: Set what you want the result to be.
 
<code>setFinalState()</code>: Set what you want the result to be.
  
<code>getFinalState()</code>: Returns what it will be changed to.
+
<code>getFinalState()</code>: Returns what it will be changed to. This will normally just return whatever the original block is unless you or another modder has set it. It can't check, for example, if something *will* turn into farmland in the future.
  
 
Example usage:
 
Example usage:
Line 214: Line 234:
 
public void onToolInteract(final BlockEvent.BlockToolInteractEvent event)
 
public void onToolInteract(final BlockEvent.BlockToolInteractEvent event)
 
     {
 
     {
         if (!world.isClientSide() && event.getFinalState().is(Blocks.FARMLAND) && event.getHeldItemStack().getItem() == Items.NETHERITE_HOE)
+
        IWorld world = event.getWorld();
 +
         if (!world.isClientSide() && event.getHeldItemStack().getItem() == Items.NETHERITE_HOE)
 
         {
 
         {
             event.setFinalState(Blocks.NETHERRACK.defaultBlockState()); // change the final state under certain conditions
+
             event.setFinalState(Blocks.NETHERRACK.defaultBlockState()); // set the block to netherrack
 +
            // note that this happens for any right click on a block, not just dirt
 
         }
 
         }
 
     }
 
     }

Latest revision as of 18:46, 20 April 2021

The variations of BlockEvent let modders intercept interactions between the player and blocks in the world. All of these events have in common the following: the World the action was taken in, and the BlockPos and BlockState that's being modified.

BreakEvent

This event is fired before a block is broken by a player. Predictably, cancelling this event prevents the block from getting broken. Modders have two extra variables to play with:

getExpToDrop(): Returns how much experience ought to drop when the block is broken. Will be zero if the event is currently canceled.

setExpToDrop(): Sets the experience that's going to drop manually.

getPlayer(): Gets the player that's doing the action.

Example usage:

public void onBlockBreak(final BlockEvent.BreakEvent event)
    {
        IWorld world = event.getWorld();
        if (!world.isClientSide() && event.getState().is(Blocks.SAND)) // use the base event to get the state and see what it is
        {
            event.setExpToDrop(10); // change the amount of experience we'll be dropping
            PlayerEntity player = event.getPlayer();
            if (player.getMainHandItem().getItem().equals(Items.DIAMOND_SHOVEL)) // check what we're holding
            {
                ItemHandlerHelper.giveItemToPlayer(player, new ItemStack(Items.BONE)); // give a bone
            }
        }
    }

Some ideas for using this event:

  • Stopping players from breaking a block
  • Changing the XP drop of a vanilla block
  • Modify the behavior of a tool

EntityPlaceEvent

This event is called when a block is placed.

getEntity(): This is the entity placing the block. Note that this might not be a player! It can be null. This means you should null check the entity. By default, Forge adds this in three places: players placing blocks, endermen placing their held block, as well as ice placed by the Frost Walker enchantment.

getBlockSnapshot(): Gets the snapshot of what's about to be placed. A BlockSnapshot is just an object containing the dimension, position, NBT, and state of a block, along with some other info. Typically, this isn't necessary.

getPlacedBlock() The BlockState to be placed.

getPlacedAgainst() The BlockState that was clicked in order to place the block. Imagine planting a flower on some dirt. The dirt would be the state here.

Example usage:

public void onEntityPlace(final BlockEvent.EntityPlaceEvent event)
    {
        IWorld world = event.getWorld();
        if (!world.isClientSide() && event.getEntity() instanceof EndermanEntity)
        {
            event.setCanceled(true); // prevent enderman placement
        }
    }

Some ideas for using this event:

  • (See above) Changing how frost walker works based on certain conditions
  • Preventing a block from being placed
  • Changing the player's inventory when a block is placed
  • Cause another block to be placed instead of another

EntityMultiPlaceEvent

This is the same as above except it's fired when a block causes another block to be replaced. A great example is beds, where placing one half of the bed causes another half to be placed.

getReplacedBlockSnapshots(): Everything is the same as before, except we now have a list of snapshots. How sweet.

NeighborNotifyEvent

This event is fired when a block tells its neighbors to update themselves. This happens all the time, typically with redstone blocks like tripwires or repeaters.

getNotifiedSides(): A set of Direction values that were updated during the event. Sometimes, blocks will choose to exclude a side from being updated, but most often this will be all six directions.

getForceRedstoneUpdate() This is a little funky. There's an option when blocks are set to force a redstone update. If that happened, this will be true. You typically won't need this.

Example usage:

public void onNeighborNotify(final BlockEvent.NeighborNotifyEvent event)
    {
        IWorld world = event.getWorld();
        if (world.isClientSide()) return;
        BlockPos eventPos = event.getPos();
        if (event.getState().is(Blocks.REDSTONE_WIRE)) // only do it for redstone
        {
            for (Direction direction : event.getNotifiedSides()) // cycle through notified directions
            {
                BlockPos pos = eventPos.relative(direction); // move to that spot
                if (world.isEmptyBlock(pos))
                {
                    world.setBlock(pos, Blocks.GLOWSTONE.defaultBlockState(), 3); // place glowstone
                }
            }
        }
    }

Some ideas for using this event:

  • Adding blocks that interact with redstone
  • Making a block update detector block

CreateFluidSourceEvent

This event controls creation of fluid sources. This is different than cancellable events, because it has a Result. Setting it to ALLOW causes a source block to created, even if that's not normally what would happen. Setting it to DENY always prevents source creation.

Example usage:

public void onFluidSourceCreate(final BlockEvent.CreateFluidSourceEvent event)
    {
        IWorldReader world = event.getWorld();
        if (!world.isClientSide() && event.getPos().getY() > 100)
        {
            event.setResult(Event.Result.DENY); // prevent source creation above y = 100
        }
    }

FluidPlaceBlockEvent

This event fires when fluids place blocks. Examples of this happening are: basalt, stone, cobblestone, obsidian, and fire (think lava pools). You can cancel this event to prevent placement.

getLiquidPos() This is the liquid's position. For cases like cobble generation, this is the same as the original getPos(). But for placing fire, this won't be the same.

getNewState() Gets the state to be placed.

setNewState() Sets the state to be placed.

getOriginalState() What the block was before this event.

Example usage:

public void onFluidPlace(final BlockEvent.FluidPlaceBlockEvent event)
    {
        IWorld world = event.getWorld();
        if (world.isClientSide()) return;
        BlockState newState = event.getNewState();
        if (newState.is(Blocks.COBBLESTONE) && world.dayTime() > 14000L)
        {
            event.setNewState(Blocks.ANDESITE.defaultBlockState()); // change the block if done during nighttime.
        }
    }

Some ideas for using this event:

  • Changing behavior of cobble generators
  • Preventing cobble generators
  • Adding more aggressive effects during fire spreading from lava

CropGrowEvent

These events are fired during crop growth. The Pre event is fired before. It's fired when vanilla blocks try to 'grow' during random ticks (think cacti getting taller). Setting DEFAULT as the result will cause no change. Setting ALLOW forces growth. Setting DENY prevents growth.

The Post event is fired after growth.

getOriginalState() Get the state before growth happened.

Example usage:

public void onCropGrowPre(final BlockEvent.CropGrowEvent.Pre event)
    {
        IWorld world = event.getWorld();
        if (world.isClientSide()) return;
        BlockState state = event.getState();
        if (state.is(Blocks.WHEAT))
        {
            event.setResult(Event.Result.DENY); // prevent wheat from growing
        }
        else if (state.is(Blocks.BAMBOO))
        {
            if (world.getRandom().nextFloat() > 0.2F) // take a 4/5 chance
            {
                event.setResult(Event.Result.DENY); // make bamboo grow less often
            }
        }
    }

public void onCropsGrowPost(final BlockEvent.CropGrowEvent.Post event)
    {
        IWorld world = event.getWorld();
        if (!world.isClientSide() && event.getState().is(Blocks.WHEAT)) // check wheat
        {
            BlockState newState = event.getState();
            if (newState.is(Blocks.WHEAT)) // defensive check to make sure we can do this
            {
                world.setBlock(event.getPos(), newState.setValue(CropsBlock.AGE, 7), 3); // set max growth wheat
            }
        }
    }

Some ideas for using this event:

  • Changing growth rate of crops
  • Preventing crop growth under certain conditions
  • Spawning an entity when something grows

FarmlandTrampleEvent

This event is fired when farmland gets trampled. You can cancel it to prevent trampling.

getEntity() The entity that trampled

getFallDistance() How far that entity fell

Example usage:

public void onTrample(final BlockEvent.FarmlandTrampleEvent event)
    {
        IWorld world = event.getWorld();
        if (!world.isClientSide() && event.getFallDistance() < 10.0F)
        {
            event.setCanceled(true); // change conditions for trampling
        }
    }

PortalSpawnEvent

This event is fired when a nether portal is created.

getPortalSize() returns a PortalSize object, basically only useful for determining if the portal is going to be valid or not.

This is mostly used for preventing portal spawning in certain dimensions (even the overworld).

Example usage:

public void onPortalLight(final BlockEvent.PortalSpawnEvent event)
    {
        event.setCanceled(true);
    }

BlockToolInteractEvent

This event is fired on right click tool events, such as path creation, wood stripping, and farmland tilling. You have access to the player, the ItemStack they're holding, and the ToolType they have.

setFinalState(): Set what you want the result to be.

getFinalState(): Returns what it will be changed to. This will normally just return whatever the original block is unless you or another modder has set it. It can't check, for example, if something *will* turn into farmland in the future.

Example usage:

public void onToolInteract(final BlockEvent.BlockToolInteractEvent event)
    {
        IWorld world = event.getWorld();
        if (!world.isClientSide() && event.getHeldItemStack().getItem() == Items.NETHERITE_HOE)
        {
            event.setFinalState(Blocks.NETHERRACK.defaultBlockState()); // set the block to netherrack
            // note that this happens for any right click on a block, not just dirt
        }
    }

Some ideas for using this:

  • Setting your custom farmland block during tilling
  • Adding stripped wood behavior for other blocks
  • Disable path creation