From 02a1f61c346c53a6f8bc6508ba03733dcf25f747 Mon Sep 17 00:00:00 2001 From: Brennan Ward Date: Sat, 25 May 2024 08:07:38 -0700 Subject: [PATCH] Remove uses of `Event.Result` and update events to have their own Result classes (#588) --- gradle.properties | 2 +- .../MultiPlayerGameMode.java.patch | 14 +- .../renderer/entity/EntityRenderer.java.patch | 11 +- .../server/level/ServerPlayer.java.patch | 46 +++- .../level/ServerPlayerGameMode.java.patch | 12 +- .../minecraft/world/entity/Entity.java.patch | 2 +- .../world/entity/LivingEntity.java.patch | 40 ++- .../net/minecraft/world/entity/Mob.java.patch | 21 +- .../ai/behavior/HarvestFarmland.java.patch | 2 +- .../entity/ai/goal/EatBlockGoal.java.patch | 4 +- .../entity/ai/goal/RemoveBlockGoal.java.patch | 2 +- .../world/entity/animal/Fox.java.patch | 2 +- .../world/entity/animal/Rabbit.java.patch | 2 +- .../world/entity/animal/SnowGolem.java.patch | 2 +- .../entity/animal/allay/Allay.java.patch | 2 +- .../entity/boss/wither/WitherBoss.java.patch | 10 +- .../world/entity/item/ItemEntity.java.patch | 29 +- .../world/entity/monster/EnderMan.java.patch | 4 +- .../world/entity/monster/Evoker.java.patch | 2 +- .../world/entity/monster/Ravager.java.patch | 2 +- .../entity/monster/Silverfish.java.patch | 4 +- .../world/entity/monster/Spider.java.patch | 16 -- .../world/entity/monster/Zombie.java.patch | 39 --- .../entity/monster/piglin/Piglin.java.patch | 2 +- .../world/entity/player/Player.java.patch | 14 +- .../projectile/LargeFireball.java.patch | 2 +- .../entity/projectile/Projectile.java.patch | 2 +- .../projectile/SmallFireball.java.patch | 2 +- .../world/item/BoneMealItem.java.patch | 8 +- .../world/item/BucketItem.java.patch | 9 - .../minecraft/world/level/Level.java.patch | 2 +- .../world/level/NaturalSpawner.java.patch | 2 +- .../level/block/BambooStalkBlock.java.patch | 4 +- .../world/level/block/CactusBlock.java.patch | 4 +- .../level/block/ChorusFlowerBlock.java.patch | 4 +- .../world/level/block/CocoaBlock.java.patch | 4 +- .../world/level/block/CropBlock.java.patch | 6 +- .../world/level/block/FungusBlock.java.patch | 11 +- .../block/GrowingPlantHeadBlock.java.patch | 4 +- .../level/block/MushroomBlock.java.patch | 22 +- .../level/block/NetherWartBlock.java.patch | 4 +- .../world/level/block/StemBlock.java.patch | 4 +- .../level/block/SugarCaneBlock.java.patch | 4 +- .../block/SweetBerryBushBlock.java.patch | 4 +- .../level/block/TurtleEggBlock.java.patch | 2 +- .../level/block/grower/TreeGrower.java.patch | 8 +- .../level/levelgen/PhantomSpawner.java.patch | 14 +- .../level/material/FlowingFluid.java.patch | 2 +- .../neoforge/client/ClientHooks.java | 8 +- .../client/event/RenderNameTagEvent.java | 47 ++-- .../neoforge/client/event/ScreenEvent.java | 142 +++++++--- .../neoforge/common/CommonHooks.java | 55 ++-- .../common/extensions/IBlockExtension.java | 30 +-- .../extensions/IBlockStateExtension.java | 17 +- .../neoforge/common/util/TriState.java | 16 +- .../neoforged/neoforge/event/EventHooks.java | 251 ++++++++++-------- .../event/entity/EntityMobGriefingEvent.java | 47 +++- .../entity/living/LivingPackSizeEvent.java | 34 --- .../event/entity/living/MobDespawnEvent.java | 67 +++++ .../event/entity/living/MobEffectEvent.java | 56 +++- .../event/entity/living/MobSpawnEvent.java | 138 ++++++---- .../entity/living/SpawnClusterSizeEvent.java | 47 ++++ .../event/entity/living/ZombieEvent.java | 115 -------- .../event/entity/player/BonemealEvent.java | 104 ++++++-- .../player/CanContinueSleepingEvent.java | 70 +++++ .../entity/player/CanPlayerSleepEvent.java | 82 ++++++ .../event/entity/player/CriticalHitEvent.java | 98 ++++--- .../entity/player/EntityItemPickupEvent.java | 35 --- .../event/entity/player/FillBucketEvent.java | 61 ----- .../entity/player/ItemEntityPickupEvent.java | 117 ++++++++ .../event/entity/player/PlayerEvent.java | 26 -- .../entity/player/PlayerInteractEvent.java | 41 +-- .../entity/player/PlayerSleepInBedEvent.java | 50 ---- .../player/PlayerSpawnPhantomsEvent.java | 87 ++++-- .../player/SleepingLocationCheckEvent.java | 38 --- .../entity/player/SleepingTimeCheckEvent.java | 40 --- .../neoforge/event/level/BlockEvent.java | 81 ------ ...eEvent.java => BlockGrowFeatureEvent.java} | 43 +-- .../level/block/CreateFluidSourceEvent.java | 63 +++++ .../event/level/block/CropGrowEvent.java | 105 ++++++++ .../event/level/block/package-info.java | 13 + .../gametest/GameTestPlayer.java | 7 +- .../neoforge/debug/block/BlockTests.java | 4 +- .../debug/entity/player/PlayerEventTests.java | 7 +- .../neoforge/debug/fluid/FluidEventTests.java | 9 +- .../neoforge/debug/level/LevelEventTests.java | 4 +- .../neoforge/oldtest/PotionEventTest.java | 4 +- .../NameplateRenderingEventTest.java | 4 +- .../player/PlayerSpawnPhantomsEventTest.java | 3 +- 89 files changed, 1570 insertions(+), 1110 deletions(-) delete mode 100644 patches/net/minecraft/world/entity/monster/Spider.java.patch delete mode 100644 src/main/java/net/neoforged/neoforge/event/entity/living/LivingPackSizeEvent.java create mode 100644 src/main/java/net/neoforged/neoforge/event/entity/living/MobDespawnEvent.java create mode 100644 src/main/java/net/neoforged/neoforge/event/entity/living/SpawnClusterSizeEvent.java delete mode 100644 src/main/java/net/neoforged/neoforge/event/entity/living/ZombieEvent.java create mode 100644 src/main/java/net/neoforged/neoforge/event/entity/player/CanContinueSleepingEvent.java create mode 100644 src/main/java/net/neoforged/neoforge/event/entity/player/CanPlayerSleepEvent.java delete mode 100644 src/main/java/net/neoforged/neoforge/event/entity/player/EntityItemPickupEvent.java delete mode 100644 src/main/java/net/neoforged/neoforge/event/entity/player/FillBucketEvent.java create mode 100644 src/main/java/net/neoforged/neoforge/event/entity/player/ItemEntityPickupEvent.java delete mode 100644 src/main/java/net/neoforged/neoforge/event/entity/player/PlayerSleepInBedEvent.java delete mode 100644 src/main/java/net/neoforged/neoforge/event/entity/player/SleepingLocationCheckEvent.java delete mode 100644 src/main/java/net/neoforged/neoforge/event/entity/player/SleepingTimeCheckEvent.java rename src/main/java/net/neoforged/neoforge/event/level/{SaplingGrowTreeEvent.java => BlockGrowFeatureEvent.java} (54%) create mode 100644 src/main/java/net/neoforged/neoforge/event/level/block/CreateFluidSourceEvent.java create mode 100644 src/main/java/net/neoforged/neoforge/event/level/block/CropGrowEvent.java create mode 100644 src/main/java/net/neoforged/neoforge/event/level/block/package-info.java diff --git a/gradle.properties b/gradle.properties index 821d8aeab5..43b414de00 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ neoforge_snapshot_next_stable=21.0 mergetool_version=2.0.0 accesstransformers_version=10.0.1 coremods_version=6.0.4 -eventbus_version=7.2.0 +eventbus_version=8.0.1 modlauncher_version=11.0.2 securejarhandler_version=3.0.4 bootstraplauncher_version=2.0.0 diff --git a/patches/net/minecraft/client/multiplayer/MultiPlayerGameMode.java.patch b/patches/net/minecraft/client/multiplayer/MultiPlayerGameMode.java.patch index 90ac22fcf0..c84184a51f 100644 --- a/patches/net/minecraft/client/multiplayer/MultiPlayerGameMode.java.patch +++ b/patches/net/minecraft/client/multiplayer/MultiPlayerGameMode.java.patch @@ -30,12 +30,12 @@ this.startPrediction(this.minecraft.level, p_233728_ -> { boolean flag = !blockstate1.isAir(); if (flag && this.destroyProgress == 0.0F) { -+ if (event.getUseBlock() != net.neoforged.bus.api.Event.Result.DENY) ++ if (event.getUseBlock() != net.neoforged.neoforge.common.util.TriState.FALSE) blockstate1.attack(this.minecraft.level, p_105270_, this.minecraft.player); } + ServerboundPlayerActionPacket packet = new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK, p_105270_, p_105271_, p_233728_); -+ if (event.getUseItem() == net.neoforged.bus.api.Event.Result.DENY) return packet; ++ if (event.getUseItem().isFalse()) return packet; if (flag && blockstate1.getDestroyProgress(this.minecraft.player, this.minecraft.player.level(), p_105270_) >= 1.0F) { this.destroyBlock(p_105270_); } else { @@ -69,7 +69,7 @@ this.destroyTicks++; this.minecraft.getTutorial().onDestroyBlock(this.minecraft.level, p_105284_, blockstate, Mth.clamp(this.destroyProgress, 0.0F, 1.0F)); -+ if (net.neoforged.neoforge.common.CommonHooks.onClientMineHold(this.minecraft.player, p_105284_, p_105285_).getUseItem() == net.neoforged.bus.api.Event.Result.DENY) return true; ++ if (net.neoforged.neoforge.common.CommonHooks.onClientMineHold(this.minecraft.player, p_105284_, p_105285_).getUseItem().isFalse()) return true; if (this.destroyProgress >= 1.0F) { this.isDestroying = false; this.startPrediction(this.minecraft.level, p_233739_ -> { @@ -95,7 +95,7 @@ } else { - boolean flag = !p_233747_.getMainHandItem().isEmpty() || !p_233747_.getOffhandItem().isEmpty(); + UseOnContext useoncontext = new UseOnContext(p_233747_, p_233748_, p_233749_); -+ if (event.getUseItem() != net.neoforged.bus.api.Event.Result.DENY) { ++ if (event.getUseItem() != net.neoforged.neoforge.common.util.TriState.FALSE) { + InteractionResult result = itemstack.onItemUseFirst(useoncontext); + if (result != InteractionResult.PASS) { + return result; @@ -104,7 +104,7 @@ + boolean flag = !p_233747_.getMainHandItem().doesSneakBypassUse(p_233747_.level(), blockpos, p_233747_) || !p_233747_.getOffhandItem().doesSneakBypassUse(p_233747_.level(), blockpos, p_233747_); boolean flag1 = p_233747_.isSecondaryUseActive() && flag; - if (!flag1) { -+ if (event.getUseBlock() == net.neoforged.bus.api.Event.Result.ALLOW || (event.getUseBlock() != net.neoforged.bus.api.Event.Result.DENY && !flag1)) { ++ if (event.getUseBlock().isTrue() || (event.getUseBlock().isDefault() && !flag1)) { BlockState blockstate = this.minecraft.level.getBlockState(blockpos); if (!this.connection.isFeatureEnabled(blockstate.getBlock().requiredFeatures())) { return InteractionResult.FAIL; @@ -114,10 +114,10 @@ - if (!itemstack.isEmpty() && !p_233747_.getCooldowns().isOnCooldown(itemstack.getItem())) { - UseOnContext useoncontext = new UseOnContext(p_233747_, p_233748_, p_233749_); -+ if (event.getUseItem() == net.neoforged.bus.api.Event.Result.DENY) { ++ if (event.getUseItem().isFalse()) { + return InteractionResult.PASS; + } -+ if (event.getUseItem() == net.neoforged.bus.api.Event.Result.ALLOW || (!itemstack.isEmpty() && !p_233747_.getCooldowns().isOnCooldown(itemstack.getItem()))) { ++ if (event.getUseItem().isTrue() || (!itemstack.isEmpty() && !p_233747_.getCooldowns().isOnCooldown(itemstack.getItem()))) { InteractionResult interactionresult1; if (this.localPlayerMode.isCreative()) { int i = itemstack.getCount(); diff --git a/patches/net/minecraft/client/renderer/entity/EntityRenderer.java.patch b/patches/net/minecraft/client/renderer/entity/EntityRenderer.java.patch index f23d1e70fc..d8feb19e12 100644 --- a/patches/net/minecraft/client/renderer/entity/EntityRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/entity/EntityRenderer.java.patch @@ -1,15 +1,16 @@ --- a/net/minecraft/client/renderer/entity/EntityRenderer.java +++ b/net/minecraft/client/renderer/entity/EntityRenderer.java -@@ -71,8 +_,10 @@ +@@ -71,8 +_,11 @@ } public void render(T p_114485_, float p_114486_, float p_114487_, PoseStack p_114488_, MultiBufferSource p_114489_, int p_114490_) { - if (this.shouldShowName(p_114485_)) { - this.renderNameTag(p_114485_, p_114485_.getDisplayName(), p_114488_, p_114489_, p_114490_, p_114487_); -+ var renderNameTagEvent = new net.neoforged.neoforge.client.event.RenderNameTagEvent(p_114485_, p_114485_.getDisplayName(), this, p_114488_, p_114489_, p_114490_, p_114487_); -+ net.neoforged.neoforge.common.NeoForge.EVENT_BUS.post(renderNameTagEvent); -+ if (renderNameTagEvent.getResult() != net.neoforged.bus.api.Event.Result.DENY && (renderNameTagEvent.getResult() == net.neoforged.bus.api.Event.Result.ALLOW || this.shouldShowName(p_114485_))) { -+ this.renderNameTag(p_114485_, renderNameTagEvent.getContent(), p_114488_, p_114489_, p_114490_, p_114487_); ++ // Neo: Post the RenderNameTagEvent and conditionally wrap #renderNameTag based on the result. ++ var event = new net.neoforged.neoforge.client.event.RenderNameTagEvent(p_114485_, p_114485_.getDisplayName(), this, p_114488_, p_114489_, p_114490_, p_114487_); ++ net.neoforged.neoforge.common.NeoForge.EVENT_BUS.post(event); ++ if (event.canRender().isTrue() || event.canRender().isDefault() && this.shouldShowName(p_114485_)) { ++ this.renderNameTag(p_114485_, event.getContent(), p_114488_, p_114489_, p_114490_, p_114487_); } } diff --git a/patches/net/minecraft/server/level/ServerPlayer.java.patch b/patches/net/minecraft/server/level/ServerPlayer.java.patch index dfa8ebad7c..6930c04d8a 100644 --- a/patches/net/minecraft/server/level/ServerPlayer.java.patch +++ b/patches/net/minecraft/server/level/ServerPlayer.java.patch @@ -87,25 +87,45 @@ } return this; -@@ -912,6 +_,9 @@ +@@ -912,6 +_,15 @@ @Override public Either startSleepInBed(BlockPos p_9115_) { -+ java.util.Optional optAt = java.util.Optional.of(p_9115_); -+ Player.BedSleepingProblem ret = net.neoforged.neoforge.event.EventHooks.onPlayerSleepInBed(this, optAt); -+ if (ret != null) return Either.left(ret); ++ // Neo: Encapsulate the vanilla check logic to supply to the CanPlayerSleepEvent ++ var vanillaResult = ((java.util.function.Supplier>) () -> { ++ // Guard against modded beds that may not have the FACING property. ++ // We just return success (Unit) here. Modders will need to implement conditions in the CanPlayerSleepEvent ++ if (!this.level().getBlockState(p_9115_).hasProperty(HorizontalDirectionalBlock.FACING)) { ++ return Either.right(Unit.INSTANCE); ++ } ++ ++ // Start vanilla code Direction direction = this.level().getBlockState(p_9115_).getValue(HorizontalDirectionalBlock.FACING); if (this.isSleeping() || !this.isAlive()) { return Either.left(Player.BedSleepingProblem.OTHER_PROBLEM); -@@ -923,7 +_,7 @@ - return Either.left(Player.BedSleepingProblem.OBSTRUCTED); - } else { - this.setRespawnPosition(this.level().dimension(), p_9115_, this.getYRot(), false, true); -- if (this.level().isDay()) { -+ if (!net.neoforged.neoforge.event.EventHooks.fireSleepingTimeCheck(this, optAt)) { - return Either.left(Player.BedSleepingProblem.NOT_POSSIBLE_NOW); - } else { - if (!this.isCreative()) { +@@ -940,7 +_,21 @@ + return Either.left(Player.BedSleepingProblem.NOT_SAFE); + } + } +- ++ // End vanilla code ++ } ++ } ++ return Either.right(Unit.INSTANCE); ++ }).get(); ++ ++ // Fire the event. Return the error if one exists after the event, otherwise use the vanilla logic to start sleeping. ++ vanillaResult = net.neoforged.neoforge.event.EventHooks.canPlayerStartSleeping(this, p_9115_, vanillaResult); ++ if (vanillaResult.left().isPresent()) { ++ return vanillaResult; ++ } ++ ++ { ++ { ++ // Start vanilla code + Either either = super.startSleepInBed(p_9115_).ifRight(p_9029_ -> { + this.awardStat(Stats.SLEEP_IN_BED); + CriteriaTriggers.SLEPT_IN_BED.trigger(this); @@ -962,6 +_,7 @@ } diff --git a/patches/net/minecraft/server/level/ServerPlayerGameMode.java.patch b/patches/net/minecraft/server/level/ServerPlayerGameMode.java.patch index 0be8bb4cbb..a3adabf0c8 100644 --- a/patches/net/minecraft/server/level/ServerPlayerGameMode.java.patch +++ b/patches/net/minecraft/server/level/ServerPlayerGameMode.java.patch @@ -16,7 +16,7 @@ public void handleBlockBreakAction(BlockPos p_215120_, ServerboundPlayerActionPacket.Action p_215121_, Direction p_215122_, int p_215123_, int p_215124_) { + net.neoforged.neoforge.event.entity.player.PlayerInteractEvent.LeftClickBlock event = net.neoforged.neoforge.common.CommonHooks.onLeftClickBlock(player, p_215120_, p_215122_, p_215121_); -+ if (event.isCanceled() || (!this.isCreative() && event.getResult() == net.neoforged.bus.api.Event.Result.DENY)) { ++ if (event.isCanceled()) { + return; + } if (!this.player.canInteractWithBlock(p_215120_, 1.0)) { @@ -26,7 +26,7 @@ float f = 1.0F; BlockState blockstate = this.level.getBlockState(p_215120_); if (!blockstate.isAir()) { -+ if (event.getUseBlock() != net.neoforged.bus.api.Event.Result.DENY) ++ if (event.getUseBlock() != net.neoforged.neoforge.common.util.TriState.FALSE) blockstate.attack(this.level, p_215120_, this.player); f = blockstate.getDestroyProgress(this.player, this.player.level(), p_215120_); } @@ -119,7 +119,7 @@ } } else { + UseOnContext useoncontext = new UseOnContext(p_9266_, p_9269_, p_9270_); -+ if (event.getUseItem() != net.neoforged.bus.api.Event.Result.DENY) { ++ if (event.getUseItem() != net.neoforged.neoforge.common.util.TriState.FALSE) { + InteractionResult result = p_9268_.onItemUseFirst(useoncontext); + if (result != InteractionResult.PASS) return result; + } @@ -128,7 +128,7 @@ + boolean flag1 = (p_9266_.isSecondaryUseActive() && flag) && !(p_9266_.getMainHandItem().doesSneakBypassUse(p_9267_, blockpos, p_9266_) && p_9266_.getOffhandItem().doesSneakBypassUse(p_9267_, blockpos, p_9266_)); ItemStack itemstack = p_9268_.copy(); - if (!flag1) { -+ if (event.getUseBlock() == net.neoforged.bus.api.Event.Result.ALLOW || (event.getUseBlock() != net.neoforged.bus.api.Event.Result.DENY && !flag1)) { ++ if (event.getUseBlock().isTrue() || (event.getUseBlock().isDefault() && !flag1)) { ItemInteractionResult iteminteractionresult = blockstate.useItemOn(p_9266_.getItemInHand(p_9269_), p_9267_, p_9266_, p_9269_, p_9270_); if (iteminteractionresult.consumesAction()) { CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(p_9266_, blockpos, itemstack); @@ -138,8 +138,8 @@ - if (!p_9268_.isEmpty() && !p_9266_.getCooldowns().isOnCooldown(p_9268_.getItem())) { - UseOnContext useoncontext = new UseOnContext(p_9266_, p_9269_, p_9270_); -+ if (event.getUseItem() == net.neoforged.bus.api.Event.Result.ALLOW || (!p_9268_.isEmpty() && !p_9266_.getCooldowns().isOnCooldown(p_9268_.getItem()))) { -+ if (event.getUseItem() == net.neoforged.bus.api.Event.Result.DENY) return InteractionResult.PASS; ++ if (event.getUseItem().isTrue() || (!p_9268_.isEmpty() && !p_9266_.getCooldowns().isOnCooldown(p_9268_.getItem()))) { ++ if (event.getUseItem().isFalse()) return InteractionResult.PASS; InteractionResult interactionresult1; if (this.isCreative()) { int i = p_9268_.getCount(); diff --git a/patches/net/minecraft/world/entity/Entity.java.patch b/patches/net/minecraft/world/entity/Entity.java.patch index 7ac6cab039..8d19921228 100644 --- a/patches/net/minecraft/world/entity/Entity.java.patch +++ b/patches/net/minecraft/world/entity/Entity.java.patch @@ -520,7 +520,7 @@ + public boolean canTrample(BlockState state, BlockPos pos, float fallDistance) { + return level.random.nextFloat() < fallDistance - 0.5F + && this instanceof LivingEntity -+ && (this instanceof Player || net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(level, this)) ++ && (this instanceof Player || net.neoforged.neoforge.event.EventHooks.canEntityGrief(level, this)) + && this.getBbWidth() * this.getBbWidth() * this.getBbHeight() > 0.512F; + } + diff --git a/patches/net/minecraft/world/entity/LivingEntity.java.patch b/patches/net/minecraft/world/entity/LivingEntity.java.patch index 5421a0f39a..aee9094d7d 100644 --- a/patches/net/minecraft/world/entity/LivingEntity.java.patch +++ b/patches/net/minecraft/world/entity/LivingEntity.java.patch @@ -97,7 +97,13 @@ iterator.remove(); } -@@ -975,6 +_,7 @@ +@@ -970,11 +_,12 @@ + } + + public boolean addEffect(MobEffectInstance p_147208_, @Nullable Entity p_147209_) { +- if (!this.canBeAffected(p_147208_)) { ++ if (!net.neoforged.neoforge.common.CommonHooks.canMobEffectBeApplied(this, p_147208_)) { + return false; } else { MobEffectInstance mobeffectinstance = this.activeEffects.get(p_147208_.getEffect()); boolean flag = false; @@ -105,16 +111,30 @@ if (mobeffectinstance == null) { this.activeEffects.put(p_147208_.getEffect(), p_147208_); this.onEffectAdded(p_147208_, p_147209_); -@@ -991,6 +_,9 @@ +@@ -990,6 +_,14 @@ + } } ++ /** ++ * Neo: Override-Only. Call via {@link net.neoforged.neoforge.common.CommonHooks#canMobEffectBeApplied(LivingEntity, MobEffectInstance)} ++ * ++ * @param p_21197_ A mob effect instance ++ * @return If the mob effect instance can be applied to this entity ++ */ ++ @Deprecated ++ @org.jetbrains.annotations.ApiStatus.OverrideOnly public boolean canBeAffected(MobEffectInstance p_21197_) { -+ net.neoforged.neoforge.event.entity.living.MobEffectEvent.Applicable event = new net.neoforged.neoforge.event.entity.living.MobEffectEvent.Applicable(this, p_21197_); -+ net.neoforged.neoforge.common.NeoForge.EVENT_BUS.post(event); -+ if (event.getResult() != net.neoforged.bus.api.Event.Result.DEFAULT) return event.getResult() == net.neoforged.bus.api.Event.Result.ALLOW; if (this.getType().is(EntityTypeTags.IMMUNE_TO_INFESTED)) { return !p_21197_.is(MobEffects.INFESTED); - } else if (this.getType().is(EntityTypeTags.IMMUNE_TO_OOZING)) { +@@ -1003,7 +_,7 @@ + } + + public void forceAddEffect(MobEffectInstance p_147216_, @Nullable Entity p_147217_) { +- if (this.canBeAffected(p_147216_)) { ++ if (net.neoforged.neoforge.common.CommonHooks.canMobEffectBeApplied(this, p_147216_)) { + MobEffectInstance mobeffectinstance = this.activeEffects.put(p_147216_.getEffect(), p_147216_); + if (mobeffectinstance == null) { + this.onEffectAdded(p_147216_, p_147217_); @@ -1024,6 +_,7 @@ } @@ -221,7 +241,7 @@ boolean flag = false; if (p_21269_ instanceof WitherBoss) { - if (this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.level(), p_21269_)) { ++ if (net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.level(), p_21269_)) { BlockPos blockpos = this.blockPosition(); BlockState blockstate = Blocks.WITHER_ROSE.defaultBlockState(); if (this.level().getBlockState(blockpos).isAir() && blockstate.canSurvive(this.level(), blockpos)) { @@ -579,9 +599,9 @@ private boolean checkBedExists() { - return this.getSleepingPos().map(p_337701_ -> this.level().getBlockState(p_337701_).getBlock() instanceof BedBlock).orElse(false); -+ return this.getSleepingPos().map((p_289310_) -> { -+ return net.neoforged.neoforge.event.EventHooks.fireSleepingLocationCheck(this, p_289310_); -+ }).orElse(false); ++ // Neo: Overwrite the vanilla instanceof BedBlock check with isBed and fire the CanContinueSleepingEvent. ++ boolean hasBed = this.getSleepingPos().map(pos -> this.level().getBlockState(pos).isBed(this.level(), pos, this)).orElse(false); ++ return net.neoforged.neoforge.event.EventHooks.canEntityContinueSleeping(this, hasBed ? null : Player.BedSleepingProblem.NOT_POSSIBLE_HERE); } public void stopSleeping() { diff --git a/patches/net/minecraft/world/entity/Mob.java.patch b/patches/net/minecraft/world/entity/Mob.java.patch index 481c013482..1ed422cfa4 100644 --- a/patches/net/minecraft/world/entity/Mob.java.patch +++ b/patches/net/minecraft/world/entity/Mob.java.patch @@ -65,25 +65,18 @@ && this.isAlive() && !this.dead - && this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ && net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.level(), this)) { ++ && net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.level(), this)) { Vec3i vec3i = this.getPickupReach(); for (ItemEntity itementity : this.level() -@@ -736,6 +_,14 @@ +@@ -732,6 +_,7 @@ + + @Override + public void checkDespawn() { ++ if (net.neoforged.neoforge.event.EventHooks.checkMobDespawn(this)) return; + if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) { this.discard(); } else if (!this.isPersistenceRequired() && !this.requiresCustomPersistence()) { - Entity entity = this.level().getNearestPlayer(this, -1.0); -+ net.neoforged.bus.api.Event.Result result = net.neoforged.neoforge.event.EventHooks.canEntityDespawn(this, (ServerLevel) this.level()); -+ if (result == net.neoforged.bus.api.Event.Result.DENY) { -+ noActionTime = 0; -+ entity = null; -+ } else if (result == net.neoforged.bus.api.Event.Result.ALLOW) { -+ this.discard(); -+ entity = null; -+ } - if (entity != null) { - double d0 = entity.distanceToSqr(this); - int i = this.getType().getCategory().getDespawnDistance(); @@ -1135,6 +_,11 @@ } } diff --git a/patches/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java.patch b/patches/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java.patch index 9a34ee81b6..e492a05b83 100644 --- a/patches/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java.patch +++ b/patches/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java.patch @@ -5,7 +5,7 @@ protected boolean checkExtraStartConditions(ServerLevel p_23174_, Villager p_23175_) { - if (!p_23174_.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (!net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(p_23174_, p_23175_)) { ++ if (!net.neoforged.neoforge.event.EventHooks.canEntityGrief(p_23174_, p_23175_)) { return false; } else if (p_23175_.getVillagerData().getProfession() != VillagerProfession.FARMER) { return false; diff --git a/patches/net/minecraft/world/entity/ai/goal/EatBlockGoal.java.patch b/patches/net/minecraft/world/entity/ai/goal/EatBlockGoal.java.patch index 2d2ca4cab1..f817b98919 100644 --- a/patches/net/minecraft/world/entity/ai/goal/EatBlockGoal.java.patch +++ b/patches/net/minecraft/world/entity/ai/goal/EatBlockGoal.java.patch @@ -5,7 +5,7 @@ BlockPos blockpos = this.mob.blockPosition(); if (IS_TALL_GRASS.test(this.level.getBlockState(blockpos))) { - if (this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.level, this.mob)) { ++ if (net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.level, this.mob)) { this.level.destroyBlock(blockpos, false); } @@ -14,7 +14,7 @@ BlockPos blockpos1 = blockpos.below(); if (this.level.getBlockState(blockpos1).is(Blocks.GRASS_BLOCK)) { - if (this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.level, this.mob)) { ++ if (net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.level, this.mob)) { this.level.levelEvent(2001, blockpos1, Block.getId(Blocks.GRASS_BLOCK.defaultBlockState())); this.level.setBlock(blockpos1, Blocks.DIRT.defaultBlockState(), 2); } diff --git a/patches/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java.patch b/patches/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java.patch index e5945226c9..276d4f5010 100644 --- a/patches/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java.patch +++ b/patches/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java.patch @@ -5,7 +5,7 @@ @Override public boolean canUse() { - if (!this.removerMob.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (!net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.removerMob.level(), this.removerMob)) { ++ if (!net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.removerMob.level(), this.removerMob)) { return false; } else if (this.nextStartTick > 0) { this.nextStartTick--; diff --git a/patches/net/minecraft/world/entity/animal/Fox.java.patch b/patches/net/minecraft/world/entity/animal/Fox.java.patch index 82eb17b1f9..30af9f3a1e 100644 --- a/patches/net/minecraft/world/entity/animal/Fox.java.patch +++ b/patches/net/minecraft/world/entity/animal/Fox.java.patch @@ -41,7 +41,7 @@ protected void onReachedTarget() { - if (Fox.this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(Fox.this.level(), Fox.this)) { ++ if (net.neoforged.neoforge.event.EventHooks.canEntityGrief(Fox.this.level(), Fox.this)) { BlockState blockstate = Fox.this.level().getBlockState(this.blockPos); if (blockstate.is(Blocks.SWEET_BERRY_BUSH)) { this.pickSweetBerries(blockstate); diff --git a/patches/net/minecraft/world/entity/animal/Rabbit.java.patch b/patches/net/minecraft/world/entity/animal/Rabbit.java.patch index b71d6d55bd..d0837c7cf5 100644 --- a/patches/net/minecraft/world/entity/animal/Rabbit.java.patch +++ b/patches/net/minecraft/world/entity/animal/Rabbit.java.patch @@ -5,7 +5,7 @@ public boolean canUse() { if (this.nextStartTick <= 0) { - if (!this.rabbit.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (!net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.rabbit.level(), this.rabbit)) { ++ if (!net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.rabbit.level(), this.rabbit)) { return false; } diff --git a/patches/net/minecraft/world/entity/animal/SnowGolem.java.patch b/patches/net/minecraft/world/entity/animal/SnowGolem.java.patch index 907cd15285..3232426b5f 100644 --- a/patches/net/minecraft/world/entity/animal/SnowGolem.java.patch +++ b/patches/net/minecraft/world/entity/animal/SnowGolem.java.patch @@ -5,7 +5,7 @@ } - if (!this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (!net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.level(), this)) { ++ if (!net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.level(), this)) { return; } diff --git a/patches/net/minecraft/world/entity/animal/allay/Allay.java.patch b/patches/net/minecraft/world/entity/animal/allay/Allay.java.patch index c11160e1b8..4a5d0dd804 100644 --- a/patches/net/minecraft/world/entity/animal/allay/Allay.java.patch +++ b/patches/net/minecraft/world/entity/animal/allay/Allay.java.patch @@ -8,7 +8,7 @@ && this.inventory.canAddItem(p_218387_) - && this.allayConsidersItemEqual(itemstack, p_218387_); + && this.allayConsidersItemEqual(itemstack, p_218387_) -+ && net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.level(), this); ++ && net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.level(), this); } private boolean allayConsidersItemEqual(ItemStack p_252278_, ItemStack p_250405_) { diff --git a/patches/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch b/patches/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch index 3ac2613fb4..41d0e7a880 100644 --- a/patches/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch +++ b/patches/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch @@ -5,7 +5,7 @@ if (this.destroyBlocksTick > 0) { this.destroyBlocksTick--; - if (this.destroyBlocksTick == 0 && this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (this.destroyBlocksTick == 0 && net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.level(), this)) { ++ if (this.destroyBlocksTick == 0 && net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.level(), this)) { boolean flag = false; int l = Mth.floor(this.getBbWidth() / 2.0F + 1.0F); int i1 = Mth.floor(this.getBbHeight()); @@ -29,3 +29,11 @@ public static boolean canDestroy(BlockState p_31492_) { return !p_31492_.isAir() && !p_31492_.is(BlockTags.WITHER_IMMUNE); } +@@ -493,6 +_,7 @@ + + @Override + public void checkDespawn() { ++ if (net.neoforged.neoforge.event.EventHooks.checkMobDespawn(this)) return; + if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) { + this.discard(); + } else { diff --git a/patches/net/minecraft/world/entity/item/ItemEntity.java.patch b/patches/net/minecraft/world/entity/item/ItemEntity.java.patch index ef93c50397..19842bd249 100644 --- a/patches/net/minecraft/world/entity/item/ItemEntity.java.patch +++ b/patches/net/minecraft/world/entity/item/ItemEntity.java.patch @@ -100,22 +100,27 @@ if (p_32034_.hasUUID("Owner")) { this.target = p_32034_.getUUID("Owner"); -@@ -339,10 +_,17 @@ - @Override - public void playerTouch(Player p_32040_) { - if (!this.level().isClientSide) { -+ if (this.pickupDelay > 0) return; +@@ -342,7 +_,22 @@ ItemStack itemstack = this.getItem(); Item item = itemstack.getItem(); int i = itemstack.getCount(); - if (this.pickupDelay == 0 && (this.target == null || this.target.equals(p_32040_.getUUID())) && p_32040_.getInventory().add(itemstack)) { -+ int hook = net.neoforged.neoforge.event.EventHooks.onItemPickup(this, p_32040_); -+ if (hook < 0) return; -+ ItemStack copy = itemstack.copy(); -+ if (this.pickupDelay == 0 && (this.target == null || this.target.equals(p_32040_.getUUID())) && (hook == 1 || i <= 0 || p_32040_.getInventory().add(itemstack))) { -+ i = copy.getCount() - itemstack.getCount(); -+ copy.setCount(i); -+ net.neoforged.neoforge.event.EventHooks.firePlayerItemPickupEvent(p_32040_, this, copy); ++ ++ // Neo: Fire item pickup pre/post and adjust handling logic to adhere to the event result. ++ var result = net.neoforged.neoforge.event.EventHooks.fireItemPickupPre(this, p_32040_).canPickup(); ++ if (result.isFalse()) { ++ return; ++ } ++ ++ // Make a copy of the original stack for use in ItemEntityPickupEvent.Post ++ ItemStack originalCopy = itemstack.copy(); ++ // Subvert the vanilla conditions (pickup delay and target check) if the result is true. ++ if ((result.isTrue() || this.pickupDelay == 0 && (this.target == null || this.target.equals(p_32040_.getUUID()))) && p_32040_.getInventory().add(itemstack)) { ++ // Fire ItemEntityPickupEvent.Post ++ net.neoforged.neoforge.event.EventHooks.fireItemPickupPost(this, p_32040_, originalCopy); ++ // Update `i` to reflect the actual pickup amount. Vanilla is wrong here and always reports the whole stack. ++ i = originalCopy.getCount() - itemstack.getCount(); ++ p_32040_.take(this, i); if (itemstack.isEmpty()) { this.discard(); diff --git a/patches/net/minecraft/world/entity/monster/EnderMan.java.patch b/patches/net/minecraft/world/entity/monster/EnderMan.java.patch index 425799d7ab..15116180df 100644 --- a/patches/net/minecraft/world/entity/monster/EnderMan.java.patch +++ b/patches/net/minecraft/world/entity/monster/EnderMan.java.patch @@ -42,7 +42,7 @@ return false; } else { - return !this.enderman.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) -+ return !net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.enderman.level(), this.enderman) ++ return !net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.enderman.level(), this.enderman) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0; } @@ -68,7 +68,7 @@ return false; } else { - return !this.enderman.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) -+ return !net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.enderman.level(), this.enderman) ++ return !net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.enderman.level(), this.enderman) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0; } diff --git a/patches/net/minecraft/world/entity/monster/Evoker.java.patch b/patches/net/minecraft/world/entity/monster/Evoker.java.patch index a7549041ac..a0a972ccac 100644 --- a/patches/net/minecraft/world/entity/monster/Evoker.java.patch +++ b/patches/net/minecraft/world/entity/monster/Evoker.java.patch @@ -5,7 +5,7 @@ } else if (Evoker.this.tickCount < this.nextAttackTickCount) { return false; - } else if (!Evoker.this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ } else if (!net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(Evoker.this.level(), Evoker.this)) { ++ } else if (!net.neoforged.neoforge.event.EventHooks.canEntityGrief(Evoker.this.level(), Evoker.this)) { return false; } else { List list = Evoker.this.level() diff --git a/patches/net/minecraft/world/entity/monster/Ravager.java.patch b/patches/net/minecraft/world/entity/monster/Ravager.java.patch index 50f6d92831..a0a55e4739 100644 --- a/patches/net/minecraft/world/entity/monster/Ravager.java.patch +++ b/patches/net/minecraft/world/entity/monster/Ravager.java.patch @@ -5,7 +5,7 @@ } - if (this.horizontalCollision && this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (this.horizontalCollision && net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.level(), this)) { ++ if (this.horizontalCollision && net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.level(), this)) { boolean flag = false; AABB aabb = this.getBoundingBox().inflate(0.2); diff --git a/patches/net/minecraft/world/entity/monster/Silverfish.java.patch b/patches/net/minecraft/world/entity/monster/Silverfish.java.patch index 7e73ef502a..41ef0fa648 100644 --- a/patches/net/minecraft/world/entity/monster/Silverfish.java.patch +++ b/patches/net/minecraft/world/entity/monster/Silverfish.java.patch @@ -5,7 +5,7 @@ } else { RandomSource randomsource = this.mob.getRandom(); - if (this.mob.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && randomsource.nextInt(reducedTickDelay(10)) == 0) { -+ if (net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.mob.level(), this.mob) && randomsource.nextInt(reducedTickDelay(10)) == 0) { ++ if (net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.mob.level(), this.mob) && randomsource.nextInt(reducedTickDelay(10)) == 0) { this.selectedDirection = Direction.getRandom(randomsource); BlockPos blockpos = BlockPos.containing(this.mob.getX(), this.mob.getY() + 0.5, this.mob.getZ()).relative(this.selectedDirection); BlockState blockstate = this.mob.level().getBlockState(blockpos); @@ -14,7 +14,7 @@ Block block = blockstate.getBlock(); if (block instanceof InfestedBlock) { - if (level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(level, this.silverfish)) { ++ if (net.neoforged.neoforge.event.EventHooks.canEntityGrief(level, this.silverfish)) { level.destroyBlock(blockpos1, true, this.silverfish); } else { level.setBlock(blockpos1, ((InfestedBlock)block).hostStateByInfested(level.getBlockState(blockpos1)), 3); diff --git a/patches/net/minecraft/world/entity/monster/Spider.java.patch b/patches/net/minecraft/world/entity/monster/Spider.java.patch deleted file mode 100644 index e6368793de..0000000000 --- a/patches/net/minecraft/world/entity/monster/Spider.java.patch +++ /dev/null @@ -1,16 +0,0 @@ ---- a/net/minecraft/world/entity/monster/Spider.java -+++ b/net/minecraft/world/entity/monster/Spider.java -@@ -121,7 +_,12 @@ - - @Override - public boolean canBeAffected(MobEffectInstance p_33809_) { -- return p_33809_.is(MobEffects.POISON) ? false : super.canBeAffected(p_33809_); -+ if (p_33809_.getEffect() == MobEffects.POISON) { -+ net.neoforged.neoforge.event.entity.living.MobEffectEvent.Applicable event = new net.neoforged.neoforge.event.entity.living.MobEffectEvent.Applicable(this, p_33809_); -+ net.neoforged.neoforge.common.NeoForge.EVENT_BUS.post(event); -+ return event.getResult() == net.neoforged.bus.api.Event.Result.ALLOW; -+ } -+ return super.canBeAffected(p_33809_); - } - - public boolean isClimbing() { diff --git a/patches/net/minecraft/world/entity/monster/Zombie.java.patch b/patches/net/minecraft/world/entity/monster/Zombie.java.patch index 49e8c23de4..40cb5726d5 100644 --- a/patches/net/minecraft/world/entity/monster/Zombie.java.patch +++ b/patches/net/minecraft/world/entity/monster/Zombie.java.patch @@ -16,45 +16,6 @@ } } -@@ -282,14 +_,19 @@ - livingentity = (LivingEntity)p_34288_.getEntity(); - } - -- if (livingentity != null -- && this.level().getDifficulty() == Difficulty.HARD -- && (double)this.random.nextFloat() < this.getAttributeValue(Attributes.SPAWN_REINFORCEMENTS_CHANCE) -- && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) { -- int i = Mth.floor(this.getX()); -- int j = Mth.floor(this.getY()); -- int k = Mth.floor(this.getZ()); -- Zombie zombie = new Zombie(this.level()); -+ int i = Mth.floor(this.getX()); -+ int j = Mth.floor(this.getY()); -+ int k = Mth.floor(this.getZ()); -+ -+ net.neoforged.neoforge.event.entity.living.ZombieEvent.SummonAidEvent event = net.neoforged.neoforge.event.EventHooks.fireZombieSummonAid(this, level(), i, j, k, livingentity, this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).getValue()); -+ if (event.getResult() == net.neoforged.bus.api.Event.Result.DENY) return true; -+ if (event.getResult() == net.neoforged.bus.api.Event.Result.ALLOW || -+ (livingentity != null -+ && this.level().getDifficulty() == Difficulty.HARD -+ && (double)this.random.nextFloat() < this.getAttributeValue(Attributes.SPAWN_REINFORCEMENTS_CHANCE) -+ && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING))) { -+ -+ Zombie zombie = event.getCustomSummonedAid() != null && event.getResult() == net.neoforged.bus.api.Event.Result.ALLOW ? event.getCustomSummonedAid() : EntityType.ZOMBIE.create(this.level()); - - for (int l = 0; l < 50; l++) { - int i1 = i + Mth.nextInt(this.random, 7, 40) * Mth.nextInt(this.random, -1, 1); -@@ -304,7 +_,9 @@ - && this.level().isUnobstructed(zombie) - && this.level().noCollision(zombie) - && !this.level().containsAnyLiquid(zombie.getBoundingBox())) { -- zombie.setTarget(livingentity); -+ if (livingentity != null) { -+ zombie.setTarget(livingentity); -+ } - zombie.finalizeSpawn(serverlevel, this.level().getCurrentDifficultyAt(zombie.blockPosition()), MobSpawnType.REINFORCEMENT, null); - serverlevel.addFreshEntityWithPassengers(zombie); - this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE) @@ -398,7 +_,7 @@ @Override public boolean killedEntity(ServerLevel p_219160_, LivingEntity p_219161_) { diff --git a/patches/net/minecraft/world/entity/monster/piglin/Piglin.java.patch b/patches/net/minecraft/world/entity/monster/piglin/Piglin.java.patch index 7177a80222..8bb786c6e0 100644 --- a/patches/net/minecraft/world/entity/monster/piglin/Piglin.java.patch +++ b/patches/net/minecraft/world/entity/monster/piglin/Piglin.java.patch @@ -23,7 +23,7 @@ @Override public boolean wantsToPickUp(ItemStack p_34777_) { - return this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, p_34777_); -+ return net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.level(), this) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, p_34777_); ++ return net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.level(), this) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, p_34777_); } protected boolean canReplaceCurrentItem(ItemStack p_34788_) { diff --git a/patches/net/minecraft/world/entity/player/Player.java.patch b/patches/net/minecraft/world/entity/player/Player.java.patch index 9d7b70f5a1..fc14f137ab 100644 --- a/patches/net/minecraft/world/entity/player/Player.java.patch +++ b/patches/net/minecraft/world/entity/player/Player.java.patch @@ -44,7 +44,7 @@ } - if (!this.level().isClientSide && this.level().isDay()) { -+ if (!this.level().isClientSide && !net.neoforged.neoforge.event.EventHooks.fireSleepingTimeCheck(this, getSleepingPos())) { ++ if (!this.level().isClientSide && !net.neoforged.neoforge.event.EventHooks.canEntityContinueSleeping(this, this.level().isDay() ? BedSleepingProblem.NOT_POSSIBLE_NOW : null)) { this.stopSleepInBed(false, true); } } else if (this.sleepCounter > 0) { @@ -241,15 +241,17 @@ i += EnchantmentHelper.getKnockbackBonus(this); if (this.isSprinting() && flag) { this.level() -@@ -1162,8 +_,10 @@ +@@ -1162,8 +_,12 @@ && !this.isPassenger() && p_36347_ instanceof LivingEntity && !this.isSprinting(); -+ net.neoforged.neoforge.event.entity.player.CriticalHitEvent hitResult = net.neoforged.neoforge.common.CommonHooks.getCriticalHit(this, p_36347_, flag2, flag2 ? 1.5F : 1.0F); -+ flag2 = hitResult != null; ++ // Neo: Fire the critical hit event and override the critical hit status and damage multiplier based on the event. ++ // The boolean local above (flag2) is the vanilla critical hit result. ++ var critEvent = net.neoforged.neoforge.common.CommonHooks.fireCriticalHit(this, p_36347_, flag2, flag2 ? 1.5F : 1.0F); ++ flag2 = critEvent.isCriticalHit(); if (flag2) { - f *= 1.5F; -+ f *= hitResult.getDamageModifier(); ++ f *= critEvent.getDamageMultiplier(); } f += f1; @@ -326,7 +328,7 @@ return BedBlock.findStandUpPosition(EntityType.PLAYER, p_36131_, p_36132_, blockstate.getValue(BedBlock.FACING), p_36133_); } else if (!p_36134_) { - return Optional.empty(); -+ return blockstate.getRespawnPosition(EntityType.PLAYER, p_36131_, p_36132_, p_36133_, null); ++ return blockstate.getRespawnPosition(EntityType.PLAYER, p_36131_, p_36132_, p_36133_); } else { boolean flag = block.isPossibleToRespawnInThis(blockstate); BlockState blockstate1 = p_36131_.getBlockState(p_36132_.above()); diff --git a/patches/net/minecraft/world/entity/projectile/LargeFireball.java.patch b/patches/net/minecraft/world/entity/projectile/LargeFireball.java.patch index 9508077661..466b8709a3 100644 --- a/patches/net/minecraft/world/entity/projectile/LargeFireball.java.patch +++ b/patches/net/minecraft/world/entity/projectile/LargeFireball.java.patch @@ -6,7 +6,7 @@ if (!this.level().isClientSide) { - boolean flag = this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); + // TODO 1.19.3: The creation of Level.ExplosionInteraction means this code path will fire EntityMobGriefingEvent twice. Should we try and fix it? -SS -+ boolean flag = net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.level(), this.getOwner()); ++ boolean flag = net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.level(), this.getOwner()); this.level().explode(this, this.getX(), this.getY(), this.getZ(), (float)this.explosionPower, flag, Level.ExplosionInteraction.MOB); this.discard(); } diff --git a/patches/net/minecraft/world/entity/projectile/Projectile.java.patch b/patches/net/minecraft/world/entity/projectile/Projectile.java.patch index beda4d0988..4d1b5b44a5 100644 --- a/patches/net/minecraft/world/entity/projectile/Projectile.java.patch +++ b/patches/net/minecraft/world/entity/projectile/Projectile.java.patch @@ -5,7 +5,7 @@ return entity instanceof Player ? entity.mayInteract(p_150167_, p_150168_) - : entity == null || p_150167_.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); -+ : entity == null || net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(p_150167_, entity); ++ : entity == null || net.neoforged.neoforge.event.EventHooks.canEntityGrief(p_150167_, entity); } public boolean mayBreak(Level p_307481_) { diff --git a/patches/net/minecraft/world/entity/projectile/SmallFireball.java.patch b/patches/net/minecraft/world/entity/projectile/SmallFireball.java.patch index 06132ff570..ae18509d63 100644 --- a/patches/net/minecraft/world/entity/projectile/SmallFireball.java.patch +++ b/patches/net/minecraft/world/entity/projectile/SmallFireball.java.patch @@ -5,7 +5,7 @@ if (!this.level().isClientSide) { Entity entity = this.getOwner(); - if (!(entity instanceof Mob) || this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (!(entity instanceof Mob) || net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this.level(), entity)) { ++ if (!(entity instanceof Mob) || net.neoforged.neoforge.event.EventHooks.canEntityGrief(this.level(), entity)) { BlockPos blockpos = p_37384_.getBlockPos().relative(p_37384_.getDirection()); if (this.level().isEmptyBlock(blockpos)) { this.level().setBlockAndUpdate(blockpos, BaseFireBlock.getState(this.level(), blockpos)); diff --git a/patches/net/minecraft/world/item/BoneMealItem.java.patch b/patches/net/minecraft/world/item/BoneMealItem.java.patch index 5cbc49f60f..6e9b04c0ea 100644 --- a/patches/net/minecraft/world/item/BoneMealItem.java.patch +++ b/patches/net/minecraft/world/item/BoneMealItem.java.patch @@ -16,14 +16,14 @@ + @Deprecated //Forge: Use Player/Hand version public static boolean growCrop(ItemStack p_40628_, Level p_40629_, BlockPos p_40630_) { + if (p_40629_ instanceof net.minecraft.server.level.ServerLevel) -+ return applyBonemeal(p_40628_, p_40629_, p_40630_, net.neoforged.neoforge.common.util.FakePlayerFactory.getMinecraft((net.minecraft.server.level.ServerLevel)p_40629_)); ++ return applyBonemeal(p_40628_, p_40629_, p_40630_, null); + return false; + } + -+ public static boolean applyBonemeal(ItemStack p_40628_, Level p_40629_, BlockPos p_40630_, net.minecraft.world.entity.player.Player player) { ++ public static boolean applyBonemeal(ItemStack p_40628_, Level p_40629_, BlockPos p_40630_, @Nullable net.minecraft.world.entity.player.Player player) { BlockState blockstate = p_40629_.getBlockState(p_40630_); -+ int hook = net.neoforged.neoforge.event.EventHooks.onApplyBonemeal(player, p_40629_, p_40630_, blockstate, p_40628_); -+ if (hook != 0) return hook > 0; ++ var event = net.neoforged.neoforge.event.EventHooks.fireBonemealEvent(player, p_40629_, p_40630_, blockstate, p_40628_); ++ if (event.isCanceled()) return event.isSuccessful(); if (blockstate.getBlock() instanceof BonemealableBlock bonemealableblock && bonemealableblock.isValidBonemealTarget(p_40629_, p_40630_, blockstate)) { if (p_40629_ instanceof ServerLevel) { if (bonemealableblock.isBonemealSuccess(p_40629_, p_40629_.random, p_40630_, blockstate)) { diff --git a/patches/net/minecraft/world/item/BucketItem.java.patch b/patches/net/minecraft/world/item/BucketItem.java.patch index a01af4ecbc..2691ea1bec 100644 --- a/patches/net/minecraft/world/item/BucketItem.java.patch +++ b/patches/net/minecraft/world/item/BucketItem.java.patch @@ -1,14 +1,5 @@ --- a/net/minecraft/world/item/BucketItem.java +++ b/net/minecraft/world/item/BucketItem.java -@@ -42,6 +_,8 @@ - BlockHitResult blockhitresult = getPlayerPOVHitResult( - p_40703_, p_40704_, this.content == Fluids.EMPTY ? ClipContext.Fluid.SOURCE_ONLY : ClipContext.Fluid.NONE - ); -+ InteractionResultHolder ret = net.neoforged.neoforge.event.EventHooks.onBucketUse(p_40704_, p_40703_, itemstack, blockhitresult); -+ if (ret != null) return ret; - if (blockhitresult.getType() == HitResult.Type.MISS) { - return InteractionResultHolder.pass(itemstack); - } else if (blockhitresult.getType() != HitResult.Type.BLOCK) { @@ -58,7 +_,7 @@ ItemStack itemstack2 = bucketpickup.pickupBlock(p_40704_, p_40703_, blockpos, blockstate1); if (!itemstack2.isEmpty()) { diff --git a/patches/net/minecraft/world/level/Level.java.patch b/patches/net/minecraft/world/level/Level.java.patch index 0a588ca277..28cc0988ac 100644 --- a/patches/net/minecraft/world/level/Level.java.patch +++ b/patches/net/minecraft/world/level/Level.java.patch @@ -133,7 +133,7 @@ case NONE -> Explosion.BlockInteraction.KEEP; case BLOCK -> this.getDestroyType(GameRules.RULE_BLOCK_EXPLOSION_DROP_DECAY); - case MOB -> this.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) -+ case MOB -> net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(this, p_311934_) ++ case MOB -> net.neoforged.neoforge.event.EventHooks.canEntityGrief(this, p_311934_) ? this.getDestroyType(GameRules.RULE_MOB_EXPLOSION_DROP_DECAY) : Explosion.BlockInteraction.KEEP; case TNT -> this.getDestroyType(GameRules.RULE_TNT_EXPLOSION_DROP_DECAY); diff --git a/patches/net/minecraft/world/level/NaturalSpawner.java.patch b/patches/net/minecraft/world/level/NaturalSpawner.java.patch index 34b083ee3b..53f0facd7e 100644 --- a/patches/net/minecraft/world/level/NaturalSpawner.java.patch +++ b/patches/net/minecraft/world/level/NaturalSpawner.java.patch @@ -22,7 +22,7 @@ p_47040_.addFreshEntityWithPassengers(mob); p_47044_.run(mob, p_47041_); - if (j >= mob.getMaxSpawnClusterSize()) { -+ if (j >= net.neoforged.neoforge.event.EventHooks.getMaxSpawnPackSize(mob)) { ++ if (j >= net.neoforged.neoforge.event.EventHooks.getMaxSpawnClusterSize(mob)) { return; } diff --git a/patches/net/minecraft/world/level/block/BambooStalkBlock.java.patch b/patches/net/minecraft/world/level/block/BambooStalkBlock.java.patch index 82c3d8dce3..6f3cef6059 100644 --- a/patches/net/minecraft/world/level/block/BambooStalkBlock.java.patch +++ b/patches/net/minecraft/world/level/block/BambooStalkBlock.java.patch @@ -17,9 +17,9 @@ + if (p_261751_.isEmptyBlock(p_261616_.above()) && p_261751_.getRawBrightness(p_261616_.above(), 0) >= 9) { int i = this.getHeightBelowUpToMax(p_261751_, p_261616_) + 1; - if (i < 16) { -+ if (i < 16 && net.neoforged.neoforge.common.CommonHooks.onCropsGrowPre(p_261751_, p_261616_, p_261931_, p_261766_.nextInt(3) == 0)) { ++ if (i < 16 && net.neoforged.neoforge.common.CommonHooks.canCropGrow(p_261751_, p_261616_, p_261931_, p_261766_.nextInt(3) == 0)) { this.growBamboo(p_261931_, p_261751_, p_261616_, p_261766_, i); -+ net.neoforged.neoforge.common.CommonHooks.onCropsGrowPost(p_261751_, p_261616_, p_261931_); ++ net.neoforged.neoforge.common.CommonHooks.fireCropGrowPost(p_261751_, p_261616_, p_261931_); } } } diff --git a/patches/net/minecraft/world/level/block/CactusBlock.java.patch b/patches/net/minecraft/world/level/block/CactusBlock.java.patch index 10a127e82f..13ecdb3020 100644 --- a/patches/net/minecraft/world/level/block/CactusBlock.java.patch +++ b/patches/net/minecraft/world/level/block/CactusBlock.java.patch @@ -21,7 +21,7 @@ if (i < 3) { int j = p_220913_.getValue(AGE); -+ if(net.neoforged.neoforge.common.CommonHooks.onCropsGrowPre(p_220914_, blockpos, p_220913_, true)) { ++ if(net.neoforged.neoforge.common.CommonHooks.canCropGrow(p_220914_, blockpos, p_220913_, true)) { if (j == 15) { p_220914_.setBlockAndUpdate(blockpos, this.defaultBlockState()); BlockState blockstate = p_220913_.setValue(AGE, Integer.valueOf(0)); @@ -29,7 +29,7 @@ } else { p_220914_.setBlock(p_220915_, p_220913_.setValue(AGE, Integer.valueOf(j + 1)), 4); } -+ net.neoforged.neoforge.common.CommonHooks.onCropsGrowPost(p_220914_, p_220915_, p_220913_); ++ net.neoforged.neoforge.common.CommonHooks.fireCropGrowPost(p_220914_, p_220915_, p_220913_); + } } } diff --git a/patches/net/minecraft/world/level/block/ChorusFlowerBlock.java.patch b/patches/net/minecraft/world/level/block/ChorusFlowerBlock.java.patch index 4074d2ecab..4209cc2f40 100644 --- a/patches/net/minecraft/world/level/block/ChorusFlowerBlock.java.patch +++ b/patches/net/minecraft/world/level/block/ChorusFlowerBlock.java.patch @@ -5,7 +5,7 @@ if (p_220981_.isEmptyBlock(blockpos) && blockpos.getY() < p_220981_.getMaxBuildHeight()) { int i = p_220980_.getValue(AGE); - if (i < 5) { -+ if (i < 5 && net.neoforged.neoforge.common.CommonHooks.onCropsGrowPre(p_220981_, blockpos, p_220980_, true)) { ++ if (i < 5 && net.neoforged.neoforge.common.CommonHooks.canCropGrow(p_220981_, blockpos, p_220980_, true)) { boolean flag = false; boolean flag1 = false; BlockState blockstate = p_220981_.getBlockState(p_220982_.below()); @@ -13,7 +13,7 @@ } else { this.placeDeadFlower(p_220981_, p_220982_); } -+ net.neoforged.neoforge.common.CommonHooks.onCropsGrowPost(p_220981_, p_220982_, p_220980_); ++ net.neoforged.neoforge.common.CommonHooks.fireCropGrowPost(p_220981_, p_220982_, p_220980_); } } } diff --git a/patches/net/minecraft/world/level/block/CocoaBlock.java.patch b/patches/net/minecraft/world/level/block/CocoaBlock.java.patch index d004d4e3d8..9ba1d136c0 100644 --- a/patches/net/minecraft/world/level/block/CocoaBlock.java.patch +++ b/patches/net/minecraft/world/level/block/CocoaBlock.java.patch @@ -8,9 +8,9 @@ + if (true) { int i = p_221000_.getValue(AGE); - if (i < 2) { -+ if (i < 2 && net.neoforged.neoforge.common.CommonHooks.onCropsGrowPre(p_221001_, p_221002_, p_221000_, p_221001_.random.nextInt(5) == 0)) { ++ if (i < 2 && net.neoforged.neoforge.common.CommonHooks.canCropGrow(p_221001_, p_221002_, p_221000_, p_221001_.random.nextInt(5) == 0)) { p_221001_.setBlock(p_221002_, p_221000_.setValue(AGE, Integer.valueOf(i + 1)), 2); -+ net.neoforged.neoforge.common.CommonHooks.onCropsGrowPost(p_221001_, p_221002_, p_221000_); ++ net.neoforged.neoforge.common.CommonHooks.fireCropGrowPost(p_221001_, p_221002_, p_221000_); } } } diff --git a/patches/net/minecraft/world/level/block/CropBlock.java.patch b/patches/net/minecraft/world/level/block/CropBlock.java.patch index 30100c78d6..6f71741140 100644 --- a/patches/net/minecraft/world/level/block/CropBlock.java.patch +++ b/patches/net/minecraft/world/level/block/CropBlock.java.patch @@ -10,9 +10,9 @@ if (i < this.getMaxAge()) { float f = getGrowthSpeed(this, p_221051_, p_221052_); - if (p_221053_.nextInt((int)(25.0F / f) + 1) == 0) { -+ if (net.neoforged.neoforge.common.CommonHooks.onCropsGrowPre(p_221051_, p_221052_, p_221050_, p_221053_.nextInt((int)(25.0F / f) + 1) == 0)) { ++ if (net.neoforged.neoforge.common.CommonHooks.canCropGrow(p_221051_, p_221052_, p_221050_, p_221053_.nextInt((int)(25.0F / f) + 1) == 0)) { p_221051_.setBlock(p_221052_, this.getStateForAge(i + 1), 2); -+ net.neoforged.neoforge.common.CommonHooks.onCropsGrowPost(p_221051_, p_221052_, p_221050_); ++ net.neoforged.neoforge.common.CommonHooks.fireCropGrowPost(p_221051_, p_221052_, p_221050_); } } } @@ -33,7 +33,7 @@ @Override protected void entityInside(BlockState p_52277_, Level p_52278_, BlockPos p_52279_, Entity p_52280_) { - if (p_52280_ instanceof Ravager && p_52278_.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (p_52280_ instanceof Ravager && net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(p_52278_, p_52280_)) { ++ if (p_52280_ instanceof Ravager && net.neoforged.neoforge.event.EventHooks.canEntityGrief(p_52278_, p_52280_)) { p_52278_.destroyBlock(p_52279_, true, p_52280_); } diff --git a/patches/net/minecraft/world/level/block/FungusBlock.java.patch b/patches/net/minecraft/world/level/block/FungusBlock.java.patch index 0d1197f8a8..062861486c 100644 --- a/patches/net/minecraft/world/level/block/FungusBlock.java.patch +++ b/patches/net/minecraft/world/level/block/FungusBlock.java.patch @@ -1,14 +1,13 @@ --- a/net/minecraft/world/level/block/FungusBlock.java +++ b/net/minecraft/world/level/block/FungusBlock.java -@@ -73,6 +_,10 @@ +@@ -73,6 +_,9 @@ @Override public void performBonemeal(ServerLevel p_221243_, RandomSource p_221244_, BlockPos p_221245_, BlockState p_221246_) { - this.getFeature(p_221243_).ifPresent(p_256352_ -> p_256352_.value().place(p_221243_, p_221243_.getChunkSource().getGenerator(), p_221244_, p_221245_)); -+ this.getFeature(p_221243_).ifPresent(p_256352_ -> { -+ var event = net.neoforged.neoforge.event.EventHooks.blockGrowFeature(p_221243_, p_221244_, p_221245_, p_256352_); -+ if (event.getResult().equals(net.neoforged.bus.api.Event.Result.DENY)) return; -+ event.getFeature().value().place(p_221243_, p_221243_.getChunkSource().getGenerator(), p_221244_, p_221245_); -+ }); ++ // Neo: Fire the BlockGrowFeatureEvent and change the ifPresent call to use the event's result. ++ var event = net.neoforged.neoforge.event.EventHooks.fireBlockGrowFeature(p_221243_, p_221244_, p_221245_, this.getFeature(p_221243_).orElse(null)); ++ if (event.isCanceled()) return; ++ Optional.ofNullable(event.getFeature()).ifPresent(p_256352_ -> p_256352_.value().place(p_221243_, p_221243_.getChunkSource().getGenerator(), p_221244_, p_221245_)); } } diff --git a/patches/net/minecraft/world/level/block/GrowingPlantHeadBlock.java.patch b/patches/net/minecraft/world/level/block/GrowingPlantHeadBlock.java.patch index aaeafb00a4..a0cdaee4d2 100644 --- a/patches/net/minecraft/world/level/block/GrowingPlantHeadBlock.java.patch +++ b/patches/net/minecraft/world/level/block/GrowingPlantHeadBlock.java.patch @@ -5,11 +5,11 @@ @Override protected void randomTick(BlockState p_221350_, ServerLevel p_221351_, BlockPos p_221352_, RandomSource p_221353_) { - if (p_221350_.getValue(AGE) < 25 && p_221353_.nextDouble() < this.growPerTickProbability) { -+ if (p_221350_.getValue(AGE) < 25 && net.neoforged.neoforge.common.CommonHooks.onCropsGrowPre(p_221351_, p_221352_.relative(this.growthDirection), p_221350_, p_221353_.nextDouble() < this.growPerTickProbability)) { ++ if (p_221350_.getValue(AGE) < 25 && net.neoforged.neoforge.common.CommonHooks.canCropGrow(p_221351_, p_221352_.relative(this.growthDirection), p_221350_, p_221353_.nextDouble() < this.growPerTickProbability)) { BlockPos blockpos = p_221352_.relative(this.growthDirection); if (this.canGrowInto(p_221351_.getBlockState(blockpos))) { p_221351_.setBlockAndUpdate(blockpos, this.getGrowIntoState(p_221350_, p_221351_.random)); -+ net.neoforged.neoforge.common.CommonHooks.onCropsGrowPost(p_221351_, blockpos, p_221351_.getBlockState(blockpos)); ++ net.neoforged.neoforge.common.CommonHooks.fireCropGrowPost(p_221351_, blockpos, p_221351_.getBlockState(blockpos)); } } } diff --git a/patches/net/minecraft/world/level/block/MushroomBlock.java.patch b/patches/net/minecraft/world/level/block/MushroomBlock.java.patch index 7c60905760..e39f3d2ee6 100644 --- a/patches/net/minecraft/world/level/block/MushroomBlock.java.patch +++ b/patches/net/minecraft/world/level/block/MushroomBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/MushroomBlock.java +++ b/net/minecraft/world/level/block/MushroomBlock.java -@@ -87,7 +_,7 @@ +@@ -87,13 +_,21 @@ BlockState blockstate = p_54881_.getBlockState(blockpos); return blockstate.is(BlockTags.MUSHROOM_GROW_BLOCK) ? true @@ -9,15 +9,17 @@ } public boolean growMushroom(ServerLevel p_221774_, BlockPos p_221775_, BlockState p_221776_, RandomSource p_221777_) { -@@ -97,8 +_,10 @@ + Optional>> optional = p_221774_.registryAccess() + .registryOrThrow(Registries.CONFIGURED_FEATURE) + .getHolder(this.feature); ++ ++ // Neo: Fire the BlockGrowFeatureEvent and update the result of the Optional local with the new feature. ++ var event = net.neoforged.neoforge.event.EventHooks.fireBlockGrowFeature(p_221774_, p_221777_, p_221775_, optional.orElse(null)); ++ if (event.isCanceled()) { ++ return false; ++ } ++ optional = Optional.ofNullable(event.getFeature()); ++ if (optional.isEmpty()) { return false; } else { -+ var event = net.neoforged.neoforge.event.EventHooks.blockGrowFeature(p_221774_, p_221777_, p_221775_, optional.get()); -+ if (event.getResult().equals(net.neoforged.bus.api.Event.Result.DENY)) return false; - p_221774_.removeBlock(p_221775_, false); -- if (optional.get().value().place(p_221774_, p_221774_.getChunkSource().getGenerator(), p_221777_, p_221775_)) { -+ if (event.getFeature().value().place(p_221774_, p_221774_.getChunkSource().getGenerator(), p_221777_, p_221775_)) { - return true; - } else { - p_221774_.setBlock(p_221775_, p_221776_, 3); diff --git a/patches/net/minecraft/world/level/block/NetherWartBlock.java.patch b/patches/net/minecraft/world/level/block/NetherWartBlock.java.patch index 2860dd6f51..011e77d211 100644 --- a/patches/net/minecraft/world/level/block/NetherWartBlock.java.patch +++ b/patches/net/minecraft/world/level/block/NetherWartBlock.java.patch @@ -5,10 +5,10 @@ protected void randomTick(BlockState p_221806_, ServerLevel p_221807_, BlockPos p_221808_, RandomSource p_221809_) { int i = p_221806_.getValue(AGE); - if (i < 3 && p_221809_.nextInt(10) == 0) { -+ if (i < 3 && net.neoforged.neoforge.common.CommonHooks.onCropsGrowPre(p_221807_, p_221808_, p_221806_, p_221809_.nextInt(10) == 0)) { ++ if (i < 3 && net.neoforged.neoforge.common.CommonHooks.canCropGrow(p_221807_, p_221808_, p_221806_, p_221809_.nextInt(10) == 0)) { p_221806_ = p_221806_.setValue(AGE, Integer.valueOf(i + 1)); p_221807_.setBlock(p_221808_, p_221806_, 2); -+ net.neoforged.neoforge.common.CommonHooks.onCropsGrowPost(p_221807_, p_221808_, p_221806_); ++ net.neoforged.neoforge.common.CommonHooks.fireCropGrowPost(p_221807_, p_221808_, p_221806_); } } diff --git a/patches/net/minecraft/world/level/block/StemBlock.java.patch b/patches/net/minecraft/world/level/block/StemBlock.java.patch index 9d78d1dd4f..5a00381295 100644 --- a/patches/net/minecraft/world/level/block/StemBlock.java.patch +++ b/patches/net/minecraft/world/level/block/StemBlock.java.patch @@ -8,7 +8,7 @@ if (p_222539_.getRawBrightness(p_222540_, 0) >= 9) { float f = CropBlock.getGrowthSpeed(this, p_222539_, p_222540_); - if (p_222541_.nextInt((int)(25.0F / f) + 1) == 0) { -+ if (net.neoforged.neoforge.common.CommonHooks.onCropsGrowPre(p_222539_, p_222540_, p_222538_, p_222541_.nextInt((int)(25.0F / f) + 1) == 0)) { ++ if (net.neoforged.neoforge.common.CommonHooks.canCropGrow(p_222539_, p_222540_, p_222538_, p_222541_.nextInt((int)(25.0F / f) + 1) == 0)) { int i = p_222538_.getValue(AGE); if (i < 7) { - p_222538_ = p_222538_.setValue(AGE, Integer.valueOf(i + 1)); @@ -27,7 +27,7 @@ } } } -+ net.neoforged.neoforge.common.CommonHooks.onCropsGrowPost(p_222539_, p_222540_, p_222538_); ++ net.neoforged.neoforge.common.CommonHooks.fireCropGrowPost(p_222539_, p_222540_, p_222538_); } } } diff --git a/patches/net/minecraft/world/level/block/SugarCaneBlock.java.patch b/patches/net/minecraft/world/level/block/SugarCaneBlock.java.patch index 170176d0de..770a46529a 100644 --- a/patches/net/minecraft/world/level/block/SugarCaneBlock.java.patch +++ b/patches/net/minecraft/world/level/block/SugarCaneBlock.java.patch @@ -13,10 +13,10 @@ if (i < 3) { int j = p_222548_.getValue(AGE); -+ if (net.neoforged.neoforge.common.CommonHooks.onCropsGrowPre(p_222549_, p_222550_, p_222548_, true)) { ++ if (net.neoforged.neoforge.common.CommonHooks.canCropGrow(p_222549_, p_222550_, p_222548_, true)) { if (j == 15) { p_222549_.setBlockAndUpdate(p_222550_.above(), this.defaultBlockState()); -+ net.neoforged.neoforge.common.CommonHooks.onCropsGrowPost(p_222549_, p_222550_.above(), this.defaultBlockState()); ++ net.neoforged.neoforge.common.CommonHooks.fireCropGrowPost(p_222549_, p_222550_.above(), this.defaultBlockState()); p_222549_.setBlock(p_222550_, p_222548_.setValue(AGE, Integer.valueOf(0)), 4); } else { p_222549_.setBlock(p_222550_, p_222548_.setValue(AGE, Integer.valueOf(j + 1)), 4); diff --git a/patches/net/minecraft/world/level/block/SweetBerryBushBlock.java.patch b/patches/net/minecraft/world/level/block/SweetBerryBushBlock.java.patch index 3c4ea82cf3..934879d9b1 100644 --- a/patches/net/minecraft/world/level/block/SweetBerryBushBlock.java.patch +++ b/patches/net/minecraft/world/level/block/SweetBerryBushBlock.java.patch @@ -5,10 +5,10 @@ protected void randomTick(BlockState p_222563_, ServerLevel p_222564_, BlockPos p_222565_, RandomSource p_222566_) { int i = p_222563_.getValue(AGE); - if (i < 3 && p_222566_.nextInt(5) == 0 && p_222564_.getRawBrightness(p_222565_.above(), 0) >= 9) { -+ if (i < 3 && p_222564_.getRawBrightness(p_222565_.above(), 0) >= 9 && net.neoforged.neoforge.common.CommonHooks.onCropsGrowPre(p_222564_, p_222565_, p_222563_, p_222566_.nextInt(5) == 0)) { ++ if (i < 3 && p_222564_.getRawBrightness(p_222565_.above(), 0) >= 9 && net.neoforged.neoforge.common.CommonHooks.canCropGrow(p_222564_, p_222565_, p_222563_, p_222566_.nextInt(5) == 0)) { BlockState blockstate = p_222563_.setValue(AGE, Integer.valueOf(i + 1)); p_222564_.setBlock(p_222565_, blockstate, 2); -+ net.neoforged.neoforge.common.CommonHooks.onCropsGrowPost(p_222564_, p_222565_, p_222563_); ++ net.neoforged.neoforge.common.CommonHooks.fireCropGrowPost(p_222564_, p_222565_, p_222563_); p_222564_.gameEvent(GameEvent.BLOCK_CHANGE, p_222565_, GameEvent.Context.of(blockstate)); } } diff --git a/patches/net/minecraft/world/level/block/TurtleEggBlock.java.patch b/patches/net/minecraft/world/level/block/TurtleEggBlock.java.patch index 143ed3c617..3f07d6855e 100644 --- a/patches/net/minecraft/world/level/block/TurtleEggBlock.java.patch +++ b/patches/net/minecraft/world/level/block/TurtleEggBlock.java.patch @@ -5,7 +5,7 @@ return false; } else { - return !(p_57769_ instanceof LivingEntity) ? false : p_57769_ instanceof Player || p_57768_.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); -+ return !(p_57769_ instanceof LivingEntity) ? false : p_57769_ instanceof Player || net.neoforged.neoforge.event.EventHooks.getMobGriefingEvent(p_57768_, p_57769_); ++ return !(p_57769_ instanceof LivingEntity) ? false : p_57769_ instanceof Player || net.neoforged.neoforge.event.EventHooks.canEntityGrief(p_57768_, p_57769_); } } } diff --git a/patches/net/minecraft/world/level/block/grower/TreeGrower.java.patch b/patches/net/minecraft/world/level/block/grower/TreeGrower.java.patch index 58544d94e6..04738fa464 100644 --- a/patches/net/minecraft/world/level/block/grower/TreeGrower.java.patch +++ b/patches/net/minecraft/world/level/block/grower/TreeGrower.java.patch @@ -4,9 +4,9 @@ .registryOrThrow(Registries.CONFIGURED_FEATURE) .getHolder(resourcekey) .orElse(null); -+ var event = net.neoforged.neoforge.event.EventHooks.blockGrowFeature(p_304396_, p_304893_, p_304643_, holder); ++ var event = net.neoforged.neoforge.event.EventHooks.fireBlockGrowFeature(p_304396_, p_304893_, p_304643_, holder); + holder = event.getFeature(); -+ if (event.getResult() == net.neoforged.bus.api.Event.Result.DENY) return false; ++ if (event.isCanceled()) return false; if (holder != null) { for (int i = 0; i >= -1; i--) { for (int j = 0; j >= -1; j--) { @@ -14,9 +14,9 @@ .registryOrThrow(Registries.CONFIGURED_FEATURE) .getHolder(resourcekey1) .orElse(null); -+ var event = net.neoforged.neoforge.event.EventHooks.blockGrowFeature(p_304396_, p_304893_, p_304643_, holder1); ++ var event = net.neoforged.neoforge.event.EventHooks.fireBlockGrowFeature(p_304396_, p_304893_, p_304643_, holder1); + holder1 = event.getFeature(); -+ if (event.getResult() == net.neoforged.bus.api.Event.Result.DENY) return false; ++ if (event.isCanceled()) return false; if (holder1 == null) { return false; } else { diff --git a/patches/net/minecraft/world/level/levelgen/PhantomSpawner.java.patch b/patches/net/minecraft/world/level/levelgen/PhantomSpawner.java.patch index a2a669ba51..198e5cab39 100644 --- a/patches/net/minecraft/world/level/levelgen/PhantomSpawner.java.patch +++ b/patches/net/minecraft/world/level/levelgen/PhantomSpawner.java.patch @@ -5,17 +5,17 @@ if (!serverplayer.isSpectator()) { BlockPos blockpos = serverplayer.blockPosition(); - if (!p_64576_.dimensionType().hasSkyLight() || blockpos.getY() >= p_64576_.getSeaLevel() && p_64576_.canSeeSky(blockpos)) { -- DifficultyInstance difficultyinstance = p_64576_.getCurrentDifficultyAt(blockpos); -+ DifficultyInstance difficultyinstance = p_64576_.getCurrentDifficultyAt(blockpos); -+ var event = net.neoforged.neoforge.event.EventHooks.onPhantomSpawn(serverplayer, 1 + randomsource.nextInt(difficultyinstance.getDifficulty().getId() + 1)); -+ if (event.getResult() == net.neoforged.bus.api.Event.Result.DENY) continue; -+ if (event.getResult() == net.neoforged.bus.api.Event.Result.ALLOW || !p_64576_.dimensionType().hasSkyLight() || blockpos.getY() >= p_64576_.getSeaLevel() && p_64576_.canSeeSky(blockpos)) { - if (difficultyinstance.isHarderThan(randomsource.nextFloat() * 3.0F)) { ++ var event = net.neoforged.neoforge.event.EventHooks.firePlayerSpawnPhantoms(serverplayer, p_64576_, blockpos); ++ boolean isAllow = event.getResult() == net.neoforged.neoforge.event.entity.player.PlayerSpawnPhantomsEvent.Result.ALLOW; ++ if (event.shouldSpawnPhantoms(p_64576_, blockpos)) { + DifficultyInstance difficultyinstance = p_64576_.getCurrentDifficultyAt(blockpos); +- if (difficultyinstance.isHarderThan(randomsource.nextFloat() * 3.0F)) { ++ if (isAllow || difficultyinstance.isHarderThan(randomsource.nextFloat() * 3.0F)) { ServerStatsCounter serverstatscounter = serverplayer.getStats(); int j = Mth.clamp(serverstatscounter.getValue(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE); int k = 24000; - if (randomsource.nextInt(j) >= 72000) { -+ if (event.getResult() == net.neoforged.bus.api.Event.Result.ALLOW || randomsource.nextInt(j) >= 72000) { ++ if (isAllow || randomsource.nextInt(j) >= 72000) { BlockPos blockpos1 = blockpos.above(20 + randomsource.nextInt(15)) .east(-10 + randomsource.nextInt(21)) .south(-10 + randomsource.nextInt(21)); diff --git a/patches/net/minecraft/world/level/material/FlowingFluid.java.patch b/patches/net/minecraft/world/level/material/FlowingFluid.java.patch index 291782510b..a0acfa62cb 100644 --- a/patches/net/minecraft/world/level/material/FlowingFluid.java.patch +++ b/patches/net/minecraft/world/level/material/FlowingFluid.java.patch @@ -5,7 +5,7 @@ FluidState fluidstate = blockstate.getFluidState(); if (fluidstate.getType().isSame(this) && this.canPassThroughWall(direction, p_256464_, p_76037_, p_76038_, blockpos, blockstate)) { - if (fluidstate.isSource()) { -+ if (fluidstate.isSource() && net.neoforged.neoforge.event.EventHooks.canCreateFluidSource(p_256464_, blockpos, blockstate, fluidstate.canConvertToSource(p_256464_, blockpos))) { ++ if (fluidstate.isSource() && net.neoforged.neoforge.event.EventHooks.canCreateFluidSource(p_256464_, blockpos, blockstate)) { j++; } diff --git a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java index 404a9332f8..34d8104c02 100644 --- a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java +++ b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java @@ -565,9 +565,9 @@ public static boolean onScreenMouseClickedPre(Screen guiScreen, double mouseX, d } public static boolean onScreenMouseClickedPost(Screen guiScreen, double mouseX, double mouseY, int button, boolean handled) { - Event event = new ScreenEvent.MouseButtonPressed.Post(guiScreen, mouseX, mouseY, button, handled); + var event = new ScreenEvent.MouseButtonPressed.Post(guiScreen, mouseX, mouseY, button, handled); NeoForge.EVENT_BUS.post(event); - return event.getResult() == Event.Result.DEFAULT ? handled : event.getResult() == Event.Result.ALLOW; + return event.getClickResult(); } public static boolean onScreenMouseReleasedPre(Screen guiScreen, double mouseX, double mouseY, int button) { @@ -576,9 +576,9 @@ public static boolean onScreenMouseReleasedPre(Screen guiScreen, double mouseX, } public static boolean onScreenMouseReleasedPost(Screen guiScreen, double mouseX, double mouseY, int button, boolean handled) { - Event event = new ScreenEvent.MouseButtonReleased.Post(guiScreen, mouseX, mouseY, button, handled); + var event = new ScreenEvent.MouseButtonReleased.Post(guiScreen, mouseX, mouseY, button, handled); NeoForge.EVENT_BUS.post(event); - return event.getResult() == Event.Result.DEFAULT ? handled : event.getResult() == Event.Result.ALLOW; + return event.getReleaseResult(); } public static boolean onScreenMouseDragPre(Screen guiScreen, double mouseX, double mouseY, int mouseButton, double dragX, double dragY) { diff --git a/src/main/java/net/neoforged/neoforge/client/event/RenderNameTagEvent.java b/src/main/java/net/neoforged/neoforge/client/event/RenderNameTagEvent.java index 09749594c5..fcc27f82a1 100644 --- a/src/main/java/net/neoforged/neoforge/client/event/RenderNameTagEvent.java +++ b/src/main/java/net/neoforged/neoforge/client/event/RenderNameTagEvent.java @@ -10,30 +10,19 @@ import net.minecraft.client.renderer.entity.EntityRenderer; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.Entity; -import net.neoforged.bus.api.Event; -import net.neoforged.fml.LogicalSide; -import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.common.util.TriState; import net.neoforged.neoforge.event.entity.EntityEvent; import org.jetbrains.annotations.ApiStatus; /** - * Fired before an entity renderer renders the nameplate of an entity. - * - *

This event is not {@linkplain ICancellableEvent cancellable}, and {@linkplain Event.HasResult has a result}.

- *
    - *
  • {@link Event.Result#ALLOW} - the nameplate will be forcibly rendered.
  • - *
  • {@link Event.Result#DEFAULT} - the vanilla logic will be used.
  • - *
  • {@link Event.Result#DENY} - the nameplate will not be rendered.
  • - *
- * - *

This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus}, - * only on the {@linkplain LogicalSide#CLIENT logical client}.

+ * This event is fired before an entity renderer renders the nameplate of an entity. + * It allows reacting to the render and controlling if the name plate will be rendered, as well as changing the rendered name. + *

+ * This event is only fired on the logical client. * * @see EntityRenderer */ -@Event.HasResult public class RenderNameTagEvent extends EntityEvent { - private Component nameplateContent; private final Component originalContent; private final EntityRenderer entityRenderer; private final PoseStack poseStack; @@ -41,6 +30,9 @@ public class RenderNameTagEvent extends EntityEvent { private final int packedLight; private final float partialTick; + private Component content; + private TriState canRender = TriState.DEFAULT; + @ApiStatus.Internal public RenderNameTagEvent(Entity entity, Component content, EntityRenderer entityRenderer, PoseStack poseStack, MultiBufferSource multiBufferSource, int packedLight, float partialTick) { super(entity); @@ -53,20 +45,37 @@ public RenderNameTagEvent(Entity entity, Component content, EntityRenderer en this.partialTick = partialTick; } + /** + * Changes if the {@link #getContent() content} of the nameplate will be rendered. + * {@link TriState#TRUE} and {@link TriState#FALSE} will allow/deny the render respectively. + *

+ * Using {@link TriState#DEFAULT} will cause the name to render if {@link EntityRenderer#shouldShowName} returns true. + */ + public void setCanRender(TriState canRender) { + this.canRender = canRender; + } + + /** + * {@return if the nameplate will render or not} + */ + public TriState canRender() { + return canRender; + } + /** * Sets the new text on the nameplate. * * @param contents the new text */ public void setContent(Component contents) { - this.nameplateContent = contents; + this.content = contents; } /** - * {@return the text on the nameplate that will be rendered, if the event is not {@link Result#DENY DENIED}} + * {@return the text on the nameplate that will be rendered} */ public Component getContent() { - return this.nameplateContent; + return this.content; } /** diff --git a/src/main/java/net/neoforged/neoforge/client/event/ScreenEvent.java b/src/main/java/net/neoforged/neoforge/client/event/ScreenEvent.java index e853f9b7f2..0602526c26 100644 --- a/src/main/java/net/neoforged/neoforge/client/event/ScreenEvent.java +++ b/src/main/java/net/neoforged/neoforge/client/event/ScreenEvent.java @@ -19,6 +19,14 @@ import net.neoforged.bus.api.Event; import net.neoforged.bus.api.ICancellableEvent; import net.neoforged.fml.LogicalSide; +import net.neoforged.neoforge.client.event.ScreenEvent.CharacterTyped; +import net.neoforged.neoforge.client.event.ScreenEvent.Init; +import net.neoforged.neoforge.client.event.ScreenEvent.KeyPressed; +import net.neoforged.neoforge.client.event.ScreenEvent.KeyReleased; +import net.neoforged.neoforge.client.event.ScreenEvent.MouseButtonPressed; +import net.neoforged.neoforge.client.event.ScreenEvent.MouseButtonReleased; +import net.neoforged.neoforge.client.event.ScreenEvent.MouseDragged; +import net.neoforged.neoforge.client.event.ScreenEvent.MouseScrolled; import net.neoforged.neoforge.common.NeoForge; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -390,23 +398,14 @@ public Pre(Screen screen, double mouseX, double mouseY, int button) { } /** - * Fired after the mouse click is handled, if the corresponding {@link MouseButtonPressed.Pre} was not + * This event is fired after the mouse click is handled, if the corresponding {@link MouseButtonPressed.Pre} was not * cancelled. - * - *

This event is not {@linkplain ICancellableEvent cancellable}, {@linkplain HasResult has a result}.

- *
    - *
  • {@link Result#ALLOW} - forcibly sets the mouse click as handled
  • - *
  • {@link Result#DEFAULT} - defaults to the return value of - * {@link Screen#mouseClicked(double, double, int)} from the screen (see {@link #wasHandled()}.
  • - *
  • {@link Result#DENY} - forcibly sets the mouse click as not handled.
  • - *
- * - *

This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus}, - * only on the {@linkplain LogicalSide#CLIENT logical client}.

+ *

+ * It is only fired on the {@linkplain Dist#CLIENT physical client}. */ - @HasResult public static class Post extends MouseButtonPressed { private final boolean handled; + private Result result = Result.DEFAULT; @ApiStatus.Internal public Post(Screen screen, double mouseX, double mouseY, int button, boolean handled) { @@ -415,10 +414,55 @@ public Post(Screen screen, double mouseX, double mouseY, int button, boolean han } /** - * {@return {@code true} if the mouse click was already handled by its screen} + * {@return true if the mouse click was already handled by its screen} */ - public boolean wasHandled() { - return handled; + public boolean wasClickHandled() { + return this.handled; + } + + /** + * Changes the result of this event. + * + * @see {@link Result} for the possible states. + */ + public void setResult(Result result) { + this.result = result; + } + + /** + * {@return the result of this event, which controls if the click will be treated as handled} + */ + public Result getResult() { + return this.result; + } + + /** + * {@return The (possibly event-modified) state of the click} + */ + public boolean getClickResult() { + if (this.result == Result.FORCE_HANDLED) { + return true; + } + return this.result == Result.DEFAULT && this.wasClickHandled(); + } + + public static enum Result { + /** + * Forces the event to mark the click as handled by the screen. + */ + FORCE_HANDLED, + + /** + * The result of {@link Screen#mouseClicked(double, double, int)} will be used to determine if the click was handled. + * + * @see {@link Post#wasClickHandled()} + */ + DEFAULT, + + /** + * Forces the event to mark the click as not handled by the screen. + */ + FORCE_UNHANDLED; } } } @@ -467,23 +511,14 @@ public Pre(Screen screen, double mouseX, double mouseY, int button) { } /** - * Fired after the mouse release is handled, if the corresponding {@link MouseButtonReleased.Pre} was + * This event is fired after the mouse release is handled, if the corresponding {@link MouseButtonReleased.Pre} was * not cancelled. - * - *

This event is not {@linkplain ICancellableEvent cancellable}, {@linkplain HasResult has a result}.

- *
    - *
  • {@link Result#ALLOW} - forcibly sets the mouse release as handled
  • - *
  • {@link Result#DEFAULT} - defaults to the return value of - * {@link Screen#mouseReleased(double, double, int)} from the screen (see {@link #wasHandled()}.
  • - *
  • {@link Result#DENY} - forcibly sets the mouse release as not handled.
  • - *
- * - *

This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus}, - * only on the {@linkplain LogicalSide#CLIENT logical client}.

+ *

+ * It is only fired on the {@linkplain Dist#CLIENT physical client}. */ - @HasResult public static class Post extends MouseButtonReleased { private final boolean handled; + private Result result = Result.DEFAULT; @ApiStatus.Internal public Post(Screen screen, double mouseX, double mouseY, int button, boolean handled) { @@ -492,11 +527,56 @@ public Post(Screen screen, double mouseX, double mouseY, int button, boolean han } /** - * @return {@code true} if the mouse release was already handled by its screen + * @return {@code true} if the mouse release was already handled by the screen */ - public boolean wasHandled() { + public boolean wasReleaseHandled() { return handled; } + + /** + * Changes the result of this event. + * + * @see {@link Result} for the possible states. + */ + public void setResult(Result result) { + this.result = result; + } + + /** + * {@return the result of this event, which controls if the release will be treated as handled} + */ + public Result getResult() { + return this.result; + } + + /** + * {@return The (possibly event-modified) state of the release} + */ + public boolean getReleaseResult() { + if (this.result == Result.FORCE_HANDLED) { + return true; + } + return this.result == Result.DEFAULT && this.wasReleaseHandled(); + } + + public static enum Result { + /** + * Forces the event to mark the release as handled by the screen. + */ + FORCE_HANDLED, + + /** + * The result of {@link Screen#mouseReleased(double, double, int)} will be used to determine if the click was handled. + * + * @see {@link Post#wasReleaseHandled()} + */ + DEFAULT, + + /** + * Forces the event to mark the release as not handled by the screen. + */ + FORCE_UNHANDLED; + } } } diff --git a/src/main/java/net/neoforged/neoforge/common/CommonHooks.java b/src/main/java/net/neoforged/neoforge/common/CommonHooks.java index 284a7710e7..1ef1d380b7 100644 --- a/src/main/java/net/neoforged/neoforge/common/CommonHooks.java +++ b/src/main/java/net/neoforged/neoforge/common/CommonHooks.java @@ -78,6 +78,7 @@ import net.minecraft.world.InteractionResult; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffectUtil; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; @@ -139,7 +140,6 @@ import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; -import net.neoforged.bus.api.Event; import net.neoforged.fml.ModList; import net.neoforged.fml.ModLoader; import net.neoforged.fml.i18n.MavenVersionTranslator; @@ -181,6 +181,7 @@ import net.neoforged.neoforge.event.entity.living.LivingSwapItemsEvent; import net.neoforged.neoforge.event.entity.living.LivingUseTotemEvent; import net.neoforged.neoforge.event.entity.living.LootingLevelEvent; +import net.neoforged.neoforge.event.entity.living.MobEffectEvent; import net.neoforged.neoforge.event.entity.living.ShieldBlockEvent; import net.neoforged.neoforge.event.entity.player.AnvilRepairEvent; import net.neoforged.neoforge.event.entity.player.AttackEntityEvent; @@ -189,6 +190,7 @@ import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; import net.neoforged.neoforge.event.level.BlockEvent; import net.neoforged.neoforge.event.level.NoteBlockEvent; +import net.neoforged.neoforge.event.level.block.CropGrowEvent; import net.neoforged.neoforge.fluids.FluidType; import net.neoforged.neoforge.registries.NeoForgeRegistries; import net.neoforged.neoforge.resource.ResourcePackLoader; @@ -778,24 +780,35 @@ public interface BiomeCallbackFunction { Biome apply(final Biome.ClimateSettings climate, final BiomeSpecialEffects effects, final BiomeGenerationSettings gen, final MobSpawnSettings spawns); } - public static boolean onCropsGrowPre(Level level, BlockPos pos, BlockState state, boolean def) { - BlockEvent ev = new BlockEvent.CropGrowEvent.Pre(level, pos, state); + /** + * Checks if a crop can grow by firing {@link CropGrowEvent.Pre}. + * + * @param level The level the crop is in + * @param pos The position of the crop + * @param state The state of the crop + * @param def The result of the default checks performed by the crop. + * @return true if the crop can grow + */ + public static boolean canCropGrow(Level level, BlockPos pos, BlockState state, boolean def) { + var ev = new CropGrowEvent.Pre(level, pos, state); NeoForge.EVENT_BUS.post(ev); - return (ev.getResult() == Event.Result.ALLOW || (ev.getResult() == Event.Result.DEFAULT && def)); + return (ev.getResult() == CropGrowEvent.Pre.Result.GROW || (ev.getResult() == CropGrowEvent.Pre.Result.DEFAULT && def)); } - public static void onCropsGrowPost(Level level, BlockPos pos, BlockState state) { - NeoForge.EVENT_BUS.post(new BlockEvent.CropGrowEvent.Post(level, pos, state, level.getBlockState(pos))); + public static void fireCropGrowPost(Level level, BlockPos pos, BlockState state) { + NeoForge.EVENT_BUS.post(new CropGrowEvent.Post(level, pos, state, level.getBlockState(pos))); } - @Nullable - public static CriticalHitEvent getCriticalHit(Player player, Entity target, boolean vanillaCritical, float damageModifier) { - CriticalHitEvent hitResult = new CriticalHitEvent(player, target, damageModifier, vanillaCritical); - NeoForge.EVENT_BUS.post(hitResult); - if (hitResult.getResult() == Event.Result.ALLOW || (vanillaCritical && hitResult.getResult() == Event.Result.DEFAULT)) { - return hitResult; - } - return null; + /** + * Fires the {@link CriticalHitEvent} and returns the resulting event. + * + * @param player The attacking player + * @param target The attack target + * @param vanillaCritical If the attack would have been a critical hit by vanilla's rules in {@link Player#attack(Entity)}. + * @param damageModifier The base damage modifier. Vanilla critical hits have a damage modifier of 1.5. + */ + public static CriticalHitEvent fireCriticalHit(Player player, Entity target, boolean vanillaCritical, float damageModifier) { + return NeoForge.EVENT_BUS.post(new CriticalHitEvent(player, target, damageModifier, vanillaCritical)); } /** @@ -893,7 +906,7 @@ public static boolean canEntityDestroy(Level level, BlockPos pos, LivingEntity e if (!level.isLoaded(pos)) return false; BlockState state = level.getBlockState(pos); - return EventHooks.getMobGriefingEvent(level, entity) && state.canEntityDestroy(level, pos, entity) && EventHooks.onEntityDestroyBlock(entity, pos, state); + return EventHooks.canEntityGrief(level, entity) && state.canEntityDestroy(level, pos, entity) && EventHooks.onEntityDestroyBlock(entity, pos, state); } /** @@ -1352,4 +1365,16 @@ public static void onChunkUnload(PoiManager poiManager, ChunkAccess chunkAccess) poiManager.remove(sectionPosKey); } } + + /** + * Checks if a mob effect can be applied to an entity by firing {@link MobEffectEvent.Applicable}. + * + * @param entity The target entity the mob effect is being applied to. + * @param effect The mob effect being applied. + * @return True if the mob effect can be applied, otherwise false. + */ + public static boolean canMobEffectBeApplied(LivingEntity entity, MobEffectInstance effect) { + var event = new MobEffectEvent.Applicable(entity, effect); + return NeoForge.EVENT_BUS.post(event).getApplicationResult(); + } } diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java index ad35b5f770..0a04b076ad 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java @@ -243,36 +243,34 @@ default void onDestroyedByPushReaction(BlockState state, Level level, BlockPos p } /** - * Determines if this block is classified as a Bed, Allowing - * players to sleep in it, though the block has to specifically - * perform the sleeping functionality in it's activated event. + * Determines if this block is classified as a bed, replacing instanceof BedBlock checks. + *

+ * If true, players may sleep in it, though the block must manually put the player to sleep + * by calling {@link Player#startSleepInBed} from {@link BlockBehaviour#useWithoutItem} or similar. + *

+ * If you want players to be able to respawn at your bed, you also need to override {@link #getRespawnPosition}. * - * @param state The current state - * @param level The current level - * @param pos Block position in level - * @param player The player or camera entity, null in some cases. + * @param state The current state + * @param level The current level + * @param pos Block position in level + * @param sleeper The sleeping entity. * @return True to treat this as a bed */ - default boolean isBed(BlockState state, BlockGetter level, BlockPos pos, @Nullable Entity player) { - return self() instanceof BedBlock; //TODO: Forge: Keep isBed function? + default boolean isBed(BlockState state, BlockGetter level, BlockPos pos, LivingEntity sleeper) { + return self() instanceof BedBlock; } /** - * Returns the position that the entity is moved to upon - * respawning at this block. + * Returns the position that the entity is moved to upon respawning at this block. * * @param state The current state * @param type The entity type used when checking if a dismount blockstate is dangerous. Currently always PLAYER. * @param levelReader The current level * @param pos Block position in level * @param orientation The angle the entity had when setting the respawn point - * @param entity The entity respawning, often null * @return The spawn position or the empty optional if respawning here is not possible */ - default Optional getRespawnPosition(BlockState state, EntityType type, LevelReader levelReader, BlockPos pos, float orientation, @Nullable LivingEntity entity) { - if (isBed(state, levelReader, pos, entity) && levelReader instanceof Level level && BedBlock.canSetSpawn(level)) { - return BedBlock.findStandUpPosition(type, levelReader, pos, state.getValue(BedBlock.FACING), orientation); - } + default Optional getRespawnPosition(BlockState state, EntityType type, LevelReader levelReader, BlockPos pos, float orientation) { return Optional.empty(); } diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java index aee4f6741c..7c32ce5384 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java @@ -30,6 +30,7 @@ import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.feature.configurations.TreeConfiguration; import net.minecraft.world.level.material.FluidState; @@ -153,16 +154,17 @@ default void onDestroyedByPushReaction(Level level, BlockPos pos, Direction push } /** - * Determines if this block is classified as a Bed, Allowing - * players to sleep in it, though the block has to specifically - * perform the sleeping functionality in it's activated event. + * Determines if this block is classified as a bed, replacing instanceof BedBlock checks. + *

+ * If true, players may sleep in it, though the block must manually put the player to sleep + * by calling {@link Player#startSleepInBed} from {@link BlockBehaviour#useWithoutItem} or similar. * * @param level The current level * @param pos Block position in level - * @param sleeper The sleeper or camera entity, null in some cases. + * @param sleeper The sleeping entity * @return True to treat this as a bed */ - default boolean isBed(BlockGetter level, BlockPos pos, @Nullable LivingEntity sleeper) { + default boolean isBed(BlockGetter level, BlockPos pos, LivingEntity sleeper) { return self().getBlock().isBed(self(), level, pos, sleeper); } @@ -174,11 +176,10 @@ default boolean isBed(BlockGetter level, BlockPos pos, @Nullable LivingEntity sl * @param level The current level * @param pos Block position in level * @param orientation The angle the entity had when setting the respawn point - * @param entity The entity respawning, often null * @return The spawn position or the empty optional if respawning here is not possible */ - default Optional getRespawnPosition(EntityType type, LevelReader level, BlockPos pos, float orientation, @Nullable LivingEntity entity) { - return self().getBlock().getRespawnPosition(self(), type, level, pos, orientation, entity); + default Optional getRespawnPosition(EntityType type, LevelReader level, BlockPos pos, float orientation) { + return self().getBlock().getRespawnPosition(self(), type, level, pos, orientation); } /** diff --git a/src/main/java/net/neoforged/neoforge/common/util/TriState.java b/src/main/java/net/neoforged/neoforge/common/util/TriState.java index 33a23ccc15..d71e0155d2 100644 --- a/src/main/java/net/neoforged/neoforge/common/util/TriState.java +++ b/src/main/java/net/neoforged/neoforge/common/util/TriState.java @@ -20,5 +20,19 @@ public enum TriState { /** * Represents the boolean value {@code false}. */ - FALSE + FALSE; + + // Helper methods for use in patches + + public boolean isTrue() { + return this == TRUE; + } + + public boolean isDefault() { + return this == DEFAULT; + } + + public boolean isFalse() { + return this == FALSE; + } } diff --git a/src/main/java/net/neoforged/neoforge/event/EventHooks.java b/src/main/java/net/neoforged/neoforge/event/EventHooks.java index 1a03715e31..84b4850fa1 100644 --- a/src/main/java/net/neoforged/neoforge/event/EventHooks.java +++ b/src/main/java/net/neoforged/neoforge/event/EventHooks.java @@ -7,6 +7,7 @@ import com.mojang.authlib.GameProfile; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.datafixers.util.Either; import java.io.File; import java.util.EnumSet; import java.util.List; @@ -38,8 +39,10 @@ import net.minecraft.sounds.SoundSource; import net.minecraft.stats.Stat; import net.minecraft.util.RandomSource; +import net.minecraft.util.Unit; import net.minecraft.util.random.WeightedRandomList; import net.minecraft.world.Container; +import net.minecraft.world.Difficulty; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; @@ -59,8 +62,8 @@ import net.minecraft.world.entity.SpawnPlacements; import net.minecraft.world.entity.animal.Animal; import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.entity.monster.Zombie; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.Player.BedSleepingProblem; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.entity.projectile.ThrownEnderpearl; import net.minecraft.world.item.CreativeModeTab; @@ -78,11 +81,13 @@ import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.NaturalSpawner; import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.level.SpawnData; import net.minecraft.world.level.biome.MobSpawnSettings; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.levelgen.PhantomSpawner; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider; import net.minecraft.world.level.levelgen.feature.treedecorators.AlterGroundDecorator; @@ -93,11 +98,11 @@ import net.minecraft.world.level.storage.loot.LootTable; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; -import net.neoforged.bus.api.Event.Result; import net.neoforged.fml.ModLoader; import net.neoforged.neoforge.common.EffectCure; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.ToolAction; +import net.neoforged.neoforge.common.extensions.IFluidStateExtension; import net.neoforged.neoforge.common.extensions.IOwnedSpawner; import net.neoforged.neoforge.common.util.BlockSnapshot; import net.neoforged.neoforge.common.util.MutableHashedLinkedMap; @@ -119,21 +124,21 @@ import net.neoforged.neoforge.event.entity.living.LivingEntityUseItemEvent; import net.neoforged.neoforge.event.entity.living.LivingExperienceDropEvent; import net.neoforged.neoforge.event.entity.living.LivingHealEvent; -import net.neoforged.neoforge.event.entity.living.LivingPackSizeEvent; +import net.neoforged.neoforge.event.entity.living.MobDespawnEvent; import net.neoforged.neoforge.event.entity.living.MobEffectEvent; -import net.neoforged.neoforge.event.entity.living.MobSpawnEvent.AllowDespawn; import net.neoforged.neoforge.event.entity.living.MobSpawnEvent.PositionCheck; import net.neoforged.neoforge.event.entity.living.MobSpawnEvent.SpawnPlacementCheck; import net.neoforged.neoforge.event.entity.living.MobSplitEvent; -import net.neoforged.neoforge.event.entity.living.ZombieEvent.SummonAidEvent; +import net.neoforged.neoforge.event.entity.living.SpawnClusterSizeEvent; import net.neoforged.neoforge.event.entity.player.AdvancementEvent.AdvancementEarnEvent; import net.neoforged.neoforge.event.entity.player.AdvancementEvent.AdvancementProgressEvent; import net.neoforged.neoforge.event.entity.player.AdvancementEvent.AdvancementProgressEvent.ProgressType; import net.neoforged.neoforge.event.entity.player.ArrowLooseEvent; import net.neoforged.neoforge.event.entity.player.ArrowNockEvent; import net.neoforged.neoforge.event.entity.player.BonemealEvent; -import net.neoforged.neoforge.event.entity.player.EntityItemPickupEvent; -import net.neoforged.neoforge.event.entity.player.FillBucketEvent; +import net.neoforged.neoforge.event.entity.player.CanContinueSleepingEvent; +import net.neoforged.neoforge.event.entity.player.CanPlayerSleepEvent; +import net.neoforged.neoforge.event.entity.player.ItemEntityPickupEvent; import net.neoforged.neoforge.event.entity.player.ItemTooltipEvent; import net.neoforged.neoforge.event.entity.player.PermissionsChangedEvent; import net.neoforged.neoforge.event.entity.player.PlayerDestroyItemEvent; @@ -141,27 +146,24 @@ import net.neoforged.neoforge.event.entity.player.PlayerFlyableFallEvent; import net.neoforged.neoforge.event.entity.player.PlayerRespawnPositionEvent; import net.neoforged.neoforge.event.entity.player.PlayerSetSpawnEvent; -import net.neoforged.neoforge.event.entity.player.PlayerSleepInBedEvent; import net.neoforged.neoforge.event.entity.player.PlayerSpawnPhantomsEvent; import net.neoforged.neoforge.event.entity.player.PlayerWakeUpEvent; -import net.neoforged.neoforge.event.entity.player.SleepingLocationCheckEvent; -import net.neoforged.neoforge.event.entity.player.SleepingTimeCheckEvent; import net.neoforged.neoforge.event.furnace.FurnaceFuelBurnTimeEvent; import net.neoforged.neoforge.event.level.AlterGroundEvent; import net.neoforged.neoforge.event.level.AlterGroundEvent.StateProvider; import net.neoforged.neoforge.event.level.BlockEvent; import net.neoforged.neoforge.event.level.BlockEvent.BlockToolModificationEvent; -import net.neoforged.neoforge.event.level.BlockEvent.CreateFluidSourceEvent; import net.neoforged.neoforge.event.level.BlockEvent.EntityMultiPlaceEvent; import net.neoforged.neoforge.event.level.BlockEvent.EntityPlaceEvent; import net.neoforged.neoforge.event.level.BlockEvent.NeighborNotifyEvent; +import net.neoforged.neoforge.event.level.BlockGrowFeatureEvent; import net.neoforged.neoforge.event.level.ChunkTicketLevelUpdatedEvent; import net.neoforged.neoforge.event.level.ChunkWatchEvent; import net.neoforged.neoforge.event.level.ExplosionEvent; import net.neoforged.neoforge.event.level.LevelEvent; import net.neoforged.neoforge.event.level.PistonEvent; -import net.neoforged.neoforge.event.level.SaplingGrowTreeEvent; import net.neoforged.neoforge.event.level.SleepFinishedTimeEvent; +import net.neoforged.neoforge.event.level.block.CreateFluidSourceEvent; import net.neoforged.neoforge.event.tick.EntityTickEvent; import net.neoforged.neoforge.event.tick.LevelTickEvent; import net.neoforged.neoforge.event.tick.PlayerTickEvent; @@ -213,8 +215,7 @@ public static void onPlayerDestroyItem(Player player, ItemStack stack, @Nullable @ApiStatus.Internal public static boolean checkSpawnPlacements(EntityType entityType, ServerLevelAccessor level, MobSpawnType spawnType, BlockPos pos, RandomSource random, boolean defaultResult) { var event = new SpawnPlacementCheck(entityType, level, spawnType, pos, random, defaultResult); - NeoForge.EVENT_BUS.post(event); - return event.getResult() == Result.DEFAULT ? defaultResult : event.getResult() == Result.ALLOW; + return NeoForge.EVENT_BUS.post(event).getPlacementCheckResult(); } /** @@ -230,26 +231,26 @@ public static boolean checkSpawnPlacements(EntityType entityType, ServerLevel public static boolean checkSpawnPosition(Mob mob, ServerLevelAccessor level, MobSpawnType spawnType) { var event = new PositionCheck(mob, level, spawnType, null); NeoForge.EVENT_BUS.post(event); - if (event.getResult() == Result.DEFAULT) { + if (event.getResult() == PositionCheck.Result.DEFAULT) { return mob.checkSpawnRules(level, spawnType) && mob.checkSpawnObstruction(level); } - return event.getResult() == Result.ALLOW; + return event.getResult() == PositionCheck.Result.SUCCEED; } /** - * Specialized variant of {@link #checkSpawnPosition} for spawners, as they have slightly different checks. + * Specialized variant of {@link #checkSpawnPosition} for spawners, as they have slightly different checks, and pass through the {@link BaseSpawner} to the event. * - * @see #CheckSpawnPosition + * @see #checkSpawnPosition(Mob, ServerLevelAccessor, MobSpawnType) * @implNote See in-line comments about custom spawn rules. */ public static boolean checkSpawnPositionSpawner(Mob mob, ServerLevelAccessor level, MobSpawnType spawnType, SpawnData spawnData, BaseSpawner spawner) { - var event = new PositionCheck(mob, level, spawnType, null); + var event = new PositionCheck(mob, level, spawnType, spawner); NeoForge.EVENT_BUS.post(event); - if (event.getResult() == Result.DEFAULT) { + if (event.getResult() == PositionCheck.Result.DEFAULT) { // Spawners do not evaluate Mob#checkSpawnRules if any custom rules are present. This is despite the fact that these two methods do not check the same things. return (spawnData.getCustomSpawnRules().isPresent() || mob.checkSpawnRules(level, spawnType)) && mob.checkSpawnObstruction(level); } - return event.getResult() == Result.ALLOW; + return event.getResult() == PositionCheck.Result.SUCCEED; } /** @@ -343,16 +344,38 @@ public static FinalizeSpawnEvent finalizeMobSpawnSpawner(Mob mob, ServerLevelAcc return event; } - public static PlayerSpawnPhantomsEvent onPhantomSpawn(ServerPlayer player, int phantomsToSpawn) { - var event = new PlayerSpawnPhantomsEvent(player, phantomsToSpawn); + /** + * Called from {@link PhantomSpawner#tick} just before the spawn conditions for phantoms are evaluated. + * Fires the {@link PlayerSpawnPhantomsEvent} and returns the event. + * + * @param player The player for whom a spawn attempt is being made + * @param level The level of the player + * @param pos The block position of the player + */ + public static PlayerSpawnPhantomsEvent firePlayerSpawnPhantoms(ServerPlayer player, ServerLevel level, BlockPos pos) { + Difficulty difficulty = level.getCurrentDifficultyAt(pos).getDifficulty(); + var event = new PlayerSpawnPhantomsEvent(player, 1 + level.random.nextInt(difficulty.getId() + 1)); NeoForge.EVENT_BUS.post(event); return event; } - public static Result canEntityDespawn(Mob entity, ServerLevelAccessor level) { - AllowDespawn event = new AllowDespawn(entity, level); + /** + * Fires {@link MobDespawnEvent} and returns true if the default logic should be ignored. + * + * @param entity The entity being despawned. + * @return True if the event result is not {@link MobDespawnEvent.Result#DEFAULT}, and the vanilla logic should be ignored. + */ + public static boolean checkMobDespawn(Mob mob) { + MobDespawnEvent event = new MobDespawnEvent(mob, (ServerLevel) mob.level()); NeoForge.EVENT_BUS.post(event); - return event.getResult(); + + switch (event.getResult()) { + case ALLOW -> mob.discard(); + case DEFAULT -> {} + case DENY -> mob.setNoActionTime(0); + } + + return event.getResult() != MobDespawnEvent.Result.DEFAULT; } public static int getItemBurnTime(ItemStack itemStack, int burnTime, @Nullable RecipeType recipeType) { @@ -369,10 +392,19 @@ public static int getExperienceDrop(LivingEntity entity, Player attackingPlayer, return event.getDroppedExperience(); } - public static int getMaxSpawnPackSize(Mob entity) { - LivingPackSizeEvent maxCanSpawnEvent = new LivingPackSizeEvent(entity); - NeoForge.EVENT_BUS.post(maxCanSpawnEvent); - return maxCanSpawnEvent.getResult() == Result.ALLOW ? maxCanSpawnEvent.getMaxPackSize() : entity.getMaxSpawnClusterSize(); + /** + * Fires {@link SpawnClusterSizeEvent} and returns the size as a result of the event. + *

+ * Called in {@link NaturalSpawner#spawnCategoryForPosition} where {@link Mob#getMaxSpawnClusterSize()} would normally be called. + * + * @param entity The entity whose max spawn cluster size is being queried. + * + * @return The new spawn cluster size. + */ + public static int getMaxSpawnClusterSize(Mob entity) { + var event = new SpawnClusterSizeEvent(entity); + NeoForge.EVENT_BUS.post(event); + return event.getSize(); } public static Component getPlayerDisplayName(Player player, Component username) { @@ -399,12 +431,6 @@ public static ItemTooltipEvent onItemTooltip(ItemStack itemStack, @Nullable Play return event; } - public static SummonAidEvent fireZombieSummonAid(Zombie zombie, Level level, int x, int y, int z, LivingEntity attacker, double summonChance) { - SummonAidEvent summonEvent = new SummonAidEvent(zombie, level, x, y, z, attacker, summonChance); - NeoForge.EVENT_BUS.post(summonEvent); - return summonEvent; - } - public static boolean onEntityStruckByLightning(Entity entity, LightningBolt bolt) { return NeoForge.EVENT_BUS.post(new EntityStruckByLightningEvent(entity, bolt)).isCanceled(); } @@ -455,36 +481,18 @@ public static BlockState onToolUse(BlockState originalState, UseOnContext contex return NeoForge.EVENT_BUS.post(event).isCanceled() ? null : event.getFinalState(); } - public static int onApplyBonemeal(Player player, Level level, BlockPos pos, BlockState state, ItemStack stack) { - BonemealEvent event = new BonemealEvent(player, level, pos, state, stack); - if (NeoForge.EVENT_BUS.post(event).isCanceled()) return -1; - if (event.getResult() == Result.ALLOW) { - if (!level.isClientSide) - stack.shrink(1); - return 1; - } - return 0; - } - - @Nullable - public static InteractionResultHolder onBucketUse(Player player, Level level, ItemStack stack, @Nullable HitResult target) { - FillBucketEvent event = new FillBucketEvent(player, stack, level, target); - if (NeoForge.EVENT_BUS.post(event).isCanceled()) return new InteractionResultHolder(InteractionResult.FAIL, stack); - - if (event.getResult() == Result.ALLOW) { - if (player.getAbilities().instabuild) - return new InteractionResultHolder(InteractionResult.SUCCESS, stack); - - stack.shrink(1); - if (stack.isEmpty()) - return new InteractionResultHolder(InteractionResult.SUCCESS, event.getFilledBucket()); - - if (!player.getInventory().add(event.getFilledBucket())) - player.drop(event.getFilledBucket(), false); - - return new InteractionResultHolder(InteractionResult.SUCCESS, stack); - } - return null; + /** + * Called when bone meal (or equivalent) is used on a block. Fires the {@link BonemealEvent} and returns the event. + * + * @param player The player who used the item, if any + * @param level The level + * @param pos The position of the target block + * @param state The state of the target block + * @param stack The bone meal item stack + * @return The event + */ + public static BonemealEvent fireBonemealEvent(@Nullable Player player, Level level, BlockPos pos, BlockState state, ItemStack stack) { + return NeoForge.EVENT_BUS.post(new BonemealEvent(player, level, pos, state, stack)); } public static PlayLevelSoundEvent.AtEntity onPlaySoundAtEntity(Entity entity, Holder name, SoundSource category, float volume, float pitch) { @@ -506,10 +514,29 @@ public static int onItemExpire(ItemEntity entity, ItemStack item) { return event.getExtraLife(); } - public static int onItemPickup(ItemEntity entityItem, Player player) { - var event = new EntityItemPickupEvent(player, entityItem); - if (NeoForge.EVENT_BUS.post(event).isCanceled()) return -1; - return event.getResult() == Result.ALLOW ? 1 : 0; + /** + * Called in {@link ItemEntity#playerTouch(Player)} before any other processing occurs. + *

+ * Fires {@link ItemEntityPickupEvent.Pre} and returns the event. + * + * @param itemEntity The item entity that a player collided with + * @param player The player that collided with the item entity + */ + public static ItemEntityPickupEvent.Pre fireItemPickupPre(ItemEntity itemEntity, Player player) { + return NeoForge.EVENT_BUS.post(new ItemEntityPickupEvent.Pre(player, itemEntity)); + } + + /** + * Called in {@link ItemEntity#playerTouch(Player)} after an item was successfully picked up. + *

+ * Fires {@link ItemEntityPickupEvent.Post}. + * + * @param itemEntity The item entity that a player collided with + * @param player The player that collided with the item entity + * @param copy A copy of the item entity's item stack before the pickup + */ + public static void fireItemPickupPost(ItemEntity itemEntity, Player player, ItemStack copy) { + NeoForge.EVENT_BUS.post(new ItemEntityPickupEvent.Post(player, itemEntity, copy)); } public static boolean canMountEntity(Entity entityMounting, Entity entityBeingMounted, boolean isMounting) { @@ -526,10 +553,10 @@ public static boolean onAnimalTame(Animal animal, Player tamer) { return NeoForge.EVENT_BUS.post(new AnimalTameEvent(animal, tamer)).isCanceled(); } - public static Player.BedSleepingProblem onPlayerSleepInBed(Player player, Optional pos) { - PlayerSleepInBedEvent event = new PlayerSleepInBedEvent(player, pos); + public static Either canPlayerStartSleeping(ServerPlayer player, BlockPos pos, Either vanillaResult) { + CanPlayerSleepEvent event = new CanPlayerSleepEvent(player, pos, vanillaResult.left().orElse(null)); NeoForge.EVENT_BUS.post(event); - return event.getResultStatus(); + return event.getProblem() != null ? Either.left(event.getProblem()) : Either.right(Unit.INSTANCE); } public static void onPlayerWakeup(Player player, boolean wakeImmediately, boolean updateLevel) { @@ -603,29 +630,13 @@ public static void onPlayerBrewedPotion(Player player, ItemStack stack) { NeoForge.EVENT_BUS.post(new PlayerBrewedPotionEvent(player, stack)); } - public static boolean fireSleepingLocationCheck(LivingEntity player, BlockPos sleepingLocation) { - SleepingLocationCheckEvent evt = new SleepingLocationCheckEvent(player, sleepingLocation); - NeoForge.EVENT_BUS.post(evt); - - Result canContinueSleep = evt.getResult(); - if (canContinueSleep == Result.DEFAULT) { - return player.getSleepingPos().map(pos -> { - BlockState state = player.level().getBlockState(pos); - return state.getBlock().isBed(state, player.level(), pos, player); - }).orElse(false); - } else - return canContinueSleep == Result.ALLOW; - } - - public static boolean fireSleepingTimeCheck(Player player, Optional sleepingLocation) { - SleepingTimeCheckEvent evt = new SleepingTimeCheckEvent(player, sleepingLocation); - NeoForge.EVENT_BUS.post(evt); - - Result canContinueSleep = evt.getResult(); - if (canContinueSleep == Result.DEFAULT) - return !player.level().isDay(); - else - return canContinueSleep == Result.ALLOW; + /** + * Checks if a sleeping entity can continue sleeping with the given sleeping problem. + * + * @return true if the entity may continue sleeping + */ + public static boolean canEntityContinueSleeping(LivingEntity sleeper, @Nullable BedSleepingProblem problem) { + return NeoForge.EVENT_BUS.post(new CanContinueSleepingEvent(sleeper, problem)).mayContinueSleeping(); } public static InteractionResultHolder onArrowNock(ItemStack item, Level level, Player player, InteractionHand hand, boolean hasAmmo) { @@ -655,12 +666,12 @@ public static LootTable loadLootTable(ResourceLocation name, LootTable table) { return event.getTable(); } - public static boolean canCreateFluidSource(Level level, BlockPos pos, BlockState state, boolean def) { - CreateFluidSourceEvent evt = new CreateFluidSourceEvent(level, pos, state); - NeoForge.EVENT_BUS.post(evt); - - Result result = evt.getResult(); - return result == Result.DEFAULT ? def : result == Result.ALLOW; + /** + * Checks if a fluid is allowed to create a fluid source. This fires the {@link CreateFluidSourceEvent}. + * By default, a fluid can create a source if it returns true to {@link IFluidStateExtension#canConvertToSource(Level, BlockPos)} + */ + public static boolean canCreateFluidSource(Level level, BlockPos pos, BlockState state) { + return NeoForge.EVENT_BUS.post(new CreateFluidSourceEvent(level, pos, state)).canConvert(); } public static Optional onTrySpawnPortal(LevelAccessor level, BlockPos pos, Optional size) { @@ -678,21 +689,33 @@ public static boolean onEntityDestroyBlock(LivingEntity entity, BlockPos pos, Bl return !NeoForge.EVENT_BUS.post(new LivingDestroyBlockEvent(entity, pos, state)).isCanceled(); } - public static boolean getMobGriefingEvent(Level level, @Nullable Entity entity) { + /** + * Checks if an entity can perform a griefing action. + *

+ * If an entity is provided, this method fires {@link EntityMobGriefingEvent}. + * If an entity is not provided, this method returns the value of {@link GameRules#RULE_MOBGRIEFING}. + * + * @param level The level of the action + * @param entity The entity performing the action, or null if unknown. + * @return + */ + public static boolean canEntityGrief(Level level, @Nullable Entity entity) { if (entity == null) return level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); - EntityMobGriefingEvent event = new EntityMobGriefingEvent(entity); - NeoForge.EVENT_BUS.post(event); - - Result result = event.getResult(); - return result == Result.DEFAULT ? level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) : result == Result.ALLOW; + return NeoForge.EVENT_BUS.post(new EntityMobGriefingEvent(level, entity)).canGrief(); } - public static SaplingGrowTreeEvent blockGrowFeature(LevelAccessor level, RandomSource randomSource, BlockPos pos, @Nullable Holder> holder) { - SaplingGrowTreeEvent event = new SaplingGrowTreeEvent(level, randomSource, pos, holder); - NeoForge.EVENT_BUS.post(event); - return event; + /** + * Fires the {@link BlockGrowFeatureEvent} and returns the event object. + * + * @param level The level the feature will be grown in + * @param rand The random source + * @param pos The position the feature will be grown at + * @param holder The feature to be grown, if any + */ + public static BlockGrowFeatureEvent fireBlockGrowFeature(LevelAccessor level, RandomSource rand, BlockPos pos, @Nullable Holder> holder) { + return NeoForge.EVENT_BUS.post(new BlockGrowFeatureEvent(level, rand, pos, holder)); } /** @@ -850,10 +873,6 @@ public static void firePlayerRespawnEvent(ServerPlayer player, boolean fromEndFi NeoForge.EVENT_BUS.post(new PlayerEvent.PlayerRespawnEvent(player, fromEndFight)); } - public static void firePlayerItemPickupEvent(Player player, ItemEntity item, ItemStack clone) { - NeoForge.EVENT_BUS.post(new PlayerEvent.ItemPickupEvent(player, item, clone)); - } - public static void firePlayerCraftingEvent(Player player, ItemStack crafted, Container craftMatrix) { NeoForge.EVENT_BUS.post(new PlayerEvent.ItemCraftedEvent(player, crafted, craftMatrix)); } diff --git a/src/main/java/net/neoforged/neoforge/event/entity/EntityMobGriefingEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/EntityMobGriefingEvent.java index f6f0404073..3a26d1944e 100644 --- a/src/main/java/net/neoforged/neoforge/event/entity/EntityMobGriefingEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/entity/EntityMobGriefingEvent.java @@ -6,24 +6,45 @@ package net.neoforged.neoforge.event.entity; import net.minecraft.world.entity.Entity; -import net.neoforged.bus.api.Event.HasResult; -import net.neoforged.neoforge.common.NeoForge; +import net.minecraft.world.level.GameRules; +import net.minecraft.world.level.Level; /** * EntityMobGriefingEvent is fired when mob griefing is about to occur and allows an event listener to specify whether it should or not.
- * This event is fired when ever the {@code mobGriefing} game rule is checked.
- *
- * This event has a {@link HasResult result}: - *

    - *
  • {@link Result#ALLOW} means this instance of mob griefing is allowed.
  • - *
  • {@link Result#DEFAULT} means the {@code mobGriefing} game rule is used to determine the behaviour.
  • - *
  • {@link Result#DENY} means this instance of mob griefing is not allowed.
  • - *
- * This event is fired on the {@link NeoForge#EVENT_BUS}. + * This event is fired when ever the {@linkplain GameRules#RULE_MOBGRIEFING mob griefing game rule} is checked.
*/ -@HasResult public class EntityMobGriefingEvent extends EntityEvent { - public EntityMobGriefingEvent(Entity entity) { + private final boolean isMobGriefingEnabled; + private boolean canGrief; + + public EntityMobGriefingEvent(Level level, Entity entity) { super(entity); + this.isMobGriefingEnabled = level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); + this.canGrief = this.isMobGriefingEnabled; + } + + /** + * Returns if the {@linkplain GameRules#RULE_MOBGRIEFING mob griefing game rule} is enabled. + *

+ * The default state of this event is equivalent to this value. + */ + public boolean isMobGriefingEnabled() { + return this.isMobGriefingEnabled; + } + + /** + * Changes if the entity is allowed to perform the griefing action. + * + * @param canGrief True if the action should be allowed, false otherwise. + */ + public void setCanGrief(boolean canGrief) { + this.canGrief = canGrief; + } + + /** + * {@return if the entity is allowed to perform the griefing action} + */ + public boolean canGrief() { + return this.canGrief; } } diff --git a/src/main/java/net/neoforged/neoforge/event/entity/living/LivingPackSizeEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/living/LivingPackSizeEvent.java deleted file mode 100644 index b62a57db1d..0000000000 --- a/src/main/java/net/neoforged/neoforge/event/entity/living/LivingPackSizeEvent.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.event.entity.living; - -import net.minecraft.world.entity.Mob; -import net.neoforged.bus.api.Event.HasResult; - -@HasResult -public class LivingPackSizeEvent extends LivingEvent { - private int maxPackSize; - - public LivingPackSizeEvent(Mob entity) { - super(entity); - } - - /** - * This event is fired when the spawning system determines the - * maximum amount of the selected entity that can spawn at the same - * time. - * - * If you set the result to 'ALLOW', it means that you want to return - * the value of maxPackSize as the maximum pack size for current entity. - */ - public int getMaxPackSize() { - return maxPackSize; - } - - public void setMaxPackSize(int maxPackSize) { - this.maxPackSize = maxPackSize; - } -} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/living/MobDespawnEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/living/MobDespawnEvent.java new file mode 100644 index 0000000000..9feef4dcc8 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/event/entity/living/MobDespawnEvent.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.event.entity.living; + +import net.minecraft.world.entity.Mob; +import net.minecraft.world.level.ServerLevelAccessor; +import net.neoforged.neoforge.event.EventHooks; +import org.jetbrains.annotations.ApiStatus; + +/** + * This event is fired from {@link Mob#checkDespawn()}.
+ * It fires once per tick per mob that is attempting to despawn. + *

+ * It is fired for all entities, including {@linkplain Mob#isPersistenceRequired() persistent} entities. + * Additionally, it may be used to keep mobs from despawning in peaceful mode. + *

+ * This event is only fired on the logical server. + * + * @see Mob#checkDespawn() + */ +public class MobDespawnEvent extends MobSpawnEvent { + protected Result result = Result.DEFAULT; + + /** + * Fire via {@link EventHooks#checkMobDespawn(Mob)} + */ + @ApiStatus.Internal + public MobDespawnEvent(Mob mob, ServerLevelAccessor level) { + super(mob, level, mob.getX(), mob.getY(), mob.getZ()); + } + + /** + * Changes the result of this event. + * + * @see {@link Result} for the possible states. + */ + public void setResult(Result result) { + this.result = result; + } + + /** + * {@return the result of this event, which controls if the despawn will occur} + */ + public Result getResult() { + return this.result; + } + + public static enum Result { + /** + * Forcibly allows the despawn to occur. + */ + ALLOW, + + /** + * The default logic in {@link Mob#checkDespawn()} will be used to determine if the despawn may occur. + */ + DEFAULT, + + /** + * Forcibly prevents the despawn from occurring. + */ + DENY; + } +} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/living/MobEffectEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/living/MobEffectEvent.java index c9f7b6ef41..e33a8a3ba2 100644 --- a/src/main/java/net/neoforged/neoforge/event/entity/living/MobEffectEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/entity/living/MobEffectEvent.java @@ -86,15 +86,14 @@ public MobEffectInstance getEffectInstance() { /** * This event is fired to check if a {@link MobEffectInstance} can be applied to an entity. - * This event is not {@link ICancellableEvent}. - * This event {@link HasResult has a result}. *

- * {@link Result#ALLOW ALLOW} will apply this mob effect. - * {@link Result#DENY DENY} will not apply this mob effect. - * {@link Result#DEFAULT DEFAULT} will run vanilla logic to determine if this mob effect is applicable in {@link LivingEntity#canBeAffected}. + * It will be fired whenever {@link LivingEntity#canBeAffected(MobEffectInstance)} would be invoked. + *

+ * */ - @HasResult public static class Applicable extends MobEffectEvent { + protected Result result; + @ApiStatus.Internal public Applicable(LivingEntity living, MobEffectInstance effectInstance) { super(living, effectInstance); @@ -104,6 +103,51 @@ public Applicable(LivingEntity living, MobEffectInstance effectInstance) { public MobEffectInstance getEffectInstance() { return super.getEffectInstance(); } + + /** + * Changes the result of this event. + * + * @see {@link Result} for the possible states. + */ + public void setResult(Result result) { + this.result = result; + } + + /** + * {@return the result of this event, which controls if the mob effect will be applied} + */ + public Result getResult() { + return this.result; + } + + /** + * {@return If the mob effect should be applied or not, based on the current event result} + */ + public boolean getApplicationResult() { + if (this.result == Result.APPLY) { + return true; + } + return this.result == Result.DEFAULT && this.getEntity().canBeAffected(this.getEffectInstance()); + } + + public static enum Result { + /** + * Forces the event to apply the mob effect to the target entity. + */ + APPLY, + + /** + * The result of {@link LivingEntity#canBeAffected(MobEffectInstance)} will be used to determine if the mob effect will be applied. + * + * @see {@link Post#wasClickHandled()} + */ + DEFAULT, + + /** + * Forces the event to not apply the mob effect. + */ + DO_NOT_APPLY; + } } /** diff --git a/src/main/java/net/neoforged/neoforge/event/entity/living/MobSpawnEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/living/MobSpawnEvent.java index b75d35b116..cc28ee5c95 100644 --- a/src/main/java/net/neoforged/neoforge/event/entity/living/MobSpawnEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/entity/living/MobSpawnEvent.java @@ -8,17 +8,19 @@ import net.minecraft.core.BlockPos; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.MobSpawnType; import net.minecraft.world.entity.SpawnPlacements; +import net.minecraft.world.entity.SpawnPlacements.SpawnPredicate; import net.minecraft.world.level.BaseSpawner; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.ServerLevelAccessor; +import net.minecraft.world.level.SpawnData.CustomSpawnRules; import net.neoforged.bus.api.Event; -import net.neoforged.bus.api.ICancellableEvent; import net.neoforged.fml.LogicalSide; -import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.event.entity.EntityEvent; +import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent; import net.neoforged.neoforge.event.entity.SpawnPlacementRegisterEvent; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -87,22 +89,13 @@ public double getZ() { * Spawn Placement checks include light levels, slime chunks, grass blocks for animals, and others in the same vein.
* The purpose of this event is to permit runtime changes to any or all spawn placement logic without having to wrap the placement for each entity. *

- * This event {@linkplain HasResult has a result}.
- * To change the result of this event, use {@link #setResult}. Results are interpreted in the following manner: - *

    - *
  • Allow - The check will succeed, and the spawn process will continue.
  • - *
  • Default - The value of the vanilla check will be used to determine success.
  • - *
  • Deny - The check will fail, and the spawn process will abort.
  • - *
- * This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus}, - * only on the {@linkplain LogicalSide#SERVER logical server}. + * This event is only fired on the {@linkplain LogicalSide#SERVER logical server}. *

* This event is not fired for mob spawners which utilize {@link CustomSpawnRules}, as they do not check spawn placements. * * @apiNote If your modifications are for a single entity, and do not vary at runtime, use {@link SpawnPlacementRegisterEvent}. * @see SpawnPlacementRegisterEvent */ - @HasResult public static class SpawnPlacementCheck extends Event { private final EntityType entityType; private final ServerLevelAccessor level; @@ -110,6 +103,7 @@ public static class SpawnPlacementCheck extends Event { private final BlockPos pos; private final RandomSource random; private final boolean defaultResult; + private Result result; /** * Internal. @@ -174,6 +168,53 @@ public RandomSource getRandom() { public boolean getDefaultResult() { return this.defaultResult; } + + /** + * Changes the result of this event. + * + * @see {@link Result} for the possible states. + */ + public void setResult(Result result) { + this.result = result; + } + + /** + * {@return the result of this event, which controls if the placement check will succeed} + */ + public Result getResult() { + return this.result; + } + + /** + * {@return If the placement check will succeed or not, based on the current event result} + */ + public boolean getPlacementCheckResult() { + if (this.result == Result.SUCCEED) { + return true; + } + return this.result == Result.DEFAULT && this.getDefaultResult(); + } + + public static enum Result { + /** + * Forces the event to cause the placement check to succeed. + */ + SUCCEED, + + /** + * The result of {@link SpawnPredicate#test(EntityType, ServerLevelAccessor, MobSpawnType, BlockPos, RandomSource)} will be used to determine if the check will succeed. + *

+ * If the mob does not have a spawn predicate, the check will always succeed. + * + * @see {@link SpawnPlacementCheck#getDefaultResult()} + */ + DEFAULT, + + /** + * Forces the event to cause the placement check to fail. + */ + FAIL; + } } /** @@ -186,28 +227,17 @@ public boolean getDefaultResult() { *

  • Spawn Block - Ocelots check if the below block is grass or leaves.
  • * *

    - * These checks are performed by the vanilla methods {@link Mob#checkSpawnRules} and {@link Mob#checkSpawnObstruction}.
    - * The logical-and of both methods forms the default result of this event. - *

    - * This event {@linkplain HasResult has a result}.
    - * To change the result of this event, use {@link #setResult}. Results are interpreted in the following manner: - *

      - *
    • Allow - The position will be accepted, and the spawn process will continue.
    • - *
    • Default - The position will be accepted if {@link Mob#checkSpawnRules} and {@link Mob#checkSpawnObstruction} are both true.
    • - *
    • Deny - The position will not be accepted. The spawn process will abort, and further events will not be called.
    • - *
    - * This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus}, - * only on the {@linkplain LogicalSide#SERVER logical server}. + * This event is only fired on the {@linkplain LogicalSide#SERVER logical server}. * * @apiNote This event fires after Spawn Placement checks, which are the primary set of spawn checks. * @see {@link SpawnPlacementRegisterEvent} To modify spawn placements statically at startup. * @see {@link SpawnPlacementCheck} To modify the result of spawn placements at runtime. */ - @HasResult public static class PositionCheck extends MobSpawnEvent { @Nullable private final BaseSpawner spawner; private final MobSpawnType spawnType; + private Result result; public PositionCheck(Mob mob, ServerLevelAccessor level, MobSpawnType spawnType, @Nullable BaseSpawner spawner) { super(mob, level, mob.getX(), mob.getY(), mob.getZ()); @@ -235,30 +265,40 @@ public BaseSpawner getSpawner() { public MobSpawnType getSpawnType() { return this.spawnType; } - } - /** - * This event is fired from {@link Mob#checkDespawn()}.
    - * It fires once per tick per mob that is attempting to despawn.
    - * It is not fired if the mob is persistent (meaning it may not despawn).
    - *

    - * This event is not {@linkplain ICancellableEvent cancellable}, but does {@linkplain HasResult have a result}. - * {@link Result#DEFAULT} indicates that default despawn mechanics should be used. - * {@link Result#ALLOW} indicates that the mob should forcefully despawn. - * {@link Result#DENY} indicates that the mob should forcefully stay spawned. - *

    - * This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus}, - * only on the {@linkplain LogicalSide#SERVER logical server}. - * - * @see LivingEntity#checkDespawn() - * @author cpw - */ - // TODO: 1.20 Move to standalone class, as it is unrelated to the complex mob spawning flow. - // Such a refactor will allow the BaseSpawner and MobSpawnType params to be hoisted to MobSpawnEvent. - @HasResult - public static class AllowDespawn extends MobSpawnEvent { - public AllowDespawn(Mob mob, ServerLevelAccessor level) { - super(mob, level, mob.getX(), mob.getY(), mob.getZ()); + /** + * Changes the result of this event. + * + * @see {@link Result} for the possible states. + */ + public void setResult(Result result) { + this.result = result; + } + + /** + * {@return the result of this event, which controls if the position check will succeed} + */ + public Result getResult() { + return this.result; + } + + public static enum Result { + /** + * Forces the event to cause the position check to succeed. + */ + SUCCEED, + + /** + * The results of {@link Mob#checkSpawnRules(LevelAccessor, MobSpawnType)} and {@link Mob#checkSpawnObstruction(LevelReader)} will be used to determine if the check will succeed. + *

    + * If this is being called from a spawner, the {@link Mob#checkSpawnRules(LevelAccessor, MobSpawnType)} call will be skipped if any {@link CustomSpawnRules} are present. + */ + DEFAULT, + + /** + * Forces the event to cause the position check to fail. + */ + FAIL; } } } diff --git a/src/main/java/net/neoforged/neoforge/event/entity/living/SpawnClusterSizeEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/living/SpawnClusterSizeEvent.java new file mode 100644 index 0000000000..e1e9c83e95 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/event/entity/living/SpawnClusterSizeEvent.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.event.entity.living; + +import net.minecraft.world.entity.Mob; +import net.minecraft.world.level.NaturalSpawner; + +/** + * This event is fired from {@link NaturalSpawner#spawnCategoryForPosition} when the spawning + * system determines the maximum amount of the selected entity that can spawn at the same time. + */ +public class SpawnClusterSizeEvent extends LivingEvent { + private int size; + + public SpawnClusterSizeEvent(Mob entity) { + super(entity); + this.size = entity.getMaxSpawnClusterSize(); + } + + /** + * Gets the possibly event-modified max spawn cluster size for the entity. + *

    + * To see the default size, use {@link Mob#getMaxSpawnClusterSize()} + * + * @return The max spawn cluster size + */ + public int getSize() { + return size; + } + + /** + * Changes the max cluster size for the entity. + * + * @param size The new size + */ + public void setSize(int size) { + this.size = size; + } + + @Override + public Mob getEntity() { + return (Mob) super.getEntity(); + } +} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/living/ZombieEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/living/ZombieEvent.java deleted file mode 100644 index e5f56acdf1..0000000000 --- a/src/main/java/net/neoforged/neoforge/event/entity/living/ZombieEvent.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.event.entity.living; - -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.monster.Zombie; -import net.minecraft.world.level.Level; -import net.neoforged.bus.api.ICancellableEvent; -import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.event.EventHooks; -import net.neoforged.neoforge.event.entity.EntityEvent; - -/** - * ZombieEvent is fired whenever a zombie is spawned for aid. - * If a method utilizes this event as its parameter, the method will - * receive every child event of this class. - * - * All children of this event are fired on the {@link NeoForge#EVENT_BUS}. - **/ -public abstract class ZombieEvent extends EntityEvent { - private final Zombie zombie; - - public ZombieEvent(Zombie zombie) { - super(zombie); - this.zombie = zombie; - } - - @Override - public Zombie getEntity() { - return this.zombie; - } - - /** - * SummonAidEvent is fired when a Zombie Entity is summoned. - * This event is fired whenever a Zombie Entity is summoned in - * {@code Zombie#actuallyHurt(DamageSource, float)}. - * - * This event is fired via the {@link EventHooks#fireZombieSummonAid(Zombie, Level, int, int, int, LivingEntity, double)}. - * - * {@link #getCustomSummonedAid()} remains null, but can be populated with a custom EntityZombie which will be spawned. - * {@link #getLevel()} contains the world that this summoning is occurring in. - * {@link #getX()} contains the x-coordinate at which this summoning event is occurring. - * {@link #getY()} contains the y-coordinate at which this summoning event is occurring. - * {@link #getZ()} contains the z-coordinate at which this summoning event is occurring. - * {@link #getAttacker()} contains the living Entity that attacked and caused this event to fire. - * {@link #getSummonChance()} contains the likelihood that a Zombie would successfully be summoned. - * - * This event is not {@link ICancellableEvent}. - * - * This event has a result. {@link HasResult} - * {@link Result#ALLOW} Zombie is summoned. - * {@link Result#DENY} Zombie is not summoned. - * - * This event is fired on the {@link NeoForge#EVENT_BUS}. - **/ - @HasResult - public static class SummonAidEvent extends ZombieEvent { - private Zombie customSummonedAid; - - private final Level level; - private final int x; - private final int y; - private final int z; - private final LivingEntity attacker; - private final double summonChance; - - public SummonAidEvent(Zombie zombie, Level level, int x, int y, int z, LivingEntity attacker, double summonChance) { - super(zombie); - this.level = level; - this.x = x; - this.y = y; - this.z = z; - this.attacker = attacker; - this.summonChance = summonChance; - } - - /** - * Populate this field to have a custom zombie instead of a normal zombie summoned - */ - public Zombie getCustomSummonedAid() { - return customSummonedAid; - } - - public void setCustomSummonedAid(Zombie customSummonedAid) { - this.customSummonedAid = customSummonedAid; - } - - public Level getLevel() { - return level; - } - - public int getX() { - return x; - } - - public int getY() { - return y; - } - - public int getZ() { - return z; - } - - public LivingEntity getAttacker() { - return attacker; - } - - public double getSummonChance() { - return summonChance; - } - } -} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/player/BonemealEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/player/BonemealEvent.java index 435ad706ba..33f93e61ca 100644 --- a/src/main/java/net/neoforged/neoforge/event/entity/player/BonemealEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/entity/player/BonemealEvent.java @@ -9,48 +9,114 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BonemealableBlock; import net.minecraft.world.level.block.state.BlockState; import net.neoforged.bus.api.Event; import net.neoforged.bus.api.ICancellableEvent; +import org.jetbrains.annotations.Nullable; /** - * This event is called when a player attempts to use Bonemeal on a block. - * It can be canceled to completely prevent any further processing. - * - * You can also set the result to ALLOW to mark the event as processed - * and use up a bonemeal from the stack but do no further processing. - * - * setResult(ALLOW) is the same as the old setHandled() + * This event is called when a player attempts to use bone meal on a block. + *

    + * This event can be cancelled, preventing vanilla handling from occurring. + * If you want to perform custom logic, cancel the event and perform your own handling. + * Use {@link #setSuccessful(boolean)} to control if handling should believe bone meal was used. + *

    + * This event is fired on both client and server. */ -// TODO 1.20.5: Make player nullable. Remove extends PlayerEvent and remove the dispenser fake player. -@Event.HasResult -public class BonemealEvent extends PlayerEvent implements ICancellableEvent { +public class BonemealEvent extends Event implements ICancellableEvent { + @Nullable + private final Player player; private final Level level; private final BlockPos pos; - private final BlockState block; + private final BlockState state; private final ItemStack stack; + private final boolean isValidBonemealTarget; + private boolean isSuccess = false; - public BonemealEvent(Player player, Level level, BlockPos pos, BlockState block, ItemStack stack) { - super(player); + public BonemealEvent(@Nullable Player player, Level level, BlockPos pos, BlockState state, ItemStack stack) { + this.player = player; this.level = level; this.pos = pos; - this.block = block; + this.state = state; this.stack = stack; + this.isValidBonemealTarget = state.getBlock() instanceof BonemealableBlock bonemealable && bonemealable.isValidBonemealTarget(level, pos, state); } + /** + * {@return the player who used the bone meal, if any} + */ + @Nullable + public Player getPlayer() { + return this.player; + } + + /** + * {@return the level} + */ public Level getLevel() { - return level; + return this.level; } + /** + * {@return the position of the bone mealed block} + */ public BlockPos getPos() { - return pos; + return this.pos; } - public BlockState getBlock() { - return block; + /** + * {@return the state of the bone mealed block} + */ + public BlockState getState() { + return this.state; } + /** + * Returns the bone meal item stack. + *

    + * Changes to this stack will write-back to the consumer. + */ public ItemStack getStack() { - return stack; + return this.stack; + } + + /** + * Returns true if the block is a valid bone meal target. + *

    + * This is determined by {@link BonemealableBlock#isValidBonemealTarget}. + */ + public boolean isValidBonemealTarget() { + return this.isValidBonemealTarget; + } + + /** + * Cancels the event and changes the successful state. + *

    + * The state controls if handlers believe bone meal was successfully applied, and + * controls things like hand swings. + */ + public void setSuccessful(boolean success) { + this.isSuccess = success; + this.setCanceled(true); + } + + /** + * Returns if the event is successful. Only relevant if the event {@link #isCanceled()}. + * + * @see #setSuccessful(boolean) + */ + public boolean isSuccessful() { + return this.isSuccess; + } + + /** + * Cancels the event, preventing vanilla handling from being applied. + * + * @see #setSuccessful(boolean) + */ + @Override + public void setCanceled(boolean canceled) { + ICancellableEvent.super.setCanceled(canceled); } } diff --git a/src/main/java/net/neoforged/neoforge/event/entity/player/CanContinueSleepingEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/player/CanContinueSleepingEvent.java new file mode 100644 index 0000000000..dbf313c4a6 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/event/entity/player/CanContinueSleepingEvent.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.event.entity.player; + +import java.util.Optional; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player.BedSleepingProblem; +import net.neoforged.neoforge.event.entity.living.LivingEvent; +import org.jetbrains.annotations.Nullable; + +/** + * This event is fired when the game checks if a sleeping entity may continue sleeping. + *

    + * It can be used to overwrite the vanilla check, forcing the entity to continue or stop sleeping. + *

    + * This event is only fired on the logical server. + * + * @see {@link CanPlayerSleepEvent} for when a player starts sleeping. + */ +public class CanContinueSleepingEvent extends LivingEvent { + @Nullable + protected final BedSleepingProblem problem; + protected boolean mayContinueSleeping; + + public CanContinueSleepingEvent(LivingEntity entity, @Nullable BedSleepingProblem problem) { + super(entity); + this.problem = problem; + this.mayContinueSleeping = (problem == null); + } + + /** + * Returns the sleeping position of the sleeping entity. May be empty. + */ + Optional getSleepingPos() { + return this.getEntity().getSleepingPos(); + } + + /** + * Returns the current sleeping problem, if any. By default, this event is fired with the following problems: + *

      + *
    • {@link BedSleepingProblem#NOT_POSSIBLE_HERE} if the sleeper is missing a bed.
    • + *
    • {@link BedSleepingProblem#NOT_POSSIBLE_NOW} if it is daytime.
    • + *
    + * + * Mods may fire this event with other problems if they impose additional sleeping conditions. + */ + @Nullable + public BedSleepingProblem getProblem() { + return this.problem; + } + + /** + * {@return if the sleeping entity may continue sleeping} + */ + public boolean mayContinueSleeping() { + return this.mayContinueSleeping; + } + + /** + * Sets if the sleeping entity may continue sleeping. + * By default, the entity may continue sleeping if there was not a problem detected. + */ + public void setContinueSleeping(boolean sleeping) { + this.mayContinueSleeping = sleeping; + } +} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/player/CanPlayerSleepEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/player/CanPlayerSleepEvent.java new file mode 100644 index 0000000000..fa1e158124 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/event/entity/player/CanPlayerSleepEvent.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.event.entity.player; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player.BedSleepingProblem; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +/** + * Called from {@link ServerPlayer#startSleepInBed(BlockPos)} when a player attempts to sleep. + *

    + * This event receives the result of vanilla checking if the sleep attempt is valid, and permits overriding it. + *

    + * This event is only fired on the logical server. + * + * @see {@link CanContinueSleepingEvent} for per-tick sleeping checks. + */ +public class CanPlayerSleepEvent extends PlayerEvent { + private final BlockPos pos; + private final BlockState state; + + @Nullable + private final BedSleepingProblem vanillaProblem; + + @Nullable + private BedSleepingProblem problem; + + public CanPlayerSleepEvent(ServerPlayer player, BlockPos pos, @Nullable BedSleepingProblem problem) { + super(player); + this.pos = pos; + this.state = player.level().getBlockState(pos); + this.vanillaProblem = this.problem = problem; + } + + @Override + public ServerPlayer getEntity() { + return (ServerPlayer) super.getEntity(); + } + + public Level getLevel() { + return this.getEntity().level(); + } + + public BlockPos getPos() { + return pos; + } + + public BlockState getState() { + return state; + } + + /** + * {@return the current sleeping problem} + */ + @Nullable + public BedSleepingProblem getProblem() { + return this.problem; + } + + /** + * Sets a new sleeping problem. If the new problem is null, the player is allowed to sleep here. + */ + public void setProblem(@Nullable BedSleepingProblem problem) { + this.problem = problem; + } + + /** + * Returns the default sleeping problem based on the vanilla checks. + * + * @see ServerPlayer#startSleepInBed(BlockPos) to identify the cause of a problem. + */ + @Nullable + public BedSleepingProblem getVanillaProblem() { + return vanillaProblem; + } +} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/player/CriticalHitEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/player/CriticalHitEvent.java index fca4f568bb..03b4c3e32b 100644 --- a/src/main/java/net/neoforged/neoforge/event/entity/player/CriticalHitEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/entity/player/CriticalHitEvent.java @@ -7,72 +7,96 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; -import net.neoforged.bus.api.Event.HasResult; -import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.common.CommonHooks; /** - * This event is fired whenever a player attacks an Entity in - * EntityPlayer#attackTargetEntityWithCurrentItem(Entity).
    - *
    - * This event is not {@link ICancellableEvent}.
    - *
    - * This event has a result. {@link HasResult}
    - * DEFAULT: means the vanilla logic will determine if this a critical hit.
    - * DENY: it will not be a critical hit but the player still will attack
    - * ALLOW: this attack is forced to be critical - *
    - * This event is fired on the {@link NeoForge#EVENT_BUS}. + * This event is fired when a player attacks an entity in {@link Player#attack(Entity)}. + * It can be used to change the critical hit status and damage modifier + *

    + * In the event the attack was not a critical hit, the event will still be fired, but it will be preemptively cancelled. **/ -@HasResult public class CriticalHitEvent extends PlayerEvent { - private float damageModifier; - private final float oldDamageModifier; private final Entity target; - private final boolean vanillaCritical; + private final float vanillaDmgMultiplier; + private final boolean isVanillaCritical; - public CriticalHitEvent(Player player, Entity target, float damageModifier, boolean vanillaCritical) { + private float dmgMultiplier; + private boolean isCriticalHit; + + /** + * Fire via {@link CommonHooks#fireCriticalHit(Player, Entity, boolean, float)} + */ + public CriticalHitEvent(Player player, Entity target, float dmgMultiplier, boolean isCriticalHit) { super(player); this.target = target; - this.damageModifier = damageModifier; - this.oldDamageModifier = damageModifier; - this.vanillaCritical = vanillaCritical; + this.dmgMultiplier = this.vanillaDmgMultiplier = dmgMultiplier; + this.isCriticalHit = this.isVanillaCritical = isCriticalHit; } /** - * The Entity that was damaged by the player. + * {@return the entity that was attacked by the player} */ public Entity getTarget() { - return target; + return this.target; + } + + /** + * The damage multiplier is applied to the base attack's damage if the attack {@linkplain #isCriticalHit() critically hits}. + *

    + * A damage multiplier of 1.0 will not change the damage, a value of 1.5 will increase the damage by 50%, and so on. + * + * @param modifier The new damage modifier. + */ + public float getDamageMultiplier() { + return this.dmgMultiplier; + } + + /** + * Sets the damage multiplier for the critical hit. Not used if {@link #isCriticalHit()} is false. + *

    + * Changing the damage modifier to zero does not guarantee that the attack does zero damage. + * + * @param modifier The new damage modifier. Must not be negative. + * @see #getDamageMultiplier() + */ + public void setDamageMultiplier(float dmgMultiplier) { + if (dmgMultiplier < 0) { + throw new UnsupportedOperationException("Attempted to set a negative damage multiplier: " + dmgMultiplier); + } + this.dmgMultiplier = dmgMultiplier; } /** - * This set the damage multiplier for the hit. - * If you set it to 0, then the particles are still generated but damage is not done. + * {@return if the attack will critically hit} */ - public void setDamageModifier(float mod) { - this.damageModifier = mod; + public boolean isCriticalHit() { + return this.isCriticalHit; } /** - * The damage modifier for the hit.
    - * This is by default 1.5F for ciritcal hits and 1F for normal hits . + * Changes the critical hit state. + * + * @param isCriticalHit true if the attack should critically hit */ - public float getDamageModifier() { - return this.damageModifier; + public void setCriticalHit(boolean isCriticalHit) { + this.isCriticalHit = isCriticalHit; } /** - * The orignal damage modifier for the hit wthout any changes.
    - * This is 1.5F for ciritcal hits and 1F for normal hits . + * Gets the original damage multiplier set by vanilla. + *

    + * If the event {@link #isVanillaCritical()}, the damage multiplier will be 1.5, otherwise it will be 1.0 + * + * @see #getDamageMultiplier() */ - public float getOldDamageModifier() { - return this.oldDamageModifier; + public float getVanillaMultiplier() { + return this.vanillaDmgMultiplier; } /** - * Returns true if this hit was critical by vanilla + * {@return true if the attack was considered a critical hit by vanilla} */ public boolean isVanillaCritical() { - return vanillaCritical; + return this.isVanillaCritical; } } diff --git a/src/main/java/net/neoforged/neoforge/event/entity/player/EntityItemPickupEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/player/EntityItemPickupEvent.java deleted file mode 100644 index cbb5c1b97b..0000000000 --- a/src/main/java/net/neoforged/neoforge/event/entity/player/EntityItemPickupEvent.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.event.entity.player; - -import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.entity.player.Player; -import net.neoforged.bus.api.Event; -import net.neoforged.bus.api.ICancellableEvent; - -/** - * This event is called when a player collides with a EntityItem on the ground. - * The event can be canceled, and no further processing will be done. - * - * You can set the result of this event to ALLOW which will trigger the - * processing of achievements, FML's event, play the sound, and kill the - * entity if all the items are picked up. - * - * setResult(ALLOW) is the same as the old setHandled() - */ -@Event.HasResult -public class EntityItemPickupEvent extends PlayerEvent implements ICancellableEvent { - private final ItemEntity item; - - public EntityItemPickupEvent(Player player, ItemEntity item) { - super(player); - this.item = item; - } - - public ItemEntity getItem() { - return item; - } -} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/player/FillBucketEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/player/FillBucketEvent.java deleted file mode 100644 index d08cd3abfb..0000000000 --- a/src/main/java/net/neoforged/neoforge/event/entity/player/FillBucketEvent.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.event.entity.player; - -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.phys.HitResult; -import net.neoforged.bus.api.Event; -import net.neoforged.bus.api.ICancellableEvent; -import org.jetbrains.annotations.Nullable; - -/** - * This event is fired when a player attempts to use a Empty bucket, it - * can be canceled to completely prevent any further processing. - * - * If you set the result to 'ALLOW', it means that you have processed - * the event and wants the basic functionality of adding the new - * ItemStack to your inventory and reducing the stack size to process. - * setResult(ALLOW) is the same as the old setHandled(); - */ -@Event.HasResult -public class FillBucketEvent extends PlayerEvent implements ICancellableEvent { - private final ItemStack current; - private final Level level; - @Nullable - private final HitResult target; - - private ItemStack result; - - public FillBucketEvent(Player player, ItemStack current, Level level, @Nullable HitResult target) { - super(player); - this.current = current; - this.level = level; - this.target = target; - } - - public ItemStack getEmptyBucket() { - return this.current; - } - - public Level getLevel() { - return this.level; - } - - @Nullable - public HitResult getTarget() { - return this.target; - } - - public ItemStack getFilledBucket() { - return this.result; - } - - public void setFilledBucket(ItemStack bucket) { - this.result = bucket; - } -} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/player/ItemEntityPickupEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/player/ItemEntityPickupEvent.java new file mode 100644 index 0000000000..70c69ac5a3 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/event/entity/player/ItemEntityPickupEvent.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.event.entity.player; + +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.neoforged.bus.api.Event; +import net.neoforged.neoforge.common.util.TriState; + +/** + * Parent class of the two events that fire when a {@link Player} collides with an {@link ItemEntity}. + * + * @see ItemEntityPickupEvent.Pre + * @see ItemEntityPickupEvent.Post + */ +public abstract class ItemEntityPickupEvent extends Event { + private final Player player; + private final ItemEntity item; + + public ItemEntityPickupEvent(Player player, ItemEntity item) { + this.player = player; + this.item = item; + } + + /** + * {@return the player who collided with the item} + */ + public Player getPlayer() { + return this.player; + } + + /** + * Returns the {@link ItemEntity} that was collided with. + *

    + * Changes to this item entity will impact further processing by the game and other event handlers. + *

    + * Modification of the stored stack {@link ItemEntity#getItem()} is legal, but consumers of this event + * must not call {@link ItemEntity#setItem(ItemStack)}, as it will incur undefined behavior. + */ + public ItemEntity getItemEntity() { + return item; + } + + /** + * This event is fired when a player collides with an {@link ItemEntity} on the ground. + * It can be used to determine if the item may be picked up by the player. + *

    + * If the pickup is successful (either by force or by default rules) {@link ItemEntityPickupEvent.Post} will be fired. + *

    + * This event is only fired on the logical server. + */ + public static class Pre extends ItemEntityPickupEvent { + private TriState canPickup = TriState.DEFAULT; + + public Pre(Player player, ItemEntity item) { + super(player, item); + } + + /** + * Changes if the player may pickup the item. Setting {@link TriState#TRUE} or {@link TriState#FALSE} will allow/deny the pickup respectively. + *

    + * The default rules require that {@link ItemEntity#pickupDelay} is zero, and that {@link ItemEntity#target} matches (or is null). + * + * @param state The new pickup state. + */ + public void setCanPickup(TriState state) { + this.canPickup = state; + } + + /** + * {@return the current pickup state} + * + * @see #setCanPickup(TriState) + */ + public TriState canPickup() { + return this.canPickup; + } + } + + /** + * This event is fired when an {@link ItemEntity} on the ground has been picked up by the player + * and after the item is added to the player's inventory. + *

    + * This event only fires if part of the item was picked up by the player. + *

    + * If the {@linkplain ItemEntity#getItem() remaining item stack} is empty, the item entity will be discarded. + *

    + * This event is only fired on the logical server. + */ + public static class Post extends ItemEntityPickupEvent { + private final ItemStack originalStack; + + public Post(Player player, ItemEntity item, ItemStack originalStack) { + super(player, item); + this.originalStack = originalStack; + } + + /** + * Returns a copy of the original stack, before it was added to the player's inventory. + * Changes to this item stack have no effect on any further processing. + */ + public ItemStack getOriginalStack() { + return this.originalStack.copy(); + } + + /** + * Returns a live reference to the remaining stack held by the {@link ItemEntity}. + */ + public ItemStack getCurrentStack() { + return this.getItemEntity().getItem(); + } + } +} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerEvent.java index 7a9766ef1c..020c86cb0e 100644 --- a/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerEvent.java @@ -13,7 +13,6 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockGetter; @@ -388,31 +387,6 @@ public String getPlayerUUID() { } } - public static class ItemPickupEvent extends PlayerEvent { - /** - * Original EntityItem with current remaining stack size - */ - private final ItemEntity originalEntity; - /** - * Clone item stack, containing the item and amount picked up - */ - private final ItemStack stack; - - public ItemPickupEvent(Player player, ItemEntity entPickedUp, ItemStack stack) { - super(player); - this.originalEntity = entPickedUp; - this.stack = stack; - } - - public ItemStack getStack() { - return stack; - } - - public ItemEntity getOriginalEntity() { - return originalEntity; - } - } - public static class ItemCraftedEvent extends PlayerEvent { private final ItemStack crafting; private final Container craftMatrix; diff --git a/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerInteractEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerInteractEvent.java index df49ead033..388b231c89 100644 --- a/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerInteractEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerInteractEvent.java @@ -26,6 +26,7 @@ import net.neoforged.bus.api.ICancellableEvent; import net.neoforged.fml.LogicalSide; import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.common.util.TriState; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -162,8 +163,8 @@ public void setCancellationResult(InteractionResult result) { public static class RightClickBlock extends PlayerInteractEvent implements ICancellableEvent { private InteractionResult cancellationResult = InteractionResult.PASS; - private Result useBlock = Result.DEFAULT; - private Result useItem = Result.DEFAULT; + private TriState useBlock = TriState.DEFAULT; + private TriState useItem = TriState.DEFAULT; private BlockHitResult hitVec; public RightClickBlock(Player player, InteractionHand hand, BlockPos pos, BlockHitResult hitVec) { @@ -174,14 +175,14 @@ public RightClickBlock(Player player, InteractionHand hand, BlockPos pos, BlockH /** * @return If {@link Block#use(BlockState, Level, BlockPos, Player, InteractionHand, BlockHitResult)} should be called */ - public Result getUseBlock() { + public TriState getUseBlock() { return useBlock; } /** * @return If {@link Item#onItemUseFirst} and {@link Item#useOn(UseOnContext)} should be called */ - public Result getUseItem() { + public TriState getUseItem() { return useItem; } @@ -193,21 +194,21 @@ public BlockHitResult getHitVec() { } /** - * DENY: {@link Block#use(BlockState, Level, BlockPos, Player, InteractionHand, BlockHitResult)} will never be called.
    + * FALSE: {@link Block#use(BlockState, Level, BlockPos, Player, InteractionHand, BlockHitResult)} will never be called.
    * DEFAULT: {@link Block#use(BlockState, Level, BlockPos, Player, InteractionHand, BlockHitResult)} will be called if {@link Item#onItemUseFirst} passes.
    * Note that default activation can be blocked if the user is sneaking and holding an item that does not return true to {@link Item#doesSneakBypassUse}.
    - * ALLOW: {@link Block#updateOrDestroy(BlockState, BlockState, LevelAccessor, BlockPos, int, int)} will always be called, unless {@link Item#onItemUseFirst} does not pass.
    + * TRUE: {@link Block#updateOrDestroy(BlockState, BlockState, LevelAccessor, BlockPos, int, int)} will always be called, unless {@link Item#onItemUseFirst} does not pass.
    */ - public void setUseBlock(Result triggerBlock) { + public void setUseBlock(TriState triggerBlock) { this.useBlock = triggerBlock; } /** - * DENY: Neither {@link Item#useOn(UseOnContext)} or {@link Item#onItemUseFirst} will be called.
    + * FALSE: Neither {@link Item#useOn(UseOnContext)} or {@link Item#onItemUseFirst} will be called.
    * DEFAULT: {@link Item#onItemUseFirst} will always be called, and {@link Item#useOn(UseOnContext)} will be called if the block passes.
    - * ALLOW: {@link Item#onItemUseFirst} will always be called, and {@link Item#useOn(UseOnContext)} will be called if the block passes, regardless of cooldowns or emptiness.
    + * TRUE: {@link Item#onItemUseFirst} will always be called, and {@link Item#useOn(UseOnContext)} will be called if the block passes, regardless of cooldowns or emptiness.
    */ - public void setUseItem(Result triggerItem) { + public void setUseItem(TriState triggerItem) { this.useItem = triggerItem; } @@ -215,8 +216,8 @@ public void setUseItem(Result triggerItem) { public void setCanceled(boolean canceled) { ICancellableEvent.super.setCanceled(canceled); if (canceled) { - useBlock = Result.DENY; - useItem = Result.DENY; + useBlock = TriState.FALSE; + useItem = TriState.FALSE; } } @@ -297,8 +298,8 @@ public RightClickEmpty(Player player, InteractionHand hand) { * Therefore, in creative mode, {@link #setUseBlock} and {@link #setUseItem} have no effect. */ public static class LeftClickBlock extends PlayerInteractEvent implements ICancellableEvent { - private Result useBlock = Result.DEFAULT; - private Result useItem = Result.DEFAULT; + private TriState useBlock = TriState.DEFAULT; + private TriState useItem = TriState.DEFAULT; private final Action action; @ApiStatus.Internal @@ -310,14 +311,14 @@ public LeftClickBlock(Player player, BlockPos pos, Direction face, Action action /** * @return If {@link Block#attack(BlockState, Level, BlockPos, Player)} should be called. Changing this has no effect in creative mode */ - public Result getUseBlock() { + public TriState getUseBlock() { return useBlock; } /** * @return If the block should be attempted to be mined with the current item. Changing this has no effect in creative mode */ - public Result getUseItem() { + public TriState getUseItem() { return useItem; } @@ -328,11 +329,11 @@ public Action getAction() { return this.action; } - public void setUseBlock(Result triggerBlock) { + public void setUseBlock(TriState triggerBlock) { this.useBlock = triggerBlock; } - public void setUseItem(Result triggerItem) { + public void setUseItem(TriState triggerItem) { this.useItem = triggerItem; } @@ -340,8 +341,8 @@ public void setUseItem(Result triggerItem) { public void setCanceled(boolean canceled) { ICancellableEvent.super.setCanceled(canceled); if (canceled) { - useBlock = Result.DENY; - useItem = Result.DENY; + useBlock = TriState.FALSE; + useItem = TriState.FALSE; } } diff --git a/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerSleepInBedEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerSleepInBedEvent.java deleted file mode 100644 index 8d91694d5b..0000000000 --- a/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerSleepInBedEvent.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.event.entity.player; - -import java.util.Optional; -import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.Player.BedSleepingProblem; -import net.neoforged.neoforge.common.NeoForge; - -/** - * PlayerSleepInBedEvent is fired when a player sleeps in a bed. - *
    - * This event is fired whenever a player sleeps in a bed in - * {@link Player#startSleeping(BlockPos)}.
    - *
    - * {@link #result} contains whether the player is able to sleep.
    - *
    - * This event does not have a result. {@link HasResult} - *
    - * This event is fired on the {@link NeoForge#EVENT_BUS}. - **/ -public class PlayerSleepInBedEvent extends PlayerEvent { - private BedSleepingProblem result = null; - private final Optional pos; - - public PlayerSleepInBedEvent(Player player, Optional pos) { - super(player); - this.pos = pos; - } - - public BedSleepingProblem getResultStatus() { - return result; - } - - public void setResult(BedSleepingProblem result) { - this.result = result; - } - - public BlockPos getPos() { - return pos.orElse(null); - } - - public Optional getOptionalPos() { - return pos; - } -} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerSpawnPhantomsEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerSpawnPhantomsEvent.java index 69ec112114..2878228d6e 100644 --- a/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerSpawnPhantomsEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/entity/player/PlayerSpawnPhantomsEvent.java @@ -5,26 +5,23 @@ package net.neoforged.neoforge.event.entity.player; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.stats.Stats; +import net.minecraft.world.DifficultyInstance; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.NaturalSpawner; import net.minecraft.world.level.levelgen.PhantomSpawner; -import net.neoforged.bus.api.Event; -import net.neoforged.neoforge.common.NeoForge; /** - * This event is fired from {@link PhantomSpawner#tick}, once per player, when phantoms would attempt to be spawned.
    - * This event is not fired for spectating players. + * This event is fired from {@link PhantomSpawner#tick} when phantoms would attempt to be spawned, with one event fired per player. + * It allows controlling if a spawn attempt will be made for the particular player, but cannot guarantee that a Phantom will be spawned. *

    - * This event is fired before any per-player checks (but after {@link Player#isSpectator()}), but after all global checks.
    - * The behavior of {@link PhantomSpawner} is determined by the result of this event.
    - * See {@link #setResult} for documentation.
    - *

    - * This event is fired on the {@link NeoForge#EVENT_BUS}.
    - * - * @see PlayerSpawnPhantomsEvent#setResult for the effects of each result. + * This event is only fired on the logical server. */ -@Event.HasResult public class PlayerSpawnPhantomsEvent extends PlayerEvent { private int phantomsToSpawn; + private Result result = Result.DEFAULT; public PlayerSpawnPhantomsEvent(Player player, int phantomsToSpawn) { super(player); @@ -48,15 +45,63 @@ public void setPhantomsToSpawn(int phantomsToSpawn) { } /** - * The result of this event controls if phantoms will be spawned.
    - *

      - *
    • If the result is {@link Event.Result#ALLOW}, phantoms will always be spawned;
    • - *
    • If the result is {@link Event.Result#DENY}, phantoms will never be spawned;
    • - *
    • If the result is {@link Event.Result#DEFAULT}, vanilla checks will be run to determine if the spawn may occur.
    • - *
    + * Changes the result of this event, which controls if a spawn attempt will be made. + * + * @see PlayerSpawnPhantomsEvent.Result + */ + public void setResult(Result result) { + this.result = result; + } + + /** + * Returns the result of this event, which controls if a spawn attempt will be made. + * + * @see PlayerSpawnPhantomsEvent.Result + */ + public Result getResult() { + return result; + } + + /** + * Checks if a spawn attempt should be made by checking the current result and evaluating the vanilla conditions if necessary. + *

    + * Does not check {@link DifficultyInstance#isHarderThan(float)} or the player's {@link Stats#TIME_SINCE_REST}, as these are checked later. + * + * @param level The level that phantoms are being spawned in (the same level as the player) + * @param pos The block position of the player + * @return true if a spawn attempt should be made */ - @Override - public void setResult(Event.Result result) { - super.setResult(result); + public boolean shouldSpawnPhantoms(ServerLevel level, BlockPos pos) { + if (this.getResult() == Result.ALLOW) { + return true; + } + return this.getResult() == Result.DEFAULT && (!level.dimensionType().hasSkyLight() || pos.getY() >= level.getSeaLevel() && level.canSeeSky(pos)); + } + + /** + * Controls if the spawn attempt for the group of phantoms will occur. + *

    + * This does not guarantee the spawn attempt will be successful, since this only controls pre-checks + * before the spawn position is selected and {@link NaturalSpawner#isValidEmptySpawnBlock} is checked. + */ + public static enum Result { + /** + * A spawn attempt will always be made, bypassing all rules described in {@link #DEFAULT}. + */ + ALLOW, + + /** + * A spawn attempt will only be made if the dimension does not have skylight + * or the player's Y-level is above sea level and they can see the sky. + *

    + * Additionally, the local difficulty must be higher than a random threshold in [0, 3) + * and a random number check based on the player's {@link Stats#TIME_SINCE_REST} must succeed. + */ + DEFAULT, + + /** + * A spawn attempt will never be made. + */ + DENY; } } diff --git a/src/main/java/net/neoforged/neoforge/event/entity/player/SleepingLocationCheckEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/player/SleepingLocationCheckEvent.java deleted file mode 100644 index 5a1261496c..0000000000 --- a/src/main/java/net/neoforged/neoforge/event/entity/player/SleepingLocationCheckEvent.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.event.entity.player; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.neoforged.bus.api.Event.HasResult; -import net.neoforged.neoforge.event.entity.living.LivingEvent; - -/** - * This event is fired when game checks, if sleeping player should be still considered "in bed".
    - * Failing this check will cause player to wake up.
    - * - * This event has a result. {@link HasResult}
    - * - * setResult(ALLOW) informs game that player is still "in bed"
    - * setResult(DEFAULT) causes game to check {@link Block#isBed(BlockState, BlockGetter, BlockPos, Entity)} instead - */ -@HasResult -public class SleepingLocationCheckEvent extends LivingEvent { - private final BlockPos sleepingLocation; - - public SleepingLocationCheckEvent(LivingEntity player, BlockPos sleepingLocation) { - super(player); - this.sleepingLocation = sleepingLocation; - } - - public BlockPos getSleepingLocation() { - return sleepingLocation; - } -} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/player/SleepingTimeCheckEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/player/SleepingTimeCheckEvent.java deleted file mode 100644 index d26881c3b4..0000000000 --- a/src/main/java/net/neoforged/neoforge/event/entity/player/SleepingTimeCheckEvent.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.event.entity.player; - -import java.util.Optional; -import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.Level; -import net.neoforged.bus.api.Event.HasResult; - -/** - * This event is fired when the game checks if players can sleep at this time.
    - * Failing this check will cause sleeping players to wake up and prevent awake players from sleeping.
    - * - * This event has a result. {@link HasResult}
    - * - * setResult(ALLOW) informs game that player can sleep at this time.
    - * setResult(DEFAULT) causes game to check !{@link Level#isDay()} instead. - */ -@HasResult -public class SleepingTimeCheckEvent extends PlayerEvent { - private final Optional sleepingLocation; - - public SleepingTimeCheckEvent(Player player, Optional sleepingLocation) { - super(player); - this.sleepingLocation = sleepingLocation; - } - - /** - * Note that the sleeping location may be an approximated one. - * - * @return The player's sleeping location. - */ - public Optional getSleepingLocation() { - return sleepingLocation; - } -} diff --git a/src/main/java/net/neoforged/neoforge/event/level/BlockEvent.java b/src/main/java/net/neoforged/neoforge/event/level/BlockEvent.java index 16b632f363..71b4b7e5e4 100644 --- a/src/main/java/net/neoforged/neoforge/event/level/BlockEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/level/BlockEvent.java @@ -203,37 +203,6 @@ public boolean getForceRedstoneUpdate() { } } - /** - * Fired to check whether a non-source block can turn into a source block. - * A result of ALLOW causes a source block to be created even if the liquid - * usually doesn't do that (like lava), and a result of DENY prevents creation - * even if the liquid usually does do that (like water). - */ - @HasResult - public static class CreateFluidSourceEvent extends Event { - private final Level level; - private final BlockPos pos; - private final BlockState state; - - public CreateFluidSourceEvent(Level level, BlockPos pos, BlockState state) { - this.level = level; - this.pos = pos; - this.state = state; - } - - public Level getLevel() { - return level; - } - - public BlockPos getPos() { - return pos; - } - - public BlockState getState() { - return state; - } - } - /** * Fired when a liquid places a block. Use {@link #setNewState(BlockState)} to change the result of * a cobblestone generator or add variants of obsidian. Alternatively, you could execute @@ -280,56 +249,6 @@ public BlockState getOriginalState() { } } - /** - * Fired when a crop block grows. See subevents. - * - */ - public static abstract class CropGrowEvent extends BlockEvent { - public CropGrowEvent(Level level, BlockPos pos, BlockState state) { - super(level, pos, state); - } - - /** - * Fired when any "growing age" blocks (for example cacti, chorus plants, or crops - * in vanilla) attempt to advance to the next growth age state during a random tick.
    - *
    - * {@link Result#DEFAULT} will pass on to the vanilla growth mechanics.
    - * {@link Result#ALLOW} will force the plant to advance a growth stage.
    - * {@link Result#DENY} will prevent the plant from advancing a growth stage.
    - *
    - * This event is not {@link ICancellableEvent}.
    - *
    - */ - @HasResult - public static class Pre extends CropGrowEvent { - public Pre(Level level, BlockPos pos, BlockState state) { - super(level, pos, state); - } - } - - /** - * Fired when "growing age" blocks (for example cacti, chorus plants, or crops - * in vanilla) have successfully grown. The block's original state is available, - * in addition to its new state.
    - *
    - * This event is not {@link ICancellableEvent}.
    - *
    - * This event does not have a result. {@link HasResult}
    - */ - public static class Post extends CropGrowEvent { - private final BlockState originalState; - - public Post(Level level, BlockPos pos, BlockState original, BlockState state) { - super(level, pos, state); - originalState = original; - } - - public BlockState getOriginalState() { - return originalState; - } - } - } - /** * Fired when when farmland gets trampled * This event is {@link ICancellableEvent} diff --git a/src/main/java/net/neoforged/neoforge/event/level/SaplingGrowTreeEvent.java b/src/main/java/net/neoforged/neoforge/event/level/BlockGrowFeatureEvent.java similarity index 54% rename from src/main/java/net/neoforged/neoforge/event/level/SaplingGrowTreeEvent.java rename to src/main/java/net/neoforged/neoforge/event/level/BlockGrowFeatureEvent.java index ba90503572..39bd77581a 100644 --- a/src/main/java/net/neoforged/neoforge/event/level/SaplingGrowTreeEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/level/BlockGrowFeatureEvent.java @@ -12,32 +12,25 @@ import net.minecraft.util.RandomSource; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; -import net.neoforged.bus.api.Event.HasResult; -import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.bus.api.ICancellableEvent; import org.jetbrains.annotations.Nullable; /** - * This event is fired whenever a sapling, fungus, mushroom or azalea grows into a tree. + * This event is fired whenever a block (like a sapling) grows into a feature (like a tree). *

    - * This event is not {@linkplain ICancellableEvent cancellable} but does {@linkplain HasResult have a result}. - * {@linkplain Result#ALLOW ALLOW} and {@linkplain Result#DEFAULT DEFAULT} will allow the sapling to grow - * using the features set on the event. - * {@linkplain Result#DENY DENY} will prevent the sapling from growing. + * In vanilla, this fires for saplings, fungi, mushrooms and azaleas. Mods may fire it for similar blocks. *

    - * This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus} - * only on the {@linkplain net.neoforged.fml.LogicalSide#SERVER logical server}. + * This event is only fired on the logical server. */ -// TODO: Rename to BlockFeatureGrowEvent in 1.20 -@HasResult -public class SaplingGrowTreeEvent extends LevelEvent { - private final RandomSource randomSource; +public class BlockGrowFeatureEvent extends LevelEvent implements ICancellableEvent { + private final RandomSource rand; private final BlockPos pos; @Nullable private Holder> feature; - public SaplingGrowTreeEvent(LevelAccessor level, RandomSource randomSource, BlockPos pos, @Nullable Holder> feature) { + public BlockGrowFeatureEvent(LevelAccessor level, RandomSource rand, BlockPos pos, @Nullable Holder> feature) { super(level); - this.randomSource = randomSource; + this.rand = rand; this.pos = pos; this.feature = feature; } @@ -45,8 +38,8 @@ public SaplingGrowTreeEvent(LevelAccessor level, RandomSource randomSource, Bloc /** * {@return the random source which initiated the sapling growth} */ - public RandomSource getRandomSource() { - return this.randomSource; + public RandomSource getRandom() { + return this.rand; } /** @@ -65,16 +58,28 @@ public BlockPos getPos() { } /** - * @param feature a {@linkplain Holder} referencing a tree feature to be placed instead of the current feature. + * Changes the feature that will be grown. If a null feature is set, the original block will remain in place. + * + * @param feature a {@linkplain Holder} referencing a new feature to be placed instead of the current feature. */ public void setFeature(@Nullable Holder> feature) { this.feature = feature; } /** - * @param featureKey a {@linkplain ResourceKey} referencing a tree feature to be placed instead of the current feature. + * Changes the feature that will be grown. If the holder cannot be resolved, a null feature will be set. + * + * @param featureKey a {@linkplain ResourceKey} referencing a new feature to be placed instead of the current feature. */ public void setFeature(ResourceKey> featureKey) { this.feature = this.getLevel().registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE).getHolder(featureKey).orElse(null); } + + /** + * Canceling this event will prevent the feature from growing. The original block will remain in place. + */ + @Override + public void setCanceled(boolean canceled) { + ICancellableEvent.super.setCanceled(canceled); + } } diff --git a/src/main/java/net/neoforged/neoforge/event/level/block/CreateFluidSourceEvent.java b/src/main/java/net/neoforged/neoforge/event/level/block/CreateFluidSourceEvent.java new file mode 100644 index 0000000000..97143ef893 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/event/level/block/CreateFluidSourceEvent.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.event.level.block; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; +import net.neoforged.neoforge.common.extensions.IFluidStateExtension; +import net.neoforged.neoforge.event.level.BlockEvent; + +/** + * Fired when a fluid checks if nearby blocks can convert it to a source block. + *

    + * This can be used to manipulate if fluids are allowed to create sources dynamically. + */ +public class CreateFluidSourceEvent extends BlockEvent { + private final boolean vanillaResult; + private boolean canConvert; + + public CreateFluidSourceEvent(Level level, BlockPos pos, BlockState state) { + super(level, pos, state); + this.vanillaResult = state.getFluidState().canConvertToSource(level, pos); + this.canConvert = this.vanillaResult; + } + + @Override + public Level getLevel() { + return (Level) super.getLevel(); + } + + public FluidState getFluidState() { + return this.getState().getFluidState(); + } + + /** + * Returns if the fluid would normally be converted to a source block. + *

    + * This is computed by calling {@link IFluidStateExtension#canConvertToSource(Level, BlockPos)}. + */ + public boolean getVanillaResult() { + return this.vanillaResult; + } + + /** + * {@return if the fluid will be converted to a source block} + */ + public boolean canConvert() { + return this.canConvert; + } + + /** + * Sets if the fluid will be converted to a source block. + * + * @param convert True to permit the conversion, false otherwise. + */ + public void setCanConvert(boolean convert) { + this.canConvert = convert; + } +} diff --git a/src/main/java/net/neoforged/neoforge/event/level/block/CropGrowEvent.java b/src/main/java/net/neoforged/neoforge/event/level/block/CropGrowEvent.java new file mode 100644 index 0000000000..fb4c3cee70 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/event/level/block/CropGrowEvent.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.event.level.block; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.event.level.BlockEvent; + +/** + * Parent of the two crop growth events. + * + * @see CropGrowEvent.Pre + * @see CropGrowEvent.Post + */ +public abstract class CropGrowEvent extends BlockEvent { + public CropGrowEvent(Level level, BlockPos pos, BlockState state) { + super(level, pos, state); + } + + /** + * Fired when any "growing age" blocks (for example cacti, chorus plants, or crops + * in vanilla) attempt to advance to the next growth age state during a random tick. + *

    + * This event is only fired on the logical server. + */ + public static class Pre extends CropGrowEvent { + private Result result = Result.DEFAULT; + + public Pre(Level level, BlockPos pos, BlockState state) { + super(level, pos, state); + } + + /** + * Changes the result of this event. + * + * @see {@link Result} for the possible states. + */ + public void setResult(Result result) { + this.result = result; + } + + /** + * {@return the result of this event, which controls if the click will be treated as handled} + */ + public Result getResult() { + return this.result; + } + + public static enum Result { + /** + * Forces the event to make the crop grow. + *

    + * {@link CropGrowEvent.Post} will be fired after the growth. + */ + GROW, + + /** + * The crop will use its own checks to determine if it should grow. + *

    + * {@link CropGrowEvent.Post} will be fired after the growth. + */ + DEFAULT, + + /** + * Forces the event to prevent the crop from growing. + */ + DO_NOT_GROW; + } + } + + /** + * Fired when "growing age" blocks (for example cacti, chorus plants, or crops + * in vanilla) have successfully grown. The block's original state is available, + * in addition to its new state. + *

    + * This event is only fired on the logical server. + */ + public static class Post extends CropGrowEvent { + private final BlockState originalState; + + public Post(Level level, BlockPos pos, BlockState original, BlockState state) { + super(level, pos, state); + originalState = original; + } + + /** + * {@return the original state of the crop before growing} + */ + public BlockState getOriginalState() { + return originalState; + } + + /** + * {@return the new state of the crop after growing} + */ + @Override + public BlockState getState() { + return super.getState(); + } + } +} diff --git a/src/main/java/net/neoforged/neoforge/event/level/block/package-info.java b/src/main/java/net/neoforged/neoforge/event/level/block/package-info.java new file mode 100644 index 0000000000..4b200cf1fc --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/event/level/block/package-info.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +@FieldsAreNonnullByDefault +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package net.neoforged.neoforge.event.level.block; + +import javax.annotation.ParametersAreNonnullByDefault; +import net.minecraft.FieldsAreNonnullByDefault; +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/testframework/src/main/java/net/neoforged/testframework/gametest/GameTestPlayer.java b/testframework/src/main/java/net/neoforged/testframework/gametest/GameTestPlayer.java index eafdc7b220..814658dc3e 100644 --- a/testframework/src/main/java/net/neoforged/testframework/gametest/GameTestPlayer.java +++ b/testframework/src/main/java/net/neoforged/testframework/gametest/GameTestPlayer.java @@ -31,7 +31,8 @@ import net.minecraft.server.level.ServerPlayer; import net.neoforged.bus.api.Event; import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.event.entity.player.EntityItemPickupEvent; +import net.neoforged.neoforge.common.util.TriState; +import net.neoforged.neoforge.event.entity.player.ItemEntityPickupEvent; public class GameTestPlayer extends ServerPlayer implements GameTestListener { private final GameTestHelper helper; @@ -61,8 +62,8 @@ public GameTestPlayer moveToCentre() { } public GameTestPlayer preventItemPickup() { - subscribe((final EntityItemPickupEvent event) -> { - if (event.getEntity() == this) event.setCanceled(true); + subscribe((ItemEntityPickupEvent.Pre event) -> { + if (event.getPlayer() == this) event.setCanPickup(TriState.FALSE); }); return this; } diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java index 4e93c4139d..e0561f90ba 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java @@ -18,7 +18,6 @@ import net.minecraft.world.InteractionHand; import net.minecraft.world.ItemInteractionResult; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -43,7 +42,6 @@ import net.neoforged.testframework.gametest.ExtendedGameTestHelper; import net.neoforged.testframework.gametest.StructureTemplateBuilder; import net.neoforged.testframework.registration.RegistrationHelper; -import org.jetbrains.annotations.Nullable; @ForEachTest(groups = BlockTests.GROUP) public class BlockTests { @@ -89,7 +87,7 @@ protected ItemInteractionResult useItemOn(ItemStack p_316304_, BlockState state, } @Override - public Optional getRespawnPosition(BlockState state, EntityType type, LevelReader levelReader, BlockPos pos, float orientation, @Nullable LivingEntity entity) { + public Optional getRespawnPosition(BlockState state, EntityType type, LevelReader levelReader, BlockPos pos, float orientation) { // have the player respawn a block north to the location of the anchor return Optional.of(pos.getCenter().add(0, 1, 1)); } diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/PlayerEventTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/PlayerEventTests.java index 0cdb74a7c5..cb2a7d3a72 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/PlayerEventTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/PlayerEventTests.java @@ -29,6 +29,7 @@ import net.minecraft.world.level.block.Blocks; import net.neoforged.bus.api.EventPriority; import net.neoforged.neoforge.event.StatAwardEvent; +import net.neoforged.neoforge.event.entity.player.ItemEntityPickupEvent; import net.neoforged.neoforge.event.entity.player.PermissionsChangedEvent; import net.neoforged.neoforge.event.entity.player.PlayerEvent; import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; @@ -123,11 +124,11 @@ static void entityInteractEvent(final DynamicTest test) { @EmptyTemplate(floor = true) @TestHolder(description = "Tests if the ItemPickupEvent fires") public static void itemPickupEvent(final DynamicTest test) { - test.eventListeners().forge().addListener((final PlayerEvent.ItemPickupEvent event) -> { - if (event.getStack().is(Items.MELON_SEEDS)) { + test.eventListeners().forge().addListener((ItemEntityPickupEvent.Post event) -> { + if (event.getOriginalStack().is(Items.MELON_SEEDS)) { // If the event is fired and detects pickup of melon seeds, the test will be considered pass // and the player will get pumpkin seeds too - event.getEntity().addItem(new ItemStack(Items.PUMPKIN_SEEDS)); + event.getPlayer().addItem(new ItemStack(Items.PUMPKIN_SEEDS)); test.pass(); } }); diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/fluid/FluidEventTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/fluid/FluidEventTests.java index e6c78a6c7c..658b0dbf82 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/fluid/FluidEventTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/fluid/FluidEventTests.java @@ -11,8 +11,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; -import net.neoforged.bus.api.Event; -import net.neoforged.neoforge.event.level.BlockEvent; +import net.neoforged.neoforge.event.level.block.CreateFluidSourceEvent; import net.neoforged.testframework.DynamicTest; import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; @@ -23,15 +22,15 @@ public class FluidEventTests { @GameTest @TestHolder(description = "Tests if the CreateFluidSourceEvent is fired and allows modifying the result") static void createFluidSourceEvent(final DynamicTest test) { - test.eventListeners().forge().addListener((final BlockEvent.CreateFluidSourceEvent event) -> { + test.eventListeners().forge().addListener((final CreateFluidSourceEvent event) -> { final BlockState state = event.getState(); final FluidState fluidState = state.getFluidState(); if (fluidState.getType().isSame(Fluids.WATER)) { - event.setResult(Event.Result.DENY); + event.setCanConvert(false); // Place andesite on top of the sources event.getLevel().setBlock(event.getPos().above(), Blocks.ANDESITE.defaultBlockState(), 3); } else if (fluidState.getType().isSame(Fluids.LAVA)) { - event.setResult(Event.Result.ALLOW); + event.setCanConvert(true); } }); diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/level/LevelEventTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/level/LevelEventTests.java index b60f41a805..e35a92179e 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/level/LevelEventTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/level/LevelEventTests.java @@ -26,7 +26,7 @@ import net.minecraft.world.phys.AABB; import net.neoforged.neoforge.event.VanillaGameEvent; import net.neoforged.neoforge.event.level.AlterGroundEvent; -import net.neoforged.neoforge.event.level.SaplingGrowTreeEvent; +import net.neoforged.neoforge.event.level.BlockGrowFeatureEvent; import net.neoforged.testframework.DynamicTest; import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; @@ -39,7 +39,7 @@ public class LevelEventTests { @EmptyTemplate(value = "9x9x9", floor = true) @TestHolder(description = "Tests if the sapling grow tree event is fired, replacing spruce with birch") static void saplingGrowTreeEvent(final DynamicTest test) { - test.eventListeners().forge().addListener((final SaplingGrowTreeEvent event) -> { + test.eventListeners().forge().addListener((final BlockGrowFeatureEvent event) -> { if (event.getFeature() != null && event.getFeature().is(TreeFeatures.SPRUCE)) { event.setFeature(TreeFeatures.BIRCH_BEES_005); } diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/PotionEventTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/PotionEventTest.java index 3d6ceaa422..d83f9fcb1e 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/PotionEventTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/PotionEventTest.java @@ -8,12 +8,12 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.Ingredient; -import net.neoforged.bus.api.Event.Result; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.Mod; import net.neoforged.neoforge.event.brewing.RegisterBrewingRecipesEvent; import net.neoforged.neoforge.event.entity.living.MobEffectEvent; +import net.neoforged.neoforge.event.entity.living.MobEffectEvent.Applicable; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -36,7 +36,7 @@ public static void onPotionAdded(MobEffectEvent.Added event) { @SubscribeEvent public static void isPotionApplicable(MobEffectEvent.Applicable event) { if (!event.getEntity().getCommandSenderWorld().isClientSide) { - event.setResult(Result.ALLOW); + event.setResult(Applicable.Result.APPLY); LOGGER.info("Allowed Potion {} for Entity {}", event.getEffectInstance(), event.getEntity()); } } diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/NameplateRenderingEventTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/NameplateRenderingEventTest.java index f358478d66..76cf4e562b 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/NameplateRenderingEventTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/rendering/NameplateRenderingEventTest.java @@ -10,11 +10,11 @@ import net.minecraft.world.entity.animal.Cow; import net.minecraft.world.entity.player.Player; import net.neoforged.api.distmarker.Dist; -import net.neoforged.bus.api.Event; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.Mod; import net.neoforged.neoforge.client.event.RenderNameTagEvent; +import net.neoforged.neoforge.common.util.TriState; @Mod(NameplateRenderingEventTest.MODID) @EventBusSubscriber(value = Dist.CLIENT) @@ -30,7 +30,7 @@ public static void onNameplateRender(RenderNameTagEvent event) { if (event.getEntity() instanceof Cow) { event.setContent(Component.literal("Evil Cow").withStyle(ChatFormatting.RED)); - event.setResult(Event.Result.ALLOW); + event.setCanRender(TriState.TRUE); } if (event.getEntity() instanceof Player) { diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/entity/player/PlayerSpawnPhantomsEventTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/entity/player/PlayerSpawnPhantomsEventTest.java index 663470d4ab..83471e850b 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/entity/player/PlayerSpawnPhantomsEventTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/entity/player/PlayerSpawnPhantomsEventTest.java @@ -5,7 +5,6 @@ package net.neoforged.neoforge.oldtest.entity.player; -import net.neoforged.bus.api.Event; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.Mod; @@ -26,6 +25,6 @@ public class PlayerSpawnPhantomsEventTest { public static void onPhantomsSpawn(PlayerSpawnPhantomsEvent event) { if (!ENABLE) return; event.setPhantomsToSpawn(20); - event.setResult(Event.Result.ALLOW); + event.setResult(PlayerSpawnPhantomsEvent.Result.ALLOW); } }