From 064e2fed480040a37efb50432ed2ec44ae1ab842 Mon Sep 17 00:00:00 2001 From: LexManos Date: Wed, 11 Jun 2025 14:21:17 -0700 Subject: [PATCH] Fix container screens closing when opened with an extended REACH attribute. Fixes #10560 Also fixes cases where creative mode effected reach incorrectly. --- .../level/ServerPlayerGameMode.java.patch | 2 +- .../ServerGamePacketListenerImpl.java.patch | 4 +- .../net/minecraft/world/Container.java.patch | 18 +++++ .../world/entity/player/Inventory.java.patch | 11 +++ .../world/entity/player/Player.java.patch | 7 +- .../entity/vehicle/ContainerEntity.java.patch | 8 +++ .../AbstractContainerMenu.java.patch | 11 +++ .../inventory/HorseInventoryMenu.java.patch | 13 ++++ .../inventory/ItemCombinerMenu.java.patch | 13 ++++ .../net/minecraft/world/item/Item.java.patch | 2 +- .../common/extensions/IForgePlayer.java | 72 +++++++++++++------ 11 files changed, 134 insertions(+), 27 deletions(-) create mode 100644 patches/minecraft/net/minecraft/world/Container.java.patch create mode 100644 patches/minecraft/net/minecraft/world/inventory/HorseInventoryMenu.java.patch create mode 100644 patches/minecraft/net/minecraft/world/inventory/ItemCombinerMenu.java.patch 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;