diff --git a/patches/minecraft/net/minecraft/server/level/ServerLevel.java.patch b/patches/minecraft/net/minecraft/server/level/ServerLevel.java.patch
index f66a7934dc..1ce8a5561f 100644
--- a/patches/minecraft/net/minecraft/server/level/ServerLevel.java.patch
+++ b/patches/minecraft/net/minecraft/server/level/ServerLevel.java.patch
@@ -198,6 +198,14 @@
this.f_8548_.m_6846_().m_11241_(p_8689_, p_8690_.m_20185_(), p_8690_.m_20186_(), p_8690_.m_20189_(), p_8693_ > 1.0F ? (double)(16.0F * p_8693_) : 16.0D, this.m_46472_(), new ClientboundSoundEntityPacket(p_8691_, p_8692_, p_8690_, p_8693_, p_8694_));
}
+@@ -837,6 +_,7 @@
+ }
+
+ public void m_142346_(@Nullable Entity p_143268_, GameEvent p_143269_, BlockPos p_143270_) {
++ if (net.minecraftforge.common.ForgeHooks.onVanillaGameEvent(this, p_143268_, p_143269_, p_143270_))
+ this.m_151513_(p_143268_, p_143269_, p_143270_, p_143269_.m_157827_());
+ }
+
@@ -882,6 +_,7 @@
public Explosion m_7703_(@Nullable Entity p_8653_, @Nullable DamageSource p_8654_, @Nullable ExplosionDamageCalculator p_8655_, double p_8656_, double p_8657_, double p_8658_, float p_8659_, boolean p_8660_, Explosion.BlockInteraction p_8661_) {
diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java
index b0859492bc..032dfd63ab 100644
--- a/src/main/java/net/minecraftforge/common/ForgeHooks.java
+++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java
@@ -69,6 +69,7 @@ import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.*;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.storage.WorldData;
import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.level.storage.loot.LootTable;
@@ -136,6 +137,7 @@ import net.minecraftforge.event.DifficultyChangeEvent;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.event.ItemAttributeModifierEvent;
import net.minecraftforge.event.ServerChatEvent;
+import net.minecraftforge.event.VanillaGameEvent;
import net.minecraftforge.event.entity.EntityAttributeCreationEvent;
import net.minecraftforge.event.entity.EntityAttributeModificationEvent;
import net.minecraftforge.event.entity.EntityEvent;
@@ -427,6 +429,11 @@ public class ForgeHooks
return event.getEntityItem();
}
+ public static boolean onVanillaGameEvent(Level level, @Nullable Entity cause, GameEvent vanillaEvent, BlockPos position)
+ {
+ return !MinecraftForge.EVENT_BUS.post(new VanillaGameEvent(level, cause, vanillaEvent, position));
+ }
+
@Nullable
public static Component onServerChatEvent(ServerGamePacketListenerImpl net, String raw, Component comp)
{
diff --git a/src/main/java/net/minecraftforge/event/VanillaGameEvent.java b/src/main/java/net/minecraftforge/event/VanillaGameEvent.java
new file mode 100644
index 0000000000..1f2a5cc0d9
--- /dev/null
+++ b/src/main/java/net/minecraftforge/event/VanillaGameEvent.java
@@ -0,0 +1,89 @@
+/*
+ * Minecraft Forge
+ * Copyright (c) 2016-2022.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.event;
+
+import javax.annotation.Nullable;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.gameevent.GameEvent;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.eventbus.api.Cancelable;
+import net.minecraftforge.eventbus.api.Event;
+
+/**
+ * VanillaGameEvent is fired on the server whenever one of Vanilla's {@link GameEvent GameEvents} fire.
+ *
+ * This allows for listening to Vanilla's events in a more structured and global way that is not tied to needing a block entity listener.
+ *
+ * This event is fired on the {@link MinecraftForge#EVENT_BUS}.
+ *
+ * Cancel this event to prevent Vanilla from posting the {@link GameEvent} to all nearby {@link net.minecraft.world.level.gameevent.GameEventListener GameEventListeners}.
+ **/
+@Cancelable
+public class VanillaGameEvent extends Event
+{
+ private final Level level;
+ @Nullable
+ private final Entity cause;
+ private final GameEvent vanillaEvent;
+ private final BlockPos position;
+
+ public VanillaGameEvent(Level level, @Nullable Entity cause, GameEvent vanillaEvent, BlockPos position)
+ {
+ this.level = level;
+ this.cause = cause;
+ this.vanillaEvent = vanillaEvent;
+ this.position = position;
+ }
+
+ /**
+ * @return The level the Vanilla {@link GameEvent} occurred.
+ */
+ public Level getLevel()
+ {
+ return level;
+ }
+
+ /**
+ * @return The entity that was the source or "cause" of the {@link GameEvent}.
+ */
+ @Nullable
+ public Entity getCause()
+ {
+ return cause;
+ }
+
+ /**
+ * @return The Vanilla event.
+ */
+ public GameEvent getVanillaEvent()
+ {
+ return vanillaEvent;
+ }
+
+ /**
+ * @return The position the event took place at. This may be a block or the block position of the entity targeted.
+ */
+ public BlockPos getEventPosition()
+ {
+ return position;
+ }
+}
diff --git a/src/test/java/net/minecraftforge/debug/VanillaGameEventTest.java b/src/test/java/net/minecraftforge/debug/VanillaGameEventTest.java
new file mode 100644
index 0000000000..f8cd1946cc
--- /dev/null
+++ b/src/test/java/net/minecraftforge/debug/VanillaGameEventTest.java
@@ -0,0 +1,68 @@
+/*
+ * Minecraft Forge
+ * Copyright (c) 2016-2022.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.debug;
+
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.gameevent.GameEvent;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.event.VanillaGameEvent;
+import net.minecraftforge.fml.common.Mod;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * Tests {@link VanillaGameEvent} by listening for and printing out any uses of shears in the overworld.
+ */
+@Mod(VanillaGameEventTest.MODID)
+public class VanillaGameEventTest
+{
+
+ static final String MODID = "vanilla_game_event_test";
+
+ private static final boolean ENABLED = true;
+
+ private static final Logger LOGGER = LogManager.getLogger();
+
+ public VanillaGameEventTest()
+ {
+ if (!ENABLED) return;
+
+ MinecraftForge.EVENT_BUS.addListener(this::vanillaEvent);
+ }
+
+ public void vanillaEvent(VanillaGameEvent event)
+ {
+ if (event.getVanillaEvent() == GameEvent.SHEAR && event.getLevel().dimension() == Level.OVERWORLD)
+ {
+ Entity cause = event.getCause();
+ if (cause == null)
+ {
+ //One case this will be fired is when a dispenser shears an entity like a sheep
+ LOGGER.info("Target at {} in the overworld was sheared.", event.getEventPosition());
+ }
+ else
+ {
+ //This will be fired if a player shears an entity like a sheep or carves a block like a pumpkin
+ LOGGER.info("{} sheared a target at {} in the overworld.", cause, event.getEventPosition());
+ }
+ }
+ }
+}
diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml
index fec19f8103..940683172d 100644
--- a/src/test/resources/META-INF/mods.toml
+++ b/src/test/resources/META-INF/mods.toml
@@ -16,6 +16,8 @@ license="LGPL v2.1"
modId="block_place_event_test"
[[mods]]
modId="slipperiness_test"
+[[mods]]
+ modId="vanilla_game_event_test"
[[mods]]
modId="custom_slime_block_test"
[[mods]]