diff --git a/patches/minecraft/net/minecraft/server/level/ServerPlayerGameMode.java.patch b/patches/minecraft/net/minecraft/server/level/ServerPlayerGameMode.java.patch
index 1a56c7aaff..d44deb1c3d 100644
--- a/patches/minecraft/net/minecraft/server/level/ServerPlayerGameMode.java.patch
+++ b/patches/minecraft/net/minecraft/server/level/ServerPlayerGameMode.java.patch
@@ -9,7 +9,7 @@
+ if (event.isCanceled() || (!this.m_9295_() && event.getResult() == net.minecraftforge.eventbus.api.Event.Result.DENY)) {
+ return;
+ }
-+ if (!this.f_9245_.canReach(p_215120_, 1.5)) { // Vanilla check is eye-to-center distance < 6, so padding is 6 - 4.5 = 1.5
++ if (!this.f_9245_.canReachRaw(p_215120_, 1.5)) { // Vanilla uses ServerGamePacketListenerImpl.MAX_INTERACTION_DISTANCE which is 6*6, default Block reach is 4.5 so add 1.5 padding
this.m_215125_(p_215120_, false, p_215124_, "too far");
} else if (p_215120_.m_123342_() >= p_215123_) {
this.f_9245_.f_8906_.m_9829_(new ClientboundBlockUpdatePacket(p_215120_, this.f_9244_.m_8055_(p_215120_)));
diff --git a/patches/minecraft/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/patches/minecraft/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch
index f4d3d68bfe..bcceb2653b 100644
--- a/patches/minecraft/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch
+++ b/patches/minecraft/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch
@@ -31,7 +31,7 @@
BlockPos blockpos = blockhitresult.m_82425_();
Vec3 vec31 = Vec3.m_82512_(blockpos);
- if (!(this.f_9743_.m_146892_().m_82557_(vec31) > f_215198_)) {
-+ if (this.f_9743_.canReach(blockpos, 1.5)) { // Vanilla uses eye-to-center distance < 6, which implies a padding of 1.5
++ if (this.f_9743_.canReachRaw(blockpos, 1.5)) { // Vanilla uses eye-to-center distance < ServerGamePacketListenerImpl which is 6*6, default block reach is 4.5, so add 1.5 padding
Vec3 vec32 = vec3.m_82546_(vec31);
double d0 = 1.0000001D;
if (Math.abs(vec32.m_7096_()) < 1.0000001D && Math.abs(vec32.m_7098_()) < 1.0000001D && Math.abs(vec32.m_7094_()) < 1.0000001D) {
@@ -65,7 +65,7 @@
AABB aabb = entity.m_20191_();
- if (aabb.m_272282_(this.f_9743_.m_146892_()) < f_215198_) {
-+ if (this.f_9743_.canReach(entity, 3)) { // Vanilla padding is 3.0 (distSq < 6.0 * 6.0)
++ if (this.f_9743_.canReachRaw(entity, 3.0)) { // Vanilla uses MAX_INTERACTION_DISTANCE which is 6*6 default entity reach is 3.0 so add 3.0 padding
p_9866_.m_179617_(new ServerboundInteractPacket.Handler() {
private void m_143678_(InteractionHand p_143679_, ServerGamePacketListenerImpl.EntityInteraction p_143680_) {
ItemStack itemstack = ServerGamePacketListenerImpl.this.f_9743_.m_21120_(p_143679_);
diff --git a/patches/minecraft/net/minecraft/world/Container.java.patch b/patches/minecraft/net/minecraft/world/Container.java.patch
new file mode 100644
index 0000000000..e87cd414a7
--- /dev/null
+++ b/patches/minecraft/net/minecraft/world/Container.java.patch
@@ -0,0 +1,18 @@
+--- a/net/minecraft/world/Container.java
++++ b/net/minecraft/world/Container.java
+@@ -78,10 +_,14 @@
+ }
+
+ static boolean m_272074_(BlockEntity p_273154_, Player p_273222_) {
+- return m_271806_(p_273154_, p_273222_, 8);
++ return stillValidBlockEntity(p_273154_, p_273222_, p_273222_.m_21133_(net.minecraftforge.common.ForgeMod.BLOCK_REACH.get()) + 3.5); // Vanilla uses 8, default reach is 4.5 so add 3.5 padding
+ }
+
+ static boolean m_271806_(BlockEntity p_272877_, Player p_272670_, int p_273411_) {
++ return stillValidBlockEntity(p_272877_, p_272670_, (double)p_273411_);
++ }
++
++ static boolean stillValidBlockEntity(BlockEntity p_272877_, Player p_272670_, double p_273411_) {
+ Level level = p_272877_.m_58904_();
+ BlockPos blockpos = p_272877_.m_58899_();
+ if (level == null) {
diff --git a/patches/minecraft/net/minecraft/world/entity/player/Inventory.java.patch b/patches/minecraft/net/minecraft/world/entity/player/Inventory.java.patch
index 852b26cddd..fc781218c1 100644
--- a/patches/minecraft/net/minecraft/world/entity/player/Inventory.java.patch
+++ b/patches/minecraft/net/minecraft/world/entity/player/Inventory.java.patch
@@ -43,3 +43,14 @@
crashreportcategory.m_128159_("Item ID", Item.m_41393_(p_36042_.m_41720_()));
crashreportcategory.m_128159_("Item data", p_36042_.m_41773_());
crashreportcategory.m_128165_("Item name", () -> {
+@@ -530,7 +_,9 @@
+ if (this.f_35978_.m_213877_()) {
+ return false;
+ } else {
+- return !(p_36009_.m_20280_(this.f_35978_) > 64.0D);
++ // Vanilla uses 64 which is 8*8. Default reach is 3.0 so add 5.0 padding
++ double reach = p_36009_.m_21133_(net.minecraftforge.common.ForgeMod.ENTITY_REACH.get()) + 5.0;
++ return !(p_36009_.m_20280_(this.f_35978_) > reach * reach);
+ }
+ }
+
diff --git a/patches/minecraft/net/minecraft/world/entity/player/Player.java.patch b/patches/minecraft/net/minecraft/world/entity/player/Player.java.patch
index dbae851a80..d6a5e01840 100644
--- a/patches/minecraft/net/minecraft/world/entity/player/Player.java.patch
+++ b/patches/minecraft/net/minecraft/world/entity/player/Player.java.patch
@@ -251,15 +251,16 @@
}
float f4 = 0.0F;
-@@ -1121,8 +_,9 @@
+@@ -1121,8 +_,10 @@
if (flag3) {
float f3 = 1.0F + EnchantmentHelper.m_44821_(this) * f;
- for(LivingEntity livingentity : this.m_9236_().m_45976_(LivingEntity.class, p_36347_.m_20191_().m_82377_(1.0D, 0.25D, 1.0D))) {
- if (livingentity != this && livingentity != p_36347_ && !this.m_7307_(livingentity) && (!(livingentity instanceof ArmorStand) || !((ArmorStand)livingentity).m_31677_()) && this.m_20280_(livingentity) < 9.0D) {
+ for(LivingEntity livingentity : this.m_9236_().m_45976_(LivingEntity.class, this.m_21120_(InteractionHand.MAIN_HAND).getSweepHitBox(this, p_36347_))) {
-+ double entityReachSq = Mth.m_144952_(this.getEntityReach()); // Use entity reach instead of constant 9.0. Vanilla uses bottom center-to-center checks here, so don't update this to use canReach, since it uses closest-corner checks.
-+ if (livingentity != this && livingentity != p_36347_ && !this.m_7307_(livingentity) && (!(livingentity instanceof ArmorStand) || !((ArmorStand)livingentity).m_31677_()) && this.m_20280_(livingentity) < entityReachSq) {
++ // Use entity reach instead of constant 9.0. Vanilla uses bottom center-to-center checks here, so don't update this to use canReach, since it uses closest-corner checks.
++ var reach = this.m_21133_(net.minecraftforge.common.ForgeMod.ENTITY_REACH.get());
++ if (livingentity != this && livingentity != p_36347_ && !this.m_7307_(livingentity) && (!(livingentity instanceof ArmorStand) || !((ArmorStand)livingentity).m_31677_()) && this.m_20280_(livingentity) < reach * reach) {
livingentity.m_147240_((double)0.4F, (double)Mth.m_14031_(this.m_146908_() * ((float)Math.PI / 180F)), (double)(-Mth.m_14089_(this.m_146908_() * ((float)Math.PI / 180F))));
livingentity.m_6469_(this.m_269291_().m_269075_(this), f3);
}
diff --git a/patches/minecraft/net/minecraft/world/entity/vehicle/ContainerEntity.java.patch b/patches/minecraft/net/minecraft/world/entity/vehicle/ContainerEntity.java.patch
index e73bdff861..28d5e709ad 100644
--- a/patches/minecraft/net/minecraft/world/entity/vehicle/ContainerEntity.java.patch
+++ b/patches/minecraft/net/minecraft/world/entity/vehicle/ContainerEntity.java.patch
@@ -10,3 +10,11 @@
if (p_219950_ != null) {
lootparams$builder.m_287239_(p_219950_.m_36336_()).m_287286_(LootContextParams.f_81455_, p_219950_);
}
+@@ -171,6 +_,6 @@
+ }
+
+ default boolean m_219954_(Player p_219955_) {
+- return !this.m_213877_() && this.m_20182_().m_82509_(p_219955_.m_20182_(), 8.0D);
++ return !this.m_213877_() && this.m_20182_().m_82509_(p_219955_.m_20182_(), p_219955_.m_21133_(net.minecraftforge.common.ForgeMod.ENTITY_REACH.get()) + 5.0); // Vanilla uses 8 default reach is 3.0 so add 5.0 padding
+ }
+ }
diff --git a/patches/minecraft/net/minecraft/world/inventory/AbstractContainerMenu.java.patch b/patches/minecraft/net/minecraft/world/inventory/AbstractContainerMenu.java.patch
index 0745dcfdb4..763291133b 100644
--- a/patches/minecraft/net/minecraft/world/inventory/AbstractContainerMenu.java.patch
+++ b/patches/minecraft/net/minecraft/world/inventory/AbstractContainerMenu.java.patch
@@ -1,5 +1,16 @@
--- a/net/minecraft/world/inventory/AbstractContainerMenu.java
+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java
+@@ -67,7 +_,9 @@
+
+ protected static boolean m_38889_(ContainerLevelAccess p_38890_, Player p_38891_, Block p_38892_) {
+ return p_38890_.m_39299_((p_38916_, p_38917_) -> {
+- return !p_38916_.m_8055_(p_38917_).m_60713_(p_38892_) ? false : p_38891_.m_20275_((double)p_38917_.m_123341_() + 0.5D, (double)p_38917_.m_123342_() + 0.5D, (double)p_38917_.m_123343_() + 0.5D) <= 64.0D;
++ // Vanilla uses 64 which is 8*8. Default reach is 4.5 so add 3.5 padding
++ double reach = p_38891_.m_21133_(net.minecraftforge.common.ForgeMod.BLOCK_REACH.get()) + 3.5;
++ return !p_38916_.m_8055_(p_38917_).m_60713_(p_38892_) ? false : p_38891_.m_20275_((double)p_38917_.m_123341_() + 0.5D, (double)p_38917_.m_123342_() + 0.5D, (double)p_38917_.m_123343_() + 0.5D) <= reach * reach;
+ }, true);
+ }
+
@@ -229,6 +_,8 @@
ItemStack itemstack1 = p_150438_.get();
this.f_150394_.set(p_150436_, itemstack1);
diff --git a/patches/minecraft/net/minecraft/world/inventory/HorseInventoryMenu.java.patch b/patches/minecraft/net/minecraft/world/inventory/HorseInventoryMenu.java.patch
new file mode 100644
index 0000000000..cd66fc6bbe
--- /dev/null
+++ b/patches/minecraft/net/minecraft/world/inventory/HorseInventoryMenu.java.patch
@@ -0,0 +1,13 @@
+--- a/net/minecraft/world/inventory/HorseInventoryMenu.java
++++ b/net/minecraft/world/inventory/HorseInventoryMenu.java
+@@ -62,7 +_,9 @@
+ }
+
+ public boolean m_6875_(Player p_39661_) {
+- return !this.f_39654_.m_149511_(this.f_39653_) && this.f_39653_.m_6542_(p_39661_) && this.f_39654_.m_6084_() && this.f_39654_.m_20270_(p_39661_) < 8.0F;
++ // Default reach is 3 vanilla uses 8 here so add 5 padding
++ double reach = p_39661_.m_21133_(net.minecraftforge.common.ForgeMod.ENTITY_REACH.get()) + 5;
++ return !this.f_39654_.m_149511_(this.f_39653_) && this.f_39653_.m_6542_(p_39661_) && this.f_39654_.m_6084_() && this.f_39654_.m_20270_(p_39661_) < reach;
+ }
+
+ private boolean m_150577_(AbstractHorse p_150578_) {
diff --git a/patches/minecraft/net/minecraft/world/inventory/ItemCombinerMenu.java.patch b/patches/minecraft/net/minecraft/world/inventory/ItemCombinerMenu.java.patch
new file mode 100644
index 0000000000..38f297037c
--- /dev/null
+++ b/patches/minecraft/net/minecraft/world/inventory/ItemCombinerMenu.java.patch
@@ -0,0 +1,13 @@
+--- a/net/minecraft/world/inventory/ItemCombinerMenu.java
++++ b/net/minecraft/world/inventory/ItemCombinerMenu.java
+@@ -108,7 +_,9 @@
+
+ public boolean m_6875_(Player p_39780_) {
+ return this.f_39770_.m_39299_((p_39785_, p_39786_) -> {
+- return !this.m_8039_(p_39785_.m_8055_(p_39786_)) ? false : p_39780_.m_20275_((double)p_39786_.m_123341_() + 0.5D, (double)p_39786_.m_123342_() + 0.5D, (double)p_39786_.m_123343_() + 0.5D) <= 64.0D;
++ // Vanilla uses 64 which is 8*8. Default reach is 4.5 so add 3.5 padding
++ double reach = p_39780_.m_21133_(net.minecraftforge.common.ForgeMod.BLOCK_REACH.get()) + 3.5;
++ return !this.m_8039_(p_39785_.m_8055_(p_39786_)) ? false : p_39780_.m_20275_((double)p_39786_.m_123341_() + 0.5D, (double)p_39786_.m_123342_() + 0.5D, (double)p_39786_.m_123343_() + 0.5D) <= reach * reach;
+ }, true);
+ }
+
diff --git a/patches/minecraft/net/minecraft/world/item/Item.java.patch b/patches/minecraft/net/minecraft/world/item/Item.java.patch
index cc89b21953..1bcd847f46 100644
--- a/patches/minecraft/net/minecraft/world/item/Item.java.patch
+++ b/patches/minecraft/net/minecraft/world/item/Item.java.patch
@@ -109,7 +109,7 @@
float f7 = f2 * f4;
- double d0 = 5.0D;
- Vec3 vec31 = vec3.m_82520_((double)f6 * 5.0D, (double)f5 * 5.0D, (double)f7 * 5.0D);
-+ double d0 = p_41437_.getBlockReach();
++ double d0 = p_41437_.m_21133_(net.minecraftforge.common.ForgeMod.BLOCK_REACH.get()) + 0.5; // Block reach is 4.5, vanilla uses 5.0 here, so add 0.5 padding
+ Vec3 vec31 = vec3.m_82520_((double)f6 * d0, (double)f5 * d0, (double)f7 * d0);
return p_41436_.m_45547_(new ClipContext(vec3, vec31, ClipContext.Block.OUTLINE, p_41438_, p_41437_));
}
diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgePlayer.java b/src/main/java/net/minecraftforge/common/extensions/IForgePlayer.java
index 9990988d59..a946a04cdd 100644
--- a/src/main/java/net/minecraftforge/common/extensions/IForgePlayer.java
+++ b/src/main/java/net/minecraftforge/common/extensions/IForgePlayer.java
@@ -6,7 +6,6 @@
package net.minecraftforge.common.extensions;
import net.minecraft.core.BlockPos;
-import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
@@ -14,30 +13,29 @@ import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.ForgeMod;
-public interface IForgePlayer
-{
-
- private Player self()
- {
- return (Player) this;
+public interface IForgePlayer {
+ private Player self() {
+ return (Player)this;
}
/**
* The entity reach is increased by 3 for creative players, unless it is currently zero, which disables attacks and entity interactions.
+ * This comes from {@link net.minecraft.client.multiplayer.MultiPlayerGameMode#getPickRange() MultiPlayerGameMode.getPickRange()}
+ * If you want the raw value, get the attribute yourself.
* @return The entity reach of this player.
*/
- default double getEntityReach()
- {
+ default double getEntityReach() {
double range = self().getAttributeValue(ForgeMod.ENTITY_REACH.get());
return range == 0 ? 0 : range + (self().isCreative() ? 3 : 0);
}
/**
* The reach distance is increased by 0.5 for creative players, unless it is currently zero, which disables interactions.
+ * This comes from {@link net.minecraft.client.multiplayer.MultiPlayerGameMode#getPickRange() MultiPlayerGameMode.getPickRange()}
+ * If you want the raw value, get the attribute yourself.
* @return The reach distance of this player.
*/
- default double getBlockReach()
- {
+ default double getBlockReach() {
double reach = self().getAttributeValue(ForgeMod.BLOCK_REACH.get());
return reach == 0 ? 0 : reach + (self().isCreative() ? 0.5 : 0);
}
@@ -50,8 +48,7 @@ public interface IForgePlayer
* @return If the player can attack the entity.
* @apiNote Do not use for block checks, as this method uses {@link #getEntityReach()}
*/
- default boolean canReach(Vec3 entityHitVec, double padding)
- {
+ default boolean canReach(Vec3 entityHitVec, double padding) {
return self().getEyePosition().closerThan(entityHitVec, getEntityReach() + padding);
}
@@ -63,11 +60,27 @@ public interface IForgePlayer
* @return If the player can attack the passed entity.
* @apiNote Prefer using {@link #canReach(Vec3, double)} if you have a {@link HitResult} available.
*/
- default boolean canReach(Entity entity, double padding)
- {
+ default boolean canReach(Entity entity, double padding) {
return isCloseEnough(entity, getEntityReach() + padding);
}
+ /**
+ * Checks if the player can reach an entity.
+ * On the server, additional padding is added to account for movement/lag.
+ *
+ * Unlike {@link #getEntityReach()} or {@link #canReach(Entity,double)} this does not
+ * add the 3.0 creative mode implicit padding.
+ *
+ * @param entity The entity being range-checked.
+ * @param padding Extra validation distance.
+ * @return If the player can attack the passed entity.
+ * @apiNote Prefer using {@link #canReach(Vec3, double)} if you have a {@link HitResult} available.
+ */
+ default boolean canReachRaw(Entity entity, double padding) {
+ double range = self().getAttributeValue(ForgeMod.ENTITY_REACH.get()) + padding;
+ return isCloseEnough(entity, range);
+ }
+
/**
* Checks if the player can reach a block.
* On the server, additional padding is added to account for movement/lag.
@@ -75,10 +88,25 @@ public interface IForgePlayer
* @param padding Extra validation distance.
* @return If the player can interact with this location.
*/
- default boolean canReach(BlockPos pos, double padding)
- {
+ default boolean canReach(BlockPos pos, double padding) {
double reach = this.getBlockReach() + padding;
- return self().getEyePosition().distanceToSqr(Vec3.atCenterOf(pos)) < reach * reach;
+ return self().getEyePosition().distanceToSqr(Vec3.atCenterOf(pos)) <= reach * reach;
+ }
+
+ /**
+ * Checks if the player can reach a block.
+ * On the server, additional padding is added to account for movement/lag.
+ *
+ * Unlike {@link #getBlockReach()} or {@link #canReach(BlockPos,double)} this does not
+ * add the 0.5 creative mode implicit padding.
+ *
+ * @param pos The position being range-checked.
+ * @param padding Extra validation distance.
+ * @return If the player can interact with this location.
+ */
+ default boolean canReachRaw(BlockPos pos, double padding) {
+ double reach = self().getAttributeValue(ForgeMod.BLOCK_REACH.get()) + padding;
+ return self().getEyePosition().distanceToSqr(Vec3.atCenterOf(pos)) <= reach * reach;
}
/**
@@ -88,8 +116,12 @@ public interface IForgePlayer
* @return If the eye-to-center distance between this player and the passed entity is less than dist.
* @implNote This method inflates the bounding box by the pick radius, which differs from vanilla. But vanilla doesn't use the pick radius, the only entity with > 0 is AbstractHurtingProjectile.
*/
- default boolean isCloseEnough(Entity entity, double dist)
- {
+ default boolean isCloseEnough(Entity entity, double dist) {
+ // This cuses the "eye-to-closest-corner" checks, which can cause issues with servers.
+ // https://github.com/MinecraftForge/MinecraftForge/issues/9309
+ // But to not break expectations of others, its staying until a config can be set.
+ // The vanilla code is:
+ // return entity.getBoundingBox().distanceToSqr(self().getEyePosition()) < dist * dist;
Vec3 eye = self().getEyePosition();
AABB aabb = entity.getBoundingBox().inflate(entity.getPickRadius());
return aabb.distanceToSqr(eye) < dist * dist;