This commit is contained in:
Guilherme Corvelo 2025-12-03 19:16:08 -06:00 committed by GitHub
commit 05d68b195b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 444 additions and 0 deletions

View File

@ -0,0 +1,83 @@
package org.bukkit.event.block;
import org.bukkit.block.Block;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
/**
* Called when the Creaking Heart block attempts to spawn or remove its protector (Creaking mob).
* <p>
* Examples:
* <ul>
* <li>Protector is about to spawn near the Creaking Heart block.
* <li>Protector is about to be removed (due to death, distance, or other reasons).
* </ul>
* <p>
*/
@NullMarked
public class CreakingHeartProtectorChangeEvent extends BlockEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
public boolean spawn = false;
@Nullable
private final LivingEntity protector;
private boolean cancelled;
/**
* Constructs a new CreakingHeartProtectorChangeEvent.
*
* @param block the Creaking Heart block involved in this event
* @param protector the protector entity (may be null for REMOVE if not available)
* @param spawn the indicator of whether the mob is spawned or not
*/
@ApiStatus.Internal
public CreakingHeartProtectorChangeEvent(Block block, @Nullable LivingEntity protector, boolean spawn) {
super(block);
this.protector = protector;
this.spawn = spawn;
}
/**
* Gets the protector entity involved in this event.
*
* @return the protector entity, or null if not available
*/
@Nullable
public LivingEntity getProtector() {
return protector;
}
/**
* Get whether the protector is being spawned.
*
* @return whether the protector is being spawned
*/
public boolean isSpawned() {
return spawn;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
}

View File

@ -0,0 +1,111 @@
package org.bukkit.event.block;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.type.CreakingHeart.State;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import com.google.common.base.Preconditions;
/**
* Called when a Creaking Heart block changes its state (e.g., AWAKE, DORMANT, UPROOTED) due to world conditions.
* <p>
* Examples:
* <ul>
* <li>Creaking Heart becoming AWAKE at night.
* <li>Creaking Heart becoming DORMANT during the day.
* <li>Creaking Heart becoming UPROOTED when requirements are not met.
* </ul>
* <p>
*/
@NullMarked
public class CreakingHeartTransformEvent extends BlockEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final BlockState oldState;
private final BlockState newState;
private final State oldCreakingHeartState;
private final State newCreakingHeartState;
private boolean cancelled;
/**
* Constructs a new CreakingHeartTransformEvent.
*
* @param block the block being transformed
* @param newState the new state of the block after transformation
* @param oldState the previous state of the block before transformation
*/
@ApiStatus.Internal
public CreakingHeartTransformEvent(
Block block,
BlockState oldState,
BlockState newState,
State oldCreakingHeartState,
State newCreakingHeartState
) {
super(Preconditions.checkNotNull(block, "block cannot be null"));
this.oldState = Preconditions.checkNotNull(oldState, "oldState cannot be null");
this.newState = Preconditions.checkNotNull(newState, "newState cannot be null");
this.oldCreakingHeartState = Preconditions.checkNotNull(oldCreakingHeartState, "oldCreakingHeartState cannot be null");
this.newCreakingHeartState = Preconditions.checkNotNull(newCreakingHeartState, "newCreakingHeartState cannot be null");
}
/**
* Gets the new state of the block after the transformation.
*
* @return the new block state for this event's block
*/
public BlockState getNewState() {
return this.newState;
}
/**
* Gets the previous state of the block before the transformation.
*
* @return the old block state for this event's block
*/
public BlockState getOldState() {
return this.oldState;
}
/**
* Gets the previous logical state of the creaking heart.
*
* @return the old logical state
*/
public State getOldCreakingHeartState() {
return this.oldCreakingHeartState;
}
/**
* Gets the new logical state of the creaking heart.
*
* @return the new logical state
*/
public State getNewCreakingHeartState() {
return this.newCreakingHeartState;
}
@Override
public boolean isCancelled() {
return this.cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
}

View File

@ -0,0 +1,68 @@
package org.bukkit.event.block;
import com.google.common.base.Preconditions;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Called when an Eyeblossom opens or closes (transforms its state) due to world conditions.
* <p>
* Examples:
* <ul>
* <li>Eyeblossom opening at night.
* <li>Eyeblossom closing during the day.
* </ul>
* <p>
*/
@NullMarked
public class EyeblossomTransformEvent extends BlockEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final BlockState newState;
private boolean cancelled;
/**
* Constructs a new EyeblossomTransformEvent.
*
* @param block the block being transformed
* @param newState the new state of the block after transformation
*/
@ApiStatus.Internal
public EyeblossomTransformEvent(final Block block,final BlockState newState) {
super(Preconditions.checkNotNull(block, "block"));
this.newState = Preconditions.checkNotNull(newState, "newState");
}
/**
* Gets the new state of the block after the transformation.
*
* @return the new block state for this event's block
*/
public BlockState getNewState() {
return this.newState;
}
@Override
public boolean isCancelled() {
return this.cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
}

View File

@ -0,0 +1,51 @@
--- a/net/minecraft/world/level/block/CreakingHeartBlock.java
+++ b/net/minecraft/world/level/block/CreakingHeartBlock.java
@@ -106,9 +_,46 @@
}
private static BlockState updateState(BlockState state, Level level, BlockPos pos) {
+
+ // Paper Start - Fire CreakingHeartTransformEvent
+ net.minecraft.world.level.block.state.properties.CreakingHeartState oldState = state.getValue(STATE);
+ net.minecraft.world.level.block.state.properties.CreakingHeartState newState;
+
boolean hasRequiredLogs = hasRequiredLogs(state, level, pos);
- boolean flag = state.getValue(STATE) == CreakingHeartState.UPROOTED;
- return hasRequiredLogs && flag ? state.setValue(STATE, isNaturalNight(level) ? CreakingHeartState.AWAKE : CreakingHeartState.DORMANT) : state;
+
+ boolean flag = oldState == net.minecraft.world.level.block.state.properties.CreakingHeartState.UPROOTED;
+ if (hasRequiredLogs && flag) {
+ newState = isNaturalNight(level)
+ ? net.minecraft.world.level.block.state.properties.CreakingHeartState.AWAKE
+ : net.minecraft.world.level.block.state.properties.CreakingHeartState.DORMANT;
+ } else {
+ newState = oldState;
+ }
+
+ if (oldState != newState && level instanceof net.minecraft.server.level.ServerLevel serverLevel) {
+
+ org.bukkit.World bukkitWorld = serverLevel.getWorld();
+ org.bukkit.block.Block bukkitBlock = bukkitWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+ org.bukkit.block.BlockState bukkitOldState = bukkitBlock.getState();
+ net.minecraft.world.level.block.state.BlockState newNmsState = state.setValue(STATE, newState);
+ org.bukkit.block.BlockState bukkitNewState = bukkitBlock.getState();
+ bukkitNewState.setBlockData(org.bukkit.craftbukkit.block.data.CraftBlockData.fromData(newNmsState));
+ bukkitNewState.setType(org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(state.getBlock()));
+
+ org.bukkit.block.data.type.CreakingHeart.State apiOldState = org.bukkit.block.data.type.CreakingHeart.State.valueOf(oldState.name());
+ org.bukkit.block.data.type.CreakingHeart.State apiNewState = org.bukkit.block.data.type.CreakingHeart.State.valueOf(newState.name());
+
+ org.bukkit.event.block.CreakingHeartTransformEvent event =
+ new org.bukkit.event.block.CreakingHeartTransformEvent(
+ bukkitBlock, bukkitOldState, bukkitNewState, apiOldState, apiNewState
+ );
+ if (!event.callEvent() || event.isCancelled()) {
+ return state;
+ }
+ return state.setValue(STATE, newState);
+ }
+ return state;
+ // Paper End - Fire CreakingHeartTransformEvent
}
public static boolean hasRequiredLogs(BlockState state, LevelReader level, BlockPos pos) {

View File

@ -1,5 +1,25 @@
--- a/net/minecraft/world/level/block/EyeblossomBlock.java
+++ b/net/minecraft/world/level/block/EyeblossomBlock.java
@@ -82,6 +_,19 @@
} else if (CreakingHeartBlock.isNaturalNight(level) == this.type.open) {
return false;
} else {
+
+ // Paper Start - Fire EyeblossomTransformEvent before changing block
+ org.bukkit.World bukkitWorld = level.getWorld();
+ org.bukkit.block.Block bukkitBlock = bukkitWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+ org.bukkit.block.BlockState newState = bukkitBlock.getState();
+ // Set the new state type to the transformed block (OPEN or CLOSED)
+ newState.setType(org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(this.type.transform().block()));
+ org.bukkit.event.block.EyeblossomTransformEvent event = new org.bukkit.event.block.EyeblossomTransformEvent(bukkitBlock, newState);
+ if (!event.callEvent() || event.isCancelled()) {
+ return false;
+ }
+ // Paper End - Fire EyeblossomTransformEvent before changing block
+
EyeblossomBlock.Type type = this.type.transform();
level.setBlock(pos, type.state(), 3);
level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(state));
@@ -100,6 +_,7 @@
@Override

View File

@ -38,3 +38,23 @@
if (!player.addItem(itemStack)) {
player.drop(itemStack, false);
}
@@ -135,6 +_,19 @@
boolean flag = this.potted == Blocks.OPEN_EYEBLOSSOM;
boolean isNaturalNight = CreakingHeartBlock.isNaturalNight(level);
if (flag != isNaturalNight) {
+
+ // Paper Start - Fire EyeblossomTransformEvent before changing block
+ org.bukkit.World bukkitWorld = level.getWorld();
+ org.bukkit.block.Block bukkitBlock = bukkitWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+ org.bukkit.block.BlockState newState = bukkitBlock.getState();
+ // Set the new state type to the transformed block (OPEN or CLOSED)
+ newState.setType(org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(EyeblossomBlock.Type.fromBoolean(flag).transform().block()));
+ org.bukkit.event.block.EyeblossomTransformEvent event = new org.bukkit.event.block.EyeblossomTransformEvent(bukkitBlock, newState);
+ if (!event.callEvent() || event.isCancelled()) {
+ return;
+ }
+ // Paper End - Fire EyeblossomTransformEvent before changing block
+
level.setBlock(pos, this.opposite(state), 3);
EyeblossomBlock.Type type = EyeblossomBlock.Type.fromBoolean(flag).transform();
type.spawnTransformParticle(level, pos, random);

View File

@ -0,0 +1,91 @@
--- a/net/minecraft/world/level/block/entity/CreakingHeartBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/CreakingHeartBlockEntity.java
@@ -138,12 +_,40 @@
}
private static BlockState updateCreakingState(Level level, BlockState state, BlockPos pos, CreakingHeartBlockEntity creakingHeart) {
+ // Paper Start - Fire CreakingHeartTransformEvent
+ CreakingHeartState oldState = state.getValue(CreakingHeartBlock.STATE);
+ CreakingHeartState newState;
if (!CreakingHeartBlock.hasRequiredLogs(state, level, pos) && creakingHeart.creakingInfo == null) {
- return state.setValue(CreakingHeartBlock.STATE, CreakingHeartState.UPROOTED);
+ newState = CreakingHeartState.UPROOTED;
} else {
boolean isNaturalNight = CreakingHeartBlock.isNaturalNight(level);
- return state.setValue(CreakingHeartBlock.STATE, isNaturalNight ? CreakingHeartState.AWAKE : CreakingHeartState.DORMANT);
- }
+ newState = isNaturalNight ? CreakingHeartState.AWAKE : CreakingHeartState.DORMANT;
+ }
+ if (oldState != newState && level instanceof ServerLevel serverLevel) {
+
+ org.bukkit.World bukkitWorld = serverLevel.getWorld();
+ org.bukkit.block.Block bukkitBlock = bukkitWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+ org.bukkit.block.BlockState bukkitOldState = bukkitBlock.getState();
+ net.minecraft.world.level.block.state.BlockState newNmsState = state.setValue(CreakingHeartBlock.STATE, newState);
+ org.bukkit.block.BlockState bukkitNewState = bukkitBlock.getState();
+ bukkitNewState.setBlockData(org.bukkit.craftbukkit.block.data.CraftBlockData.fromData(newNmsState));
+ bukkitNewState.setType(org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(state.getBlock()));
+
+ org.bukkit.block.data.type.CreakingHeart.State apiOldState = org.bukkit.block.data.type.CreakingHeart.State.valueOf(oldState.name());
+ org.bukkit.block.data.type.CreakingHeart.State apiNewState = org.bukkit.block.data.type.CreakingHeart.State.valueOf(newState.name());
+
+ org.bukkit.event.block.CreakingHeartTransformEvent event =
+ new org.bukkit.event.block.CreakingHeartTransformEvent(
+ bukkitBlock, bukkitOldState, bukkitNewState, apiOldState, apiNewState
+ );
+ if (!event.callEvent() || event.isCancelled()) {
+ return state;
+ }
+ return state.setValue(CreakingHeartBlock.STATE, newState);
+ }
+ return state;
+ // Paper End - Fire CreakingHeartTransformEvent
+
}
private double distanceToCreaking() {
@@ -207,6 +_,21 @@
return null;
} else {
Creaking creaking = optional.get();
+
+ // Paper Start - Fire CreakingHeartProtectorChangeEvent (SPAWN)
+ org.bukkit.World bukkitWorld = level.getWorld();
+ org.bukkit.block.Block bukkitBlock = bukkitWorld.getBlockAt(blockPos.getX(), blockPos.getY(), blockPos.getZ());
+ org.bukkit.entity.LivingEntity bukkitProtector = (org.bukkit.entity.LivingEntity) creaking.getBukkitEntity();
+ org.bukkit.event.block.CreakingHeartProtectorChangeEvent event =
+ new org.bukkit.event.block.CreakingHeartProtectorChangeEvent(
+ bukkitBlock, bukkitProtector, true
+ );
+ if (!event.callEvent() || event.isCancelled()) {
+ creaking.discard();
+ return null;
+ }
+ // Paper End - Fire CreakingHeartProtectorChangeEvent (SPAWN)
+
level.gameEvent(creaking, GameEvent.ENTITY_PLACE, creaking.position());
level.broadcastEntityEvent(creaking, (byte)60);
creaking.setTransient(blockPos);
@@ -316,6 +_,22 @@
public void removeProtector(@Nullable DamageSource damageSource) {
if (this.getCreakingProtector().orElse(null) instanceof Creaking creaking) {
+
+ // Paper Start: Fire CreakingHeartProtectorChangeEvent (REMOVE)
+ if (this.level instanceof ServerLevel serverLevel) {
+ org.bukkit.World bukkitWorld = serverLevel.getWorld();
+ org.bukkit.block.Block bukkitBlock = bukkitWorld.getBlockAt(this.getBlockPos().getX(), this.getBlockPos().getY(), this.getBlockPos().getZ());
+ org.bukkit.entity.LivingEntity bukkitProtector = (org.bukkit.entity.LivingEntity) creaking.getBukkitEntity();
+ org.bukkit.event.block.CreakingHeartProtectorChangeEvent event =
+ new org.bukkit.event.block.CreakingHeartProtectorChangeEvent(
+ bukkitBlock, bukkitProtector, false
+ );
+ if (!event.callEvent() || event.isCancelled()) {
+ return;
+ }
+ }
+ // Paper End - Fire CreakingHeartProtectorChangeEvent (REMOVE)
+
if (damageSource == null) {
creaking.tearDown();
} else {