-
Bug
-
Resolution: Unresolved
-
None
-
Minecraft 1.12.2, Minecraft 18w20c, Minecraft 1.14 Pre-Release 1, 21w37a, 1.20, 1.20.1, 1.20.2 Pre-release 2, 1.20.2, 23w40a, 23w42a, 23w46a, 1.20.3 Pre-Release 4, 1.20.3, 1.20.4 Release Candidate 1, 1.20.4, 24w07a, 24w11a, 1.20.5 Pre-Release 3, 1.20.5, 1.21, 1.21.1, 24w37a
-
Confirmed
-
Networking, Projectiles
The Bug:
After shooting another creative mode player with an arrow, only the person who had been fired at will be able to see the arrow after it reflects off and lands on the ground.
Steps to Reproduce:
(requres two players to test)
- Get two players to join into either an LAN, or multiplayer world
- Put Player 1 in creative mode
- Have Player 2 fire at Player 1
Observed Results:
The arrow will reflect off of Player 1, and only be visible to Player 1.
Expected Results:
The arrow would be visible to all players
Screenshots/Videos:
Code Analysis / MCP-Reborn 1.20.2
This issue comes from inside the onHitEntity() method of AbstractArrow. Inside this method, there are numerous checks done to see if and when an arrow should be discarded, but the noteworthy one is the check done near the bottom of this method-- "this.getPierceLevel()". This check specifically sees what the arrow's piercing level is before discarding the arrow, but does not account for any other factors that should allow the arrow to remain visible to the player who fired it. This leads to the described behavior of the issue where when an arrow hits a player in creative mode, it is simply discarded for the local player.
Class net.minecraft.world.entity.projectile/AbstractArrow.java / Method onHitEntity(EntityHitResult entityHitResult)
. . . if (entity.hurt(damagesource, (float)i)) { if (flag) { return; } if (entity instanceof LivingEntity) { LivingEntity livingentity = (LivingEntity)entity; if (!this.level().isClientSide && this.getPierceLevel() <= 0) { livingentity.setArrowCount(livingentity.getArrowCount() + 1); } if (this.knockback > 0) { double d0 = Math.max(0.0D, 1.0D - livingentity.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE)); Vec3 vec3 = this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D).normalize().scale((double)this.knockback * 0.6D * d0); if (vec3.lengthSqr() > 0.0D) { livingentity.push(vec3.x, 0.1D, vec3.z); } } if (!this.level().isClientSide && entity1 instanceof LivingEntity) { EnchantmentHelper.doPostHurtEffects(livingentity, entity1); EnchantmentHelper.doPostDamageEffects((LivingEntity)entity1, livingentity); } this.doPostHurtEffects(livingentity); if (entity1 != null && livingentity != entity1 && livingentity instanceof Player && entity1 instanceof ServerPlayer && !this.isSilent()) { ((ServerPlayer)entity1).connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.ARROW_HIT_PLAYER, 0.0F)); } if (!entity.isAlive() && this.piercedAndKilledEntities != null) { this.piercedAndKilledEntities.add(livingentity); } if (!this.level().isClientSide && entity1 instanceof ServerPlayer) { ServerPlayer serverplayer = (ServerPlayer)entity1; if (this.piercedAndKilledEntities != null && this.shotFromCrossbow()) { CriteriaTriggers.KILLED_BY_CROSSBOW.trigger(serverplayer, this.piercedAndKilledEntities); } else if (!entity.isAlive() && this.shotFromCrossbow()) { CriteriaTriggers.KILLED_BY_CROSSBOW.trigger(serverplayer, Arrays.asList(entity)); } } } this.playSound(this.soundEvent, 1.0F, 1.2F / (this.random.nextFloat() * 0.2F + 0.9F)); Issue Start if (this.getPierceLevel() <= 0) { this.discard(); } Issue End } . . .
Code Fix
Note: this fix also accounts for other very similar issues such as MC-194723 and MC-101723. While this issue can be fixed on it's own, it is highly recommended to solve all the issues at once.
So, what's the solution? Well, first before the arrow is discarded, we also need to verify that the target that has been hit is not in creative mode. To do this, we first check and see if the instance of the hit entity is a ServerPlayer (this is very important, as simply using the "Player.java" class does not provide the expected results) and if that is true, we then check their gamemode and see if it is creative. (My solution is a bit of an "if" statement mess, but it showcases what checks need to be made)
This fix provides the following behavior:
2023-10-06_21-27-40.mp4
if (!entity.isInvulnerable()) { if (entity instanceof ServerPlayer serverPlayer) { if (!serverPlayer.isCreative()) { if (!serverPlayer.isBlocking()) { if (this.getPierceLevel() <= 0) { this.discard(); } } } } }
Notes:
- The arrow still exists for both players, and can be retrieved by both. This is purely a visual issue.
- This applies to arrows being fired from any source, not just bows.
- Even using F3 + B will not show the arrow.