Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions core/src/main/java/org/geysermc/geyser/entity/type/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -567,18 +567,16 @@ public void updateNametag(@Nullable Team team) {
}

protected void updateNametag(@Nullable Team team, boolean visible) {
if (team != null) {
String newNametag;
if (!visible) {
// The name is not visible to the session player; clear name
setNametag("", false);
return;
} else if (team != null) {
// (team) visibility is LivingEntity+, team displayName is Entity+
if (visible) {
newNametag = team.displayName(getDisplayName(true));
} else {
// The name is not visible to the session player; clear name
newNametag = "";
}
setNametag(newNametag, false);
setNametag(team.displayName(getDisplayName(true)), false);
return;
}

// The name might need to be reset: no more team!
setNametag(getDisplayName(customNameVisible), false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.GenericMath;
import org.cloudburstmc.math.vector.Vector3f;
Expand Down Expand Up @@ -205,8 +206,24 @@ protected void initializeMetadata() {

@Override
public void updateNametag(@Nullable Team team) {
// Java hides the name tag when a living entity has any passengers
// if name not visible, don't mark it as visible
updateNametag(team, team == null || team.isVisibleFor(session.getPlayerEntity().getUsername()));
updateNametag(team, passengers.isEmpty() && (team == null || team.isVisibleFor(session.getPlayerEntity().getUsername())));
}

@Override
public void setPassengers(List<Entity> passengers) {
super.setPassengers(passengers);
updateNametag(session.getWorldCache().getScoreboard().getTeamFor(teamIdentifier()));
}

@Override
public void setCustomName(EntityMetadata<Optional<Component>, ?> entityMetadata) {
// Update custom name, but reset nametag to be empty when there are passengers
super.setCustomName(entityMetadata);
if (!passengers.isEmpty()) {
setNametag("", false);
}
}

public void setLivingEntityFlags(ByteEntityMetadata entityMetadata) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public void moveAbsoluteRaw(Vector3f position, float yaw, float pitch, float hea
@Override
public void updateNametag(@Nullable Team team) {
// unlike all other LivingEntities, armor stands are not affected by team nametag visibility
super.updateNametag(team, true);
super.updateNametag(team, passengers.isEmpty());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ protected void initializeMetadata() {

// Since 1.20.60, the nametag does not show properly if this is not set :/
// The nametag does disappear properly when the player is invisible though.
dirtyMetadata.put(EntityDataTypes.NAMETAG_ALWAYS_SHOW, (byte) 1);
setNametagAlwaysShow(true);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
import org.geysermc.geyser.entity.type.living.ArmorStandEntity;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.translator.protocol.java.entity.JavaSetEntityDataTranslator;
import org.geysermc.geyser.translator.protocol.java.entity.JavaSetPassengersTranslator;
import org.geysermc.geyser.translator.protocol.java.scoreboard.JavaSetPlayerTeamTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataTypes;
Expand All @@ -40,6 +44,7 @@
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamAction;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundSetEntityDataPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundSetPassengersPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.scoreboard.ClientboundSetPlayerTeamPacket;
import org.junit.jupiter.api.Test;

Expand All @@ -48,10 +53,13 @@
import java.util.Optional;

import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacketMatch;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacketType;
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.spawnArmorStand;
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.spawnPlayerSilently;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class NameVisibilityScoreboardTest {
@Test
Expand Down Expand Up @@ -369,4 +377,97 @@ void teamsDontOverrideCustomName() {
assertNoNextPacket(context);
});
}

@Test
void anyPassengersHidePlayerName() {
mockContextScoreboard(context -> {
PlayerEntity player = spawnPlayerSilently(context, "eclipseisoffline", 2L);
spawnArmorStand(context, 3L);

context.translate(new JavaSetPassengersTranslator(), new ClientboundSetPassengersPacket(2, new int[]{3}));
// Passenger/vehicle link
assertNextPacketType(context, SetEntityLinkPacket.class);
// Passenger metadata
assertNextPacketType(context, SetEntityDataPacket.class);

// Name metadata doesn't update until called for
player.updateBedrockMetadata();
assertNextPacket(context, () -> {
SetEntityDataPacket packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.NAME, "");
return packet;
});
});
}

@Test
void anyPassengersHideArmorStandName() {
mockContextScoreboard(context -> {
ArmorStandEntity vehicle = spawnArmorStand(context, 2L);
spawnArmorStand(context, 3L);

context.translate(new JavaSetEntityDataTranslator(), new ClientboundSetEntityDataPacket(2, new EntityMetadata[]{
new ObjectEntityMetadata<>(2, MetadataTypes.OPTIONAL_COMPONENT, Optional.of(Component.text("apples are gud")))
}));

assertNextPacketMatch(context, SetEntityDataPacket.class, packet -> {
assertEquals("apples are gud", packet.getMetadata().get(EntityDataTypes.NAME));
});

context.translate(new JavaSetPassengersTranslator(), new ClientboundSetPassengersPacket(2, new int[]{3}));
// Passenger/vehicle link
assertNextPacketType(context, SetEntityLinkPacket.class);
// Passenger metadata
assertNextPacketType(context, SetEntityDataPacket.class);
vehicle.updateBedrockMetadata();
assertNextPacket(context, () -> {
SetEntityDataPacket packet = new SetEntityDataPacket();
packet.setRuntimeEntityId(2);
packet.getMetadata().put(EntityDataTypes.NAME, "");
return packet;
});
});
}

@Test
void noPassengersShowsArmorStandName() {
mockContextScoreboard(context -> {
ArmorStandEntity vehicle = spawnArmorStand(context, 2L);
spawnArmorStand(context, 3L);

JavaSetPassengersTranslator setPassengersTranslator = new JavaSetPassengersTranslator();

context.translate(setPassengersTranslator, new ClientboundSetPassengersPacket(2, new int[]{3}));
// Passenger/vehicle link
assertNextPacketType(context, SetEntityLinkPacket.class);
// Passenger metadata
assertNextPacketType(context, SetEntityDataPacket.class);

vehicle.updateBedrockMetadata();
// Since the vehicle has no custom name, no new name should be sent
assertNoNextPacket(context);

context.translate(new JavaSetEntityDataTranslator(), new ClientboundSetEntityDataPacket(2, new EntityMetadata[]{
new ObjectEntityMetadata<>(2, MetadataTypes.OPTIONAL_COMPONENT, Optional.of(Component.text("apples are gud")))
}));

// Since it's a vehicle, the custom name should not be sent
assertNextPacketMatch(context, SetEntityDataPacket.class, packet -> {
assertEquals("", packet.getMetadata().get(EntityDataTypes.NAME));
});

context.translate(setPassengersTranslator, new ClientboundSetPassengersPacket(2, new int[0]));
// Passenger/vehicle link
assertNextPacketType(context, SetEntityLinkPacket.class);
// Passenger metadata
assertNextPacketType(context, SetEntityDataPacket.class);

// No longer a vehicle, now the name is sent
vehicle.updateBedrockMetadata();
assertNextPacketMatch(context, SetEntityDataPacket.class, packet -> {
assertEquals("apples are gud", packet.getMetadata().get(EntityDataTypes.NAME));
});
});
}
}
Loading