Skip to content

Commit 79ef613

Browse files
authored
Implement invisible name tag tracking in 1.8->1.7 (#666)
Fixes #518 Fixes #364
1 parent e2fcbaf commit 79ef613

4 files changed

Lines changed: 152 additions & 1 deletion

File tree

common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/rewriter/EntityPacketRewriter1_8.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,11 @@ public void register() {
337337
map(Types.ENTITY_DATA_LIST1_8, RewindTypes.ENTITY_DATA_LIST1_7); // Entity data
338338

339339
handler(getTrackerAndDataHandler(RewindTypes.ENTITY_DATA_LIST1_7, EntityTypes1_8.EntityType.PLAYER));
340+
handler(wrapper -> {
341+
final int entityId = wrapper.get(Types.VAR_INT, 0);
342+
final EntityTracker1_8 tracker = tracker(wrapper.user());
343+
tracker.checkNametagVisibility(entityId);
344+
});
340345
}
341346
});
342347
protocol.registerClientbound(ClientboundPackets1_8.SET_EQUIPPED_ITEM, new PacketHandlers() {

common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/rewriter/ScoreboardPacketRewriter1_8.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5;
2121
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
22+
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.EntityTracker1_8;
2223
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.ScoreboardTracker;
2324
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
2425
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
@@ -173,6 +174,7 @@ public void register() {
173174
}
174175

175176
final ScoreboardTracker scoreboard = wrapper.user().get(ScoreboardTracker.class);
177+
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
176178

177179
final byte mode = wrapper.passthrough(Types.BYTE);
178180
if (mode != 0 && !scoreboard.teamExists(team)) {
@@ -190,6 +192,10 @@ public void register() {
190192
if (mode == 0) {
191193
scoreboard.addTeam(team);
192194
} else if (mode == 1) {
195+
// Team removed, nametag visibility might have changed
196+
for (final String member : scoreboard.getTeamMembers(team)) {
197+
tracker.checkNametagVisbility(member);
198+
}
193199
scoreboard.removeTeam(team);
194200
}
195201

@@ -198,7 +204,9 @@ public void register() {
198204
wrapper.passthrough(Types.STRING); // Prefix
199205
wrapper.passthrough(Types.STRING); // Suffix
200206
wrapper.passthrough(Types.BYTE); // Friendly fire
201-
wrapper.read(Types.STRING); // Name tag visibility
207+
final String nameTagVisibility = wrapper.read(Types.STRING);
208+
final String previousVisibility = scoreboard.getTeamNameTagVisibility(team);
209+
scoreboard.setTeamNameTagVisibility(team, nameTagVisibility);
202210
byte color = wrapper.read(Types.BYTE);
203211
if (mode == 2 && scoreboard.getTeamColor(team).get() != color) {
204212
final String sidebar = scoreboard.getColorDependentSidebar().get(color);
@@ -209,6 +217,13 @@ public void register() {
209217
sidebarPacket.scheduleSend(Protocol1_8To1_7_6_10.class);
210218
}
211219
scoreboard.setTeamColor(team, color);
220+
221+
// Re-evaluate nametag visibility for all team members when visibility changes
222+
if (mode == 2 && !nameTagVisibility.equals(previousVisibility)) {
223+
for (final String member : scoreboard.getTeamMembers(team)) {
224+
tracker.checkNametagVisbility(member);
225+
}
226+
}
212227
}
213228
if (mode == 0 || mode == 3 || mode == 4) {
214229
byte color = scoreboard.getTeamColor(team).get();
@@ -223,6 +238,8 @@ public void register() {
223238
continue;
224239
}
225240
scoreboard.removePlayerFromTeam(entry, team);
241+
// Player left team, nametag visibility may change
242+
tracker.checkNametagVisbility(entry);
226243
if (entry.equals(username)) {
227244
final PacketWrapper sidebarPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user());
228245
sidebarPacket.write(Types.BYTE, (byte) 1);
@@ -231,6 +248,8 @@ public void register() {
231248
}
232249
} else {
233250
scoreboard.addPlayerToTeam(entry, team);
251+
// Player joined team, nametag visibility may change
252+
tracker.checkNametagVisbility(entry);
234253
if (entry.equals(username) && scoreboard.getColorDependentSidebar().containsKey(color)) {
235254
final PacketWrapper displayObjective = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user());
236255
displayObjective.write(Types.BYTE, (byte) 1);

common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/storage/EntityTracker1_8.java

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,26 @@
3737
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntOpenHashMap;
3838
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
3939
import java.util.ArrayList;
40+
import java.util.HashSet;
4041
import java.util.List;
4142
import java.util.Map;
4243
import java.util.Objects;
44+
import java.util.Set;
4345
import java.util.UUID;
4446
import java.util.logging.Level;
4547

48+
import com.viaversion.viarewind.api.minecraft.entitydata.EntityDataTypes1_7_6_10;
49+
import com.viaversion.viarewind.api.type.RewindTypes;
50+
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5;
51+
4652
public class EntityTracker1_8 extends EntityTrackerBase {
4753

4854
private final Int2ObjectMap<VirtualHologramEntity> holograms = new Int2ObjectArrayMap<>();
4955
private final Int2IntMap extraHologramIds = new Int2IntArrayMap();
5056
private final Int2IntMap vehicles = new Int2IntArrayMap();
5157
private final Int2ObjectMap<UUID> entityIdToUUID = new Int2ObjectArrayMap<>();
5258
private final Object2IntMap<UUID> entityUUIDToId = new Object2IntOpenHashMap<>();
59+
private final Int2IntMap playerNametagHiderEntities = new Int2IntArrayMap();
5360

5461
private final List<EntityData> entityData = new ArrayList<>();
5562

@@ -78,6 +85,10 @@ public void removeEntity(int entityId) {
7885
holograms.remove(entityId);
7986
}
8087

88+
if (playerNametagHiderEntities.containsKey(entityId)) {
89+
despawnNametagHiderEntity(entityId);
90+
}
91+
8192
if (entityIdToUUID.containsKey(entityId)) {
8293
final UUID playerId = entityIdToUUID.remove(entityId);
8394

@@ -92,6 +103,7 @@ public void clearEntities() {
92103
holograms.clear();
93104
extraHologramIds.clear();
94105
vehicles.clear();
106+
playerNametagHiderEntities.clear();
95107
}
96108

97109
@Override
@@ -158,6 +170,11 @@ public void setPassenger(final int vehicleId, final int passengerId) {
158170
} else {
159171
vehicles.put(vehicleId, passengerId);
160172
}
173+
174+
// Re-evaluate nametag visibility when a player entity's passenger changes
175+
if (vehicleId != -1 && entityIdToUUID.containsKey(vehicleId)) {
176+
checkNametagVisibility(vehicleId);
177+
}
161178
}
162179

163180
protected void attachEntity(final int target) {
@@ -188,6 +205,93 @@ public void setSpectating(int spectating) {
188205
}
189206
}
190207

208+
public void checkNametagVisibility(final int entityId) {
209+
if (!entityIdToUUID.containsKey(entityId)) {
210+
return;
211+
}
212+
final boolean shouldHide = isPlayerNametagHidden(entityId);
213+
final boolean hasServerPassenger = getPassenger(entityId) != -1;
214+
final boolean hasSkull = playerNametagHiderEntities.containsKey(entityId);
215+
216+
if (shouldHide && !hasServerPassenger && !hasSkull) {
217+
spawnNametagHiderEntity(entityId);
218+
} else if ((!shouldHide || hasServerPassenger) && hasSkull) {
219+
despawnNametagHiderEntity(entityId);
220+
}
221+
}
222+
223+
public void checkNametagVisbility(final String username) {
224+
final GameProfileStorage profileStorage = user().get(GameProfileStorage.class);
225+
final GameProfileStorage.GameProfile profile = profileStorage.get(username, false);
226+
if (profile == null) {
227+
return;
228+
}
229+
230+
final int entityId = getPlayerEntityId(profile.uuid);
231+
if (entityId == -1) {
232+
return;
233+
}
234+
235+
checkNametagVisibility(entityId);
236+
}
237+
238+
private boolean isPlayerNametagHidden(final int entityId) {
239+
final UUID uuid = entityIdToUUID.get(entityId);
240+
if (uuid == null) {
241+
return false;
242+
}
243+
final GameProfileStorage profileStorage = user().get(GameProfileStorage.class);
244+
final GameProfileStorage.GameProfile profile = profileStorage.get(uuid);
245+
if (profile == null) {
246+
return false;
247+
}
248+
return user().get(ScoreboardTracker.class).isNametagHidden(profile.name);
249+
}
250+
251+
private int getNametagHiderEntityId(final int playerEntityId) {
252+
return Integer.MAX_VALUE - 32000 - playerEntityId;
253+
}
254+
255+
private void spawnNametagHiderEntity(final int playerEntityId) {
256+
final int entityId = getNametagHiderEntityId(playerEntityId);
257+
playerNametagHiderEntities.put(playerEntityId, entityId);
258+
259+
final List<EntityData> mobData = new ArrayList<>();
260+
mobData.add(new EntityData(0, EntityDataTypes1_7_6_10.BYTE, (byte) 0x20));
261+
mobData.add(new EntityData(16, EntityDataTypes1_7_6_10.BYTE, (byte) 0));
262+
263+
final PacketWrapper spawnMob = PacketWrapper.create(ClientboundPackets1_7_2_5.ADD_MOB, user());
264+
spawnMob.write(Types.VAR_INT, entityId);
265+
spawnMob.write(Types.UNSIGNED_BYTE, (short) EntityTypes1_8.EntityType.MAGMA_CUBE.getId());
266+
spawnMob.write(Types.INT, 0); // X
267+
spawnMob.write(Types.INT, 0); // Y
268+
spawnMob.write(Types.INT, 0); // Z
269+
spawnMob.write(Types.BYTE, (byte) 0); // Yaw
270+
spawnMob.write(Types.BYTE, (byte) 0); // Pitch
271+
spawnMob.write(Types.BYTE, (byte) 0); // Head yaw
272+
spawnMob.write(Types.SHORT, (short) 0); // Velocity x
273+
spawnMob.write(Types.SHORT, (short) 0); // Velocity y
274+
spawnMob.write(Types.SHORT, (short) 0); // Velocity z
275+
spawnMob.write(RewindTypes.ENTITY_DATA_LIST1_7, mobData);
276+
spawnMob.scheduleSend(Protocol1_8To1_7_6_10.class);
277+
278+
final PacketWrapper attach = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_ENTITY_LINK, user());
279+
attach.write(Types.INT, entityId);
280+
attach.write(Types.INT, playerEntityId);
281+
attach.write(Types.BOOLEAN, false);
282+
attach.scheduleSend(Protocol1_8To1_7_6_10.class);
283+
}
284+
285+
private void despawnNametagHiderEntity(final int playerEntityId) {
286+
if (!playerNametagHiderEntities.containsKey(playerEntityId)) return;
287+
final int mobId = playerNametagHiderEntities.remove(playerEntityId);
288+
289+
final PacketWrapper despawn = PacketWrapper.create(ClientboundPackets1_7_2_5.REMOVE_ENTITIES, user());
290+
despawn.write(Types.BYTE, (byte) 1);
291+
despawn.write(Types.INT, mobId);
292+
despawn.scheduleSend(Protocol1_8To1_7_6_10.class);
293+
}
294+
191295
public Int2ObjectMap<VirtualHologramEntity> getHolograms() {
192296
return holograms;
193297
}

common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/storage/ScoreboardTracker.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class ScoreboardTracker extends StoredObject {
3737
private final HashMap<String, Byte> teamColors = new HashMap<>();
3838
private final HashSet<String> scoreTeamNames = new HashSet<>();
3939
private final HashMap<Byte, String> colorDependentSidebar = new HashMap<>();
40+
private final HashMap<String, String> teamNameTagVisibilities = new HashMap<>();
4041
private String colorIndependentSidebar;
4142

4243
public ScoreboardTracker(UserConnection user) {
@@ -63,6 +64,7 @@ public void removeTeam(String team) {
6364
teams.remove(team);
6465
scoreTeams.remove(team);
6566
teamColors.remove(team);
67+
teamNameTagVisibilities.remove(team);
6668
}
6769

6870
public boolean teamExists(String team) {
@@ -98,6 +100,27 @@ public Optional<String> getTeam(String player) {
98100
return Optional.empty();
99101
}
100102

103+
public void setTeamNameTagVisibility(String team, String visibility) {
104+
teamNameTagVisibilities.put(team, visibility);
105+
}
106+
107+
public String getTeamNameTagVisibility(String team) {
108+
return teamNameTagVisibilities.getOrDefault(team, "always");
109+
}
110+
111+
public boolean isNametagHidden(String username) {
112+
for (Map.Entry<String, List<String>> entry : teams.entrySet()) {
113+
if (entry.getValue().contains(username)) {
114+
return "never".equalsIgnoreCase(teamNameTagVisibilities.getOrDefault(entry.getKey(), "always"));
115+
}
116+
}
117+
return false;
118+
}
119+
120+
public List<String> getTeamMembers(String team) {
121+
return teams.getOrDefault(team, new ArrayList<>());
122+
}
123+
101124
public void addObjective(String name) {
102125
objectives.add(name);
103126
}

0 commit comments

Comments
 (0)