mojira.dev
MC-2157

Particles emitted from dragon egg when teleporting are not pointing in the correct direction

The Bug

When you cause a dragon egg to teleport (such as by clicking on it), the teleportation particles point in a random direction and not to the actual location of the dragon egg.

Steps to Reproduce

  • Give yourself a dragon egg and place it down.

    /give @s minecraft:dragon_egg
  • Interact with the dragon egg, (right-click by default) to make it teleport.

  • Take note of the particles produced upon doing this and what direction they're pointing in.

  • Locate where the dragon egg is and check whether the particles pointed in the correct direction.

Observed Behavior

Particles produced from a dragon egg upon interacting with it don't point in the direction of where the egg was teleported to.

Expected Behavior

Particles produced from a dragon egg upon interacting with it would point in the direction of where the egg was teleported to.

Code Analysis

Code analysis by @unknown can be found in this comment, and code analysis by @unknown can be found in this comment.

Linked issues

Attachments

Comments 85

Please clarify. What exactly is the bug here?

When you hit a placed dragons egg and it teleports, the particles emitted often do not lead in the direction the egg moved. Nor away from. In fact, it seems a little inconsistent overall, and it would be good to have them pointing in the direction of travel.

After hitting the egg directly underneath me, the attached screenshot was the result.

Confirmed in 13w05b

Is this still a concern in the current Minecraft version 1.6.4 / Launcher version 1.2.5 ? If so, please update the affected versions in order to best aid Mojang ensuring bugs are still valid in the latest releases/pre-releases.

75 more comments

Can confirm in 1.19.1.

Can confirm in 1.19.2.

Can confirm in 23w05a

Can confirm in 23w06a

Can confirm in 1.20.2 and 1.20.3 Pre-release 2. Regarding the proposed fix by ~marcono1234, there's a problem which makes it unviable, as it creates way too many single particles, and each one is sent by the server to every player (client) near the egg, which definitely would lag the server.

So the rest of this comment will adress an alternate fix, which has been already tested on my modified client and seems to work fine without creating lag. Here it is:

The concept of this fix is to basically let server tell the client that it has to create various teleport particles from one position to another. For this we are going to take advantage of the LevelEvent feature, which consists of sending packet ClientBoundLevelEventPacket from server to client (so now we are reducing from hundreds of packets to a single one), but since not everything is perfect, I had to modify level event's integer "data" parameter to use a long number, since we want to store the destination position on the packet witout altering this feature's behaviour (this is why I'm not suggesting to add another BlockPos parameter to the packet, because otherwise it would break this feature for any other part of the game using it and would add more weight), so now we can use the got destination position's BlockPos.asLong() directly at BlockDragonEgg.teleport(BlockState, Level, BlockPos) and through "LevelEvent" the client will handle the teleport particles correctly at LevelRenderer.levelEvent(int, BlockPos, int).

Code attachment depicting the classes I changed (excluding all LevelEvent methods' changes to "data" parameter, from integer numbers to long ones). — From decompiled minecraft 1.19.3 by MCP Reborn using official mapping and my own parameter names.

package net.minecraft.world.level.block;

public class DragonEggBlock extends FallingBlock {
   //...

   private void teleport(BlockState thisState, Level level, BlockPos position) {
       if (level.isClientSide) {//no need to use this method in the client anymore, make it server-side only
           return;
       }

       WorldBorder worldborder = level.getWorldBorder();

       for (int tries = 0; tries < 1000; ++tries) {
           BlockPos destPos = position.offset(level.random.nextInt(16) - level.random.nextInt(16), level.random.nextInt(8) - level.random.nextInt(8), level.random.nextInt(16) - level.random.nextInt(16));

           if (level.getBlockState(destPos).isAir() && worldborder.isWithinBounds(destPos)) {
               level.setBlock(destPos, thisState, 2);
               level.removeBlock(position, false);
               level.levelEvent(1999, destPos, position.asLong());//send DRAGON_EGG_TELEPORT LevelEvent
               return;
           }
       }
   }

   //...
}

Also, in these examples I assigned "1999" as the id of this level event type I added (also present in net.minecraft.level.block.LevelEvent.DRAGON_EGG_TELEPORT).

package net.minecraft.client.renderer;

public class LevelRenderer implements ResourceManagerReloadListener, AutoCloseable {
   //...
   public void levelEvent(int p_234305_, BlockPos eventPos, long /* instead of int */ data) {
      RandomSource randomsource = this.level.random;
      int dataAsInt = (int)data; //compatibilize with other level events
      switch (p_234305_) {
         //...
         case 1999: /* using this number for DRAGON_EGG_TELEPORT event */
             BlockPos destPos = BlockPos.of(data);//this is why we need long number

             for (int j = 0; j < 128; ++j) {//this is moved from DragonEggBlock.teleport(..)
                 double d0 = randomsource.nextDouble();
                 float f0 = (randomsource.nextFloat() - 0.5F) * 0.2F;
                 float f1 = (randomsource.nextFloat() - 0.5F) * 0.2F;
                 float f2 = (randomsource.nextFloat() - 0.5F) * 0.2F;
                 double d1 = Mth.lerp(d0, (double)destPos.getX(), (double)eventPos.getX()) + (randomsource.nextDouble() - 0.5D) + 0.5D;
                 double d2 = Mth.lerp(d0, (double)destPos.getY(), (double)eventPos.getY()) + randomsource.nextDouble() - 0.5D;
                 double d3 = Mth.lerp(d0, (double)destPos.getZ(), (double)eventPos.getZ()) + (randomsource.nextDouble() - 0.5D) + 0.5D;
                 this.level.addParticle(ParticleTypes.PORTAL, d1, d2, d3, (double)f0, (double)f1, (double)f2);
             }

             break;
          //...
      }
   }

   //...
}

In short, instead of creating the particles by the server like Marcono said, we are making the client create them to reduce packet usage, while also fixing this bug.

Jose Angel Guzman Moreno

Sonicwave

(Unassigned)

Confirmed

Gameplay

Low

Particles

12w18a, dragon_egg

Minecraft 1.4.2, Minecraft 1.4.7, Snapshot 13w05b, Minecraft 1.5, Minecraft 1.7.4, ..., 1.21.4 Pre-Release 1, 1.21.4, 25w08a, 1.21.6 Pre-Release 1, 1.21.7

Retrieved