Uploaded image for project: 'Minecraft: Java Edition'
  1. Minecraft: Java Edition
  2. MC-101372

Item pickup sound plays twice when picking up an item

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Minecraft 16w39c
    • Minecraft 1.9.2, Minecraft 1.9.3 Pre-Release 2, Minecraft 1.9.3 Pre-Release 3, Minecraft 1.9.3, Minecraft 1.9.4, Minecraft 16w20a, Minecraft 16w21a, Minecraft 16w21b, Minecraft 1.10 Pre-Release 1, Minecraft 1.10 Pre-Release 2, Minecraft 1.10, Minecraft 1.10.1, Minecraft 1.10.2, Minecraft 16w32b, Minecraft 16w35a, Minecraft 16w36a, Minecraft 16w39a, Minecraft 16w39b
    • Unconfirmed
    • Creative

      When picking up an item, the "Item plops" sound plays twice at the same time. It's subtle, but you can hear a slightly different pitch sometimes (but not all of the time; sometimes the two sound the same). This is minor but slightly irritating.

      To reproduce, simply toss an item on the ground and then pick it up again. You should here a slightly different sound than was heard in previous versions since it's actually two sounds at once; if not, try tossing it and picking it up again as there is a randomness factor to it.

      Fix

      The easiest and most logical fix would be to remove the code to play sounds from the handler for the collect item packet. All collectables already make the sound when collected, so it's redundant anyway. As an added benefit, this means that items which are set as silent will not make noise when picked up.

      NetHandlerPlayClient.handleCollectItem(SPacketCollectItem) is obfuscated as bkq/a (Lic;)V (bkq.a(ic)). The code that needs to be deleted is on lines 676 through 680.

      Debug logs

      Here's a clearer debug log (see the wiki.vg debugging article for more info; this is the config I used):

      [14:22:37.828] [Client thread/DEBUG]: Playing sound minecraft:random/pop for event minecraft:sounds/random/pop.ogg as channel 98ab48ca-89b5-4738-a9dd-6f53becd9b0e
      [14:22:37.829] [Client thread/DEBUG]: Playing sound minecraft:random/pop for event minecraft:sounds/random/pop.ogg as channel 07f2cf7b-bbc8-4a8b-867a-5cc7aab846c2
      [14:22:38.784] [Client thread/DEBUG]: Removed channel 98ab48ca-89b5-4738-a9dd-6f53becd9b0e because it's not playing anymore
      [14:22:38.784] [Client thread/DEBUG]: Removed channel 07f2cf7b-bbc8-4a8b-867a-5cc7aab846c2 because it's not playing anymore
      [14:22:41.199] [Client thread/DEBUG]: Playing sound minecraft:random/pop for event minecraft:sounds/random/pop.ogg as channel 11b01991-2c8b-4312-8e10-efb90c2b6e85
      [14:22:41.200] [Client thread/DEBUG]: Playing sound minecraft:random/pop for event minecraft:sounds/random/pop.ogg as channel f99181c0-ddff-47e7-91c0-f8f786f0b818
      [14:22:42.156] [Client thread/DEBUG]: Removed channel f99181c0-ddff-47e7-91c0-f8f786f0b818 because it's not playing anymore
      [14:22:42.157] [Client thread/DEBUG]: Removed channel 11b01991-2c8b-4312-8e10-efb90c2b6e85 because it's not playing anymore
      [14:22:44.837] [Client thread/DEBUG]: Playing sound minecraft:random/pop for event minecraft:sounds/random/pop.ogg as channel d0f9499d-1bb2-4d66-b810-94288b9f23f4
      [14:22:44.839] [Client thread/DEBUG]: Playing sound minecraft:random/pop for event minecraft:sounds/random/pop.ogg as channel e482c318-7d70-4a6c-bda9-56eccdb277a1
      [14:22:45.794] [Client thread/DEBUG]: Removed channel e482c318-7d70-4a6c-bda9-56eccdb277a1 because it's not playing anymore
      [14:22:45.794] [Client thread/DEBUG]: Removed channel d0f9499d-1bb2-4d66-b810-94288b9f23f4 because it's not playing anymore
      [14:22:47.824] [Client thread/DEBUG]: Playing sound minecraft:random/pop for event minecraft:sounds/random/pop.ogg as channel eaf20e49-3083-4cc1-8c96-ce431dfe7947
      [14:22:47.826] [Client thread/DEBUG]: Playing sound minecraft:random/pop for event minecraft:sounds/random/pop.ogg as channel d43b6f6e-78cc-40d2-8124-ee2ab8e1d084
      [14:22:48.797] [Client thread/DEBUG]: Removed channel d43b6f6e-78cc-40d2-8124-ee2ab8e1d084 because it's not playing anymore
      [14:22:48.797] [Client thread/DEBUG]: Removed channel eaf20e49-3083-4cc1-8c96-ce431dfe7947 because it's not playing anymore
      

      Note that for each item, there are two "Playing sound" entries at nearly the same time.

      This does not happen with /playsound. It only happens when items on the ground are picked up.

      Here's a snippet from the debug log when using /playsound (the bug is not seen here):

      [14:22:50.478] [Server thread/INFO]: [pokechu22: Played sound 'minecraft:entity.item.pickup' to pokechu22]
      [14:22:50.495] [Client thread/DEBUG]: Playing sound minecraft:random/pop for event minecraft:sounds/random/pop.ogg as channel e69b8134-1fea-40a3-b443-578a8985a080
      [14:22:50.496] [Client thread/INFO]: [CHAT] Played sound 'minecraft:entity.item.pickup' to pokechu22
      [14:22:51.434] [Client thread/DEBUG]: Removed channel e69b8134-1fea-40a3-b443-578a8985a080 because it's not playing anymore
      [14:22:52.380] [Server thread/INFO]: [pokechu22: Played sound 'minecraft:entity.item.pickup' to pokechu22]
      [14:22:52.389] [Client thread/DEBUG]: Playing sound minecraft:random/pop for event minecraft:sounds/random/pop.ogg as channel e789c734-5dd4-4e03-823c-985602b42f3f
      [14:22:52.391] [Client thread/INFO]: [CHAT] Played sound 'minecraft:entity.item.pickup' to pokechu22
      [14:22:53.337] [Client thread/DEBUG]: Removed channel e789c734-5dd4-4e03-823c-985602b42f3f because it's not playing anymore
      [14:22:54.229] [Server thread/INFO]: [pokechu22: Played sound 'minecraft:entity.item.pickup' to pokechu22]
      [14:22:54.232] [Client thread/DEBUG]: Playing sound minecraft:random/pop for event minecraft:sounds/random/pop.ogg as channel 62e364c0-faeb-4c00-bfde-c9eafba22e36
      [14:22:54.233] [Client thread/INFO]: [CHAT] Played sound 'minecraft:entity.item.pickup' to pokechu22
      [14:22:55.188] [Client thread/DEBUG]: Removed channel 62e364c0-faeb-4c00-bfde-c9eafba22e36 because it's not playing anymore
      [14:22:56.178] [Server thread/INFO]: [pokechu22: Played sound 'minecraft:entity.item.pickup' to pokechu22]
      [14:22:56.219] [Client thread/DEBUG]: Playing sound minecraft:random/pop for event minecraft:sounds/random/pop.ogg as channel c443fbe0-02e4-4b9d-8a25-31ba50ab7820
      [14:22:56.219] [Client thread/INFO]: [CHAT] Played sound 'minecraft:entity.item.pickup' to pokechu22
      [14:22:57.192] [Client thread/DEBUG]: Removed channel c443fbe0-02e4-4b9d-8a25-31ba50ab7820 because it's not playing anymore
      

      Note that in this log, there is only one "Playing sound" entry per sound.

      Sound recording

      I've attached a sound recording of this glitch in action (ItemPlopsGlitch.mp3). It corresponds with the above logs: 4 picking up items normally followed by 4 /playsound usages.

      Cause

      1.9.4 obfuscation is used in all places where obfuscation is provided; no 1.8 obfuscation is provided right now.

      The root cause is that there are two places where picking up an item makes a sound (ignoring /give's code for it). First, EntityItem.onCollideWithPlayer(EntityPlayer) (rr.d(zj)) calls worldObj.playSound (a(DDDLng;Lni;FFZ)). But it also sends a Collect Item packet. When that packet is processed in NetHandlerPlayClient.handleCollectItem (bkq.a(ic), a sound is also played. That duplication causes this issue; the easiest fix is just to remove one of the two (I recommend the one in handleCollectItem - the one on the item pickup side mutes the sound if the item is silent).

      But... the same code is present in 1.8 more or less, yet this issue does not occur. Why? Well... let's look at what each version does in both parts.

      NOTE THAT THE CODE AFTER THIS IS ONLY TO EXPLAIN WHY THIS DID NOT HAPPEN IN 1.8 DESPITE THE REDUNDANT CODE STILL EXISTING. THE ACTUAL BUG IS AN EASY FIX.

      EntityItem.onCollideWithPlayer

      Both have very similar calls in the middle of this method. Compare, though:

      1.8
      if (!this.isSlient()) {
          this.worldObj.playSoundAtEntity(entityIn, "random.pop", 0.2F, ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F);
      }
      
      1.9.4
      if (!this.isSilent()) {
          this.worldObj.playSound((EntityPlayer)null, entityIn.posX, entityIn.posY, entityIn.posZ, SoundEvents.entity_item_pickup, SoundCategory.PLAYERS, 0.2F, ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F);
      }
      

      Now, the changes here are probably part of some sound engine re-factoring that happened in 1.9 (and probably, the actual method names are the same in the real code). Let's look at the code for these methods:

      1.8
      /**
       * Plays a sound at the entity's position. Args: entity, sound, volume
       * (relative to 1.0), and frequency (or pitch, also relative to 1.0).
       */
      public void playSoundAtEntity(Entity p_72956_1_, String p_72956_2_, float p_72956_3_, float p_72956_4_) {
          for (int var5 = 0; var5 < this.worldAccesses.size(); ++var5) {
              ((IWorldAccess) this.worldAccesses.get(var5)).playSound(p_72956_2_, p_72956_1_.posX, p_72956_1_.posY, p_72956_1_.posZ, p_72956_3_, p_72956_4_);
          }
      }
      
      1.9.4
      public void playSound(EntityPlayer player, double x, double y, double z, SoundEvent soundIn, SoundCategory category, float volume, float pitch) {
          for (int i = 0; i < this.eventListeners.size(); ++i) {
              ((IWorldEventListener)this.eventListeners.get(i)).playSound(player, soundIn, category, x, y, z, volume, pitch);
          }
      }
      

      More renaming, but the gist is the same. IWorldAccess and IWorldEventListener (ahv) are the same thing, just renamed. There are several implementations. While it isn't entirely clear which is used when, there seems to be one that's used clientside and one that's used serverside (and a path debugging on in 1.9 that isn't important). Here are the implementations:

      WorldManager (1.8)
      /**
       * Plays the specified sound. Arg: soundName, x, y, z, volume, pitch
       */
      @Override
      public void playSound(String soundName, double x, double y, double z, float volume, float pitch) {
          this.mcServer.getConfigurationManager().sendToAllNear(x, y, z, volume > 1.0F ? (double)(16.0F * volume) : 16.0D, this.theWorldServer.provider.getDimensionId(), new S29PacketSoundEffect(soundName, x, y, z, volume, pitch));
      }
      
      RenderGlobal (1.8)
      /**
       * Plays the specified sound. Arg: soundName, x, y, z, volume, pitch
       */
      @Override
      public void playSound(String soundName, double x, double y, double z, float volume, float pitch) {
      }
      
      WorldManager (lr) (1.9.4)
      public void playSound(EntityPlayer player, SoundEvent soundIn, SoundCategory category, double x, double y, double z, float volume, float pitch) {
          this.mcServer.getPlayerList().sendToAllNearExcept(player, x, y, z, volume > 1.0F ? (double)(16.0F * volume) : 16.0D, this.theWorldServer.provider.getDimensionType().getId(), new SPacketSoundEffect(soundIn, category, x, y, z, volume, pitch));
      }
      
      RenderGlobal (bnl) (1.8)
      public void playSound(EntityPlayer player, SoundEvent soundIn, SoundCategory category, double x, double y, double z, float volume, float pitch) {
      }
      

      In both cases, the clientside implementation is empty. Now, the serverside implementation did change between 1.8 and 1.9.4, to exclude sending to a specific player, but in this case the player is null so that change doesn't matter. So, the bug probably isn't in this part of the code.

      NetHandlerPlayClient.handleCollectItem

      OK, so if the bug isn't on that part, what about NetHandlerPlayClient? Well, both have this logic:

      1.8
      if (var2 != null) {
          if (var2 instanceof EntityXPOrb) {
              this.clientWorldController.playSoundAtEntity(var2, "random.orb", 0.2F, ((this.avRandomizer.nextFloat() - this.avRandomizer.nextFloat()) * 0.7F + 1.0F) * 2.0F);
          } else {
              this.clientWorldController.playSoundAtEntity(var2, "random.pop", 0.2F, ((this.avRandomizer.nextFloat() - this.avRandomizer.nextFloat()) * 0.7F + 1.0F) * 2.0F);
          }
      
          this.gameController.effectRenderer.addEffect(new EntityPickupFX(this.clientWorldController, var2, (Entity) var3, 0.5F));
          this.clientWorldController.removeEntityFromWorld(packetIn.func_149354_c());
      }
      
      1.9.4
      if (entity != null) {
          if (entity instanceof EntityXPOrb) {
              this.clientWorldController.playSound(entity.posX, entity.posY, entity.posZ, SoundEvents.entity_experience_orb_pickup, SoundCategory.PLAYERS, 0.2F, ((this.avRandomizer.nextFloat() - this.avRandomizer.nextFloat()) * 0.7F + 1.0F) * 2.0F, false);
          } else {
              this.clientWorldController.playSound(entity.posX, entity.posY, entity.posZ, SoundEvents.entity_item_pickup, SoundCategory.PLAYERS, 0.2F, ((this.avRandomizer.nextFloat() - this.avRandomizer.nextFloat()) * 0.7F + 1.0F) * 2.0F, false);
          }
      
          this.gameController.effectRenderer.addEffect(new EntityPickupFX(this.clientWorldController, entity, entitylivingbase, 0.5F));
          this.clientWorldController.removeEntityFromWorld(packetIn.getCollectedItemEntityID());
      }
      

      Very similar, yes? But there's a notable difference... not there, though.

      See, in 1.8, clientWorldController.playSoundAtEntity refers to World.playSoundAtEntity. And, as previously shown, that goes through the IWorldAccess es. And the client version, RenderGlobal, does nothing.

      But in 1.9.4, that specific playSound method is both defined in World (aht/a (DDDLng;Lni;FFZ)V) and overriden in WorldClient (bks/a (DDDLng;Lni;FFZ)V). The World (aht) implementation does nothing:

      World.playSound (1.9.4)
      public void playSound(double x, double y, double z, SoundEvent soundIn, SoundCategory category, float volume, float pitch, boolean distanceDelay) {
      }
      

      but WorldClient (bks) does something very direct:

      WorldClient.playSound (1.9.4)
      public void playSound(double x, double y, double z, SoundEvent soundIn, SoundCategory category, float volume, float pitch, boolean distanceDelay) {
          double d0 = this.mc.getRenderViewEntity().getDistanceSq(x, y, z);
          PositionedSoundRecord positionedsoundrecord = new PositionedSoundRecord(soundIn, category, volume, pitch, (float) x, (float) y, (float) z);
      
          if (distanceDelay && d0 > 100.0D) {
              double d1 = Math.sqrt(d0) / 40.0D;
              this.mc.getSoundHandler().playDelayedSound(positionedsoundrecord, (int) (d1 * 20.0D));
          } else {
              this.mc.getSoundHandler().playSound(positionedsoundrecord);
          }
      }
      

      Note that, unlike in 1.8, that method actually works. So the playsound call there actually plays the sound, meaning it is played in both places.

      TLDR

      In both 1.8 and 1.9.4, there is logic to play the pickup sound in two places. However, in 1.8, one of the places it played the sound (when handling the pickup packet) used a method that did nothing (on the client side, which is the only place it would be called in that context). In 1.9.4, both places actually call functional methods, meaning that the sound plays twice.

            grum [Mojang] Grum (Erik Broes)
            pokechu22 [Mod] Pokechu22
            Votes:
            4 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: