diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/rewriter/EntityPacketRewriter1_8.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/rewriter/EntityPacketRewriter1_8.java index 51fc9e8f3..c7b783056 100644 --- a/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/rewriter/EntityPacketRewriter1_8.java +++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/rewriter/EntityPacketRewriter1_8.java @@ -337,6 +337,11 @@ public void register() { map(Types.ENTITY_DATA_LIST1_8, RewindTypes.ENTITY_DATA_LIST1_7); // Entity data handler(getTrackerAndDataHandler(RewindTypes.ENTITY_DATA_LIST1_7, EntityTypes1_8.EntityType.PLAYER)); + handler(wrapper -> { + final int entityId = wrapper.get(Types.VAR_INT, 0); + final EntityTracker1_8 tracker = tracker(wrapper.user()); + tracker.checkNametagVisibility(entityId);; + }); } }); protocol.registerClientbound(ClientboundPackets1_8.SET_EQUIPPED_ITEM, new PacketHandlers() { diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/rewriter/ScoreboardPacketRewriter1_8.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/rewriter/ScoreboardPacketRewriter1_8.java index a518886be..b15476462 100644 --- a/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/rewriter/ScoreboardPacketRewriter1_8.java +++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/rewriter/ScoreboardPacketRewriter1_8.java @@ -19,6 +19,7 @@ import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5; import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10; +import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.EntityTracker1_8; import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.ScoreboardTracker; import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers; @@ -173,6 +174,7 @@ public void register() { } final ScoreboardTracker scoreboard = wrapper.user().get(ScoreboardTracker.class); + final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class); final byte mode = wrapper.passthrough(Types.BYTE); if (mode != 0 && !scoreboard.teamExists(team)) { @@ -190,6 +192,10 @@ public void register() { if (mode == 0) { scoreboard.addTeam(team); } else if (mode == 1) { + // Team removed, nametag visibility might have changed + for (String member : scoreboard.getTeamMembers(team)) { + tracker.checkNametagVisbility(member); + } scoreboard.removeTeam(team); } @@ -198,7 +204,9 @@ public void register() { wrapper.passthrough(Types.STRING); // Prefix wrapper.passthrough(Types.STRING); // Suffix wrapper.passthrough(Types.BYTE); // Friendly fire - wrapper.read(Types.STRING); // Name tag visibility + final String nameTagVisibility = wrapper.read(Types.STRING); + final String previousVisibility = scoreboard.getTeamNameTagVisibility(team); + scoreboard.setTeamNameTagVisibility(team, nameTagVisibility); byte color = wrapper.read(Types.BYTE); if (mode == 2 && scoreboard.getTeamColor(team).get() != color) { final String sidebar = scoreboard.getColorDependentSidebar().get(color); @@ -209,6 +217,13 @@ public void register() { sidebarPacket.scheduleSend(Protocol1_8To1_7_6_10.class); } scoreboard.setTeamColor(team, color); + + // Re-evaluate nametag visibility for all team members when visibility changes + if (mode == 2 && !nameTagVisibility.equals(previousVisibility)) { + for (String member : scoreboard.getTeamMembers(team)) { + tracker.checkNametagVisbility(member); + } + } } if (mode == 0 || mode == 3 || mode == 4) { byte color = scoreboard.getTeamColor(team).get(); @@ -223,6 +238,8 @@ public void register() { continue; } scoreboard.removePlayerFromTeam(entry, team); + // Player left team, nametag visibility may change + tracker.checkNametagVisbility(entry); if (entry.equals(username)) { final PacketWrapper sidebarPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user()); sidebarPacket.write(Types.BYTE, (byte) 1); @@ -231,6 +248,8 @@ public void register() { } } else { scoreboard.addPlayerToTeam(entry, team); + // Player joined team, nametag visibility may change + tracker.checkNametagVisbility(entry); if (entry.equals(username) && scoreboard.getColorDependentSidebar().containsKey(color)) { final PacketWrapper displayObjective = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user()); displayObjective.write(Types.BYTE, (byte) 1); diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/storage/EntityTracker1_8.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/storage/EntityTracker1_8.java index ad7c0bd31..07ae15967 100644 --- a/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/storage/EntityTracker1_8.java +++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/storage/EntityTracker1_8.java @@ -37,18 +37,25 @@ import com.viaversion.viaversion.libs.fastutil.objects.Object2IntOpenHashMap; import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.UUID; import java.util.logging.Level; +import com.viaversion.viarewind.api.minecraft.entitydata.EntityDataTypes1_7_6_10; +import com.viaversion.viarewind.api.type.RewindTypes; +import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5; + public class EntityTracker1_8 extends EntityTrackerBase { private final Int2ObjectMap holograms = new Int2ObjectArrayMap<>(); private final Int2IntMap vehicles = new Int2IntArrayMap(); private final Int2ObjectMap entityIdToUUID = new Int2ObjectArrayMap<>(); private final Object2IntMap entityUUIDToId = new Object2IntOpenHashMap<>(); + private final Int2IntMap playerNametagHiderEntities = new Int2IntArrayMap(); private final List entityData = new ArrayList<>(); @@ -77,6 +84,10 @@ public void removeEntity(int entityId) { holograms.remove(entityId); } + if (playerNametagHiderEntities.containsKey(entityId)) { + despawnNametagHiderEntity(entityId); + } + if (entityIdToUUID.containsKey(entityId)) { final UUID playerId = entityIdToUUID.remove(entityId); @@ -89,6 +100,7 @@ public void removeEntity(int entityId) { public void clearEntities() { super.clearEntities(); vehicles.clear(); + playerNametagHiderEntities.clear(); } @Override @@ -155,6 +167,11 @@ public void setPassenger(final int vehicleId, final int passengerId) { } else { vehicles.put(vehicleId, passengerId); } + + // Re-evaluate nametag visibility when a player entity's passenger changes + if (vehicleId != -1 && entityIdToUUID.containsKey(vehicleId)) { + checkNametagVisibility(vehicleId); + } } protected void attachEntity(final int target) { @@ -185,6 +202,83 @@ public void setSpectating(int spectating) { } } + public void checkNametagVisibility(final int entityId) { + if (!entityIdToUUID.containsKey(entityId)) return; + final boolean shouldHide = isPlayerNametagHidden(entityId); + final boolean hasServerPassenger = getPassenger(entityId) != -1; + final boolean hasSkull = playerNametagHiderEntities.containsKey(entityId); + + if (shouldHide && !hasServerPassenger && !hasSkull) { + spawnNametagHiderEntity(entityId); + } else if ((!shouldHide || hasServerPassenger) && hasSkull) { + despawnNametagHiderEntity(entityId); + } + } + + public void checkNametagVisbility(final String username) { + final GameProfileStorage profileStorage = user().get(GameProfileStorage.class); + final GameProfileStorage.GameProfile profile = profileStorage.get(username, false); + if (profile == null) return; + + final int entityId = getPlayerEntityId(profile.uuid); + if (entityId == -1) return; + + checkNametagVisibility(entityId); + } + + private boolean isPlayerNametagHidden(final int entityId) { + final UUID uuid = entityIdToUUID.get(entityId); + if (uuid == null) return false; + final GameProfileStorage profileStorage = user().get(GameProfileStorage.class); + final GameProfileStorage.GameProfile profile = profileStorage.get(uuid); + if (profile == null) return false; + return user().get(ScoreboardTracker.class).isNametagHidden(profile.name); + } + + private int getNametagHiderEntityId(final int playerEntityId) { + return Integer.MAX_VALUE - 32000 - playerEntityId; + } + + private void spawnNametagHiderEntity(final int playerEntityId) { + final int entityId = getNametagHiderEntityId(playerEntityId); + playerNametagHiderEntities.put(playerEntityId, entityId); + + final List mobData = new ArrayList<>(); + mobData.add(new EntityData(0, EntityDataTypes1_7_6_10.BYTE, (byte) 0x20)); + mobData.add(new EntityData(16, EntityDataTypes1_7_6_10.BYTE, (byte) 0)); + + final PacketWrapper spawnMob = PacketWrapper.create(ClientboundPackets1_7_2_5.ADD_MOB, user()); + spawnMob.write(Types.VAR_INT, entityId); + spawnMob.write(Types.UNSIGNED_BYTE, (short) EntityTypes1_8.EntityType.MAGMA_CUBE.getId()); + spawnMob.write(Types.INT, 0); // X + spawnMob.write(Types.INT, 0); // Y + spawnMob.write(Types.INT, 0); // Z + spawnMob.write(Types.BYTE, (byte) 0); // Yaw + spawnMob.write(Types.BYTE, (byte) 0); // Pitch + spawnMob.write(Types.BYTE, (byte) 0); // Head yaw + spawnMob.write(Types.SHORT, (short) 0); // Velocity x + spawnMob.write(Types.SHORT, (short) 0); // Velocity y + spawnMob.write(Types.SHORT, (short) 0); // Velocity z + spawnMob.write(RewindTypes.ENTITY_DATA_LIST1_7, mobData); + spawnMob.scheduleSend(Protocol1_8To1_7_6_10.class); + + final PacketWrapper attach = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_ENTITY_LINK, user()); + attach.write(Types.INT, entityId); + attach.write(Types.INT, playerEntityId); + attach.write(Types.BOOLEAN, false); + attach.scheduleSend(Protocol1_8To1_7_6_10.class); + } + + private void despawnNametagHiderEntity(final int playerEntityId) { + if (!playerNametagHiderEntities.containsKey(playerEntityId)) return; + final int mobId = playerNametagHiderEntities.remove(playerEntityId); + + final PacketWrapper despawn = PacketWrapper.create(ClientboundPackets1_7_2_5.REMOVE_ENTITIES, user()); + despawn.write(Types.BYTE, (byte) 1); + despawn.write(Types.INT, mobId); + despawn.scheduleSend(Protocol1_8To1_7_6_10.class); + } + public Int2ObjectMap getHolograms() { return holograms; } diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/storage/ScoreboardTracker.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/storage/ScoreboardTracker.java index 32ae65eed..40a11be88 100644 --- a/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/storage/ScoreboardTracker.java +++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/storage/ScoreboardTracker.java @@ -37,6 +37,7 @@ public class ScoreboardTracker extends StoredObject { private final HashMap teamColors = new HashMap<>(); private final HashSet scoreTeamNames = new HashSet<>(); private final HashMap colorDependentSidebar = new HashMap<>(); + private final HashMap teamNameTagVisibilities = new HashMap<>(); private String colorIndependentSidebar; public ScoreboardTracker(UserConnection user) { @@ -63,6 +64,7 @@ public void removeTeam(String team) { teams.remove(team); scoreTeams.remove(team); teamColors.remove(team); + teamNameTagVisibilities.remove(team); } public boolean teamExists(String team) { @@ -98,6 +100,27 @@ public Optional getTeam(String player) { return Optional.empty(); } + public void setTeamNameTagVisibility(String team, String visibility) { + teamNameTagVisibilities.put(team, visibility); + } + + public String getTeamNameTagVisibility(String team) { + return teamNameTagVisibilities.getOrDefault(team, "always"); + } + + public boolean isNametagHidden(String username) { + for (Map.Entry> entry : teams.entrySet()) { + if (entry.getValue().contains(username)) { + return "never".equalsIgnoreCase(teamNameTagVisibilities.getOrDefault(entry.getKey(), "always")); + } + } + return false; + } + + public List getTeamMembers(String team) { + return teams.getOrDefault(team, new ArrayList<>()); + } + public void addObjective(String name) { objectives.add(name); }