diff --git a/common/src/main/java/io/github/dolphinclient/DolphinClientConfigCommon.java b/common/src/main/java/io/github/dolphinclient/DolphinClientConfigCommon.java index d69d4992f..30513a0e4 100644 --- a/common/src/main/java/io/github/dolphinclient/DolphinClientConfigCommon.java +++ b/common/src/main/java/io/github/dolphinclient/DolphinClientConfigCommon.java @@ -70,6 +70,7 @@ public boolean showButton() { public final BooleanOption showOwnNametag = new BooleanOption("showOwnNametag", false); public final BooleanOption useShadows = new BooleanOption("useShadows", false); public final BooleanOption nametagBackground = new BooleanOption("nametagBackground", true); + public final BooleanOption alwaysShowEntityNametags = new BooleanOption("alwaysShowEntityNametags", true); public final ForceableBooleanOption timeChangerEnabled = new ForceableBooleanOption("enabled", false); public final IntegerOption customTime = new IntegerOption("time", 0, 0, 24000); @@ -128,6 +129,7 @@ public DolphinClientConfigCommon() { nametagOptions.add(showOwnNametag); nametagOptions.add(useShadows); nametagOptions.add(nametagBackground); + nametagOptions.add(alwaysShowEntityNametags); general.add(customWindowTitle); general.add(debugLogOutput); diff --git a/common/src/main/resources/assets/dolphinclient/default-profile.json b/common/src/main/resources/assets/dolphinclient/default-profile.json index fb84f7320..bdcccd1b3 100644 --- a/common/src/main/resources/assets/dolphinclient/default-profile.json +++ b/common/src/main/resources/assets/dolphinclient/default-profile.json @@ -31,7 +31,8 @@ "nametagOptions": { "showOwnNametag": "false", "useShadows": "true", - "nametagBackground": "false" + "nametagBackground": "false", + "alwaysShowEntityNametags": "true" }, "timeChanger": { "enabled": "false", diff --git a/common/src/main/resources/assets/dolphinclient/lang/en_us.json b/common/src/main/resources/assets/dolphinclient/lang/en_us.json index 4e855dbb0..9be78228c 100644 --- a/common/src/main/resources/assets/dolphinclient/lang/en_us.json +++ b/common/src/main/resources/assets/dolphinclient/lang/en_us.json @@ -65,6 +65,8 @@ "allocated": "Allocated", "alwaysCrit": "Always Show", "alwaysCrit.tooltip": "Whether to always emit these particles when hitting.", + "alwaysShowEntityNametags": "Always Show Entity Name Tags", + "alwaysShowEntityNametags.tooltip": "Renders name tags on named mobs without having to look at them, like in newer versions.", "alwaysShowHeadLayer": "Always Show Hat Layer", "anchorpoint": "Anchor Point", "applyBlend": "Apply Blending", diff --git a/pack/overrides/config/dolphinclient/Default.json b/pack/overrides/config/dolphinclient/Default.json index fb84f7320..bdcccd1b3 100644 --- a/pack/overrides/config/dolphinclient/Default.json +++ b/pack/overrides/config/dolphinclient/Default.json @@ -31,7 +31,8 @@ "nametagOptions": { "showOwnNametag": "false", "useShadows": "true", - "nametagBackground": "false" + "nametagBackground": "false", + "alwaysShowEntityNametags": "true" }, "timeChanger": { "enabled": "false", diff --git a/versions/1.8.9/src/main/java/io/github/dolphinclient/mixin/EntityRendererMixin.java b/versions/1.8.9/src/main/java/io/github/dolphinclient/mixin/EntityRendererMixin.java index 68dcc3f0c..6b7068948 100644 --- a/versions/1.8.9/src/main/java/io/github/dolphinclient/mixin/EntityRendererMixin.java +++ b/versions/1.8.9/src/main/java/io/github/dolphinclient/mixin/EntityRendererMixin.java @@ -23,8 +23,7 @@ package io.github.dolphinclient.mixin; -import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; @@ -35,6 +34,7 @@ import io.github.dolphinclient.modules.hypixel.LevelHeadPlacement; import io.github.dolphinclient.modules.hypixel.bedwars.BedwarsMod; import io.github.dolphinclient.util.Util; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.living.player.ClientPlayerEntity; import net.minecraft.client.render.TextRenderer; @@ -131,9 +131,21 @@ public abstract class EntityRendererMixin { /** * Pixels to lift the level so it clears the below-name scoreboard row and holograms * parked just above the username. Vanilla only draws that scoreboard row inside 10 blocks. + * + *

The scan iterates every loaded entity in a box, so the result is cached per entity + * per tick. The world's entity set can only change the outcome once per tick anyway. + * Cache layout: {@code [tick, baseAbove, belowName, nameAnchor, tagAnchor...]}; the tag + * anchors are the payload {@link LevelHeadPlacement#extraPixels} expects.

*/ @Unique private int dolphinclient$tagsAbove(Entity entity, int fontHeight, int baseAbove) { + int tick = entity.ticks; + double[] cached = dolphinclient$tagsAboveCache.get(entity.getNetworkId()); + if (cached != null && (int) cached[0] == tick && cached[1] == baseAbove) { + double[] tags = Arrays.copyOfRange(cached, 4, cached.length); + return LevelHeadPlacement.extraPixels(fontHeight, cached[2] != 0, cached[3], tags, baseAbove); + } + boolean belowName = false; if (entity instanceof PlayerEntity player && !player.isSneaking() && this.dispatcher.camera != null && player.squaredDistanceTo(this.dispatcher.camera) < 100.0) { @@ -142,13 +154,12 @@ public abstract class EntityRendererMixin { } double nameAnchor = entity.y + entity.height + 0.5; - double[] tags = null; + double[] tags = new double[4]; if (entity.world != null) { // A nametag floats up to a couple of blocks above its carrier's hitbox, so the // query has to reach below the anchor to find tags parked right above the username. Box box = new Box(entity.x - 1.0, nameAnchor - 3.0, entity.z - 1.0, entity.x + 1.0, nameAnchor + 3.0, entity.z + 1.0); - List above = new ArrayList<>(); for (Entity other : entity.world.getEntities(entity, box)) { if (other == entity || !other.hasCustomName() || !other.isCustomNameVisible()) { continue; @@ -165,19 +176,26 @@ public abstract class EntityRendererMixin { anchor -= other.height / 2.0; } if (anchor > nameAnchor + 0.05) { - above.add(anchor); - } - } - if (!above.isEmpty()) { - tags = new double[above.size()]; - for (int i = 0; i < above.size(); i++) { - tags[i] = above.get(i); + tags = Arrays.copyOf(tags, tags.length + 1); + tags[tags.length - 1] = anchor; } } } - return LevelHeadPlacement.extraPixels(fontHeight, belowName, nameAnchor, tags, baseAbove); + + tags[0] = tick; + tags[1] = baseAbove; + tags[2] = belowName ? 1 : 0; + tags[3] = nameAnchor; + if (dolphinclient$tagsAboveCache.size() > 1024) { + dolphinclient$tagsAboveCache.clear(); + } + dolphinclient$tagsAboveCache.put(entity.getNetworkId(), tags); + return LevelHeadPlacement.extraPixels(fontHeight, belowName, nameAnchor, tags.length > 4 ? Arrays.copyOfRange(tags, 4, tags.length) : null, baseAbove); } + @Unique + private static final Int2ObjectOpenHashMap dolphinclient$tagsAboveCache = new Int2ObjectOpenHashMap<>(); + @WrapWithCondition(method = "renderNameTag(Lnet/minecraft/entity/Entity;Ljava/lang/String;DDDI)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/vertex/BufferBuilder;begin(ILnet/minecraft/client/render/vertex/VertexFormat;)V")) private boolean disableNameTagBackground(BufferBuilder instance, int drawMode, VertexFormat format) { return DolphinClient.config().nametagBackground.get(); diff --git a/versions/1.8.9/src/main/java/io/github/dolphinclient/mixin/MobRendererMixin.java b/versions/1.8.9/src/main/java/io/github/dolphinclient/mixin/MobRendererMixin.java new file mode 100644 index 000000000..3b9e219ff --- /dev/null +++ b/versions/1.8.9/src/main/java/io/github/dolphinclient/mixin/MobRendererMixin.java @@ -0,0 +1,51 @@ +/* + * Copyright © 2026 DSNS + * + * This file is part of DolphinClient. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * For more information, see the LICENSE file. + */ + +package io.github.dolphinclient.mixin; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Local; +import io.github.dolphinclient.DolphinClient; +import net.minecraft.client.render.entity.EntityRenderDispatcher; +import net.minecraft.client.render.entity.MobRenderer; +import net.minecraft.entity.Entity; +import net.minecraft.entity.living.mob.MobEntity; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(MobRenderer.class) +public class MobRendererMixin { + + /** + * Vanilla 1.8 only renders a mob's name tag while it is under the crosshair; newer versions + * always show it. Fake the crosshair check to match modern behavior. The preceding + * {@code hasCustomName()} check is left untouched so unnamed mobs stay tag-free. + */ + @ModifyExpressionValue(method = "shouldRenderNameTag(Lnet/minecraft/entity/living/mob/MobEntity;)Z", at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/client/render/entity/EntityRenderDispatcher;targetEntity:Lnet/minecraft/entity/Entity;")) + private Entity dolphinclient$renderNametagsWithoutCrosshair(Entity original, @Local(argsOnly = true) MobEntity mob) { + if (DolphinClient.config().alwaysShowEntityNametags.get()) { + return mob; + } + return original; + } +} diff --git a/versions/1.8.9/src/main/resources/dolphinclient.mixins.json b/versions/1.8.9/src/main/resources/dolphinclient.mixins.json index 5ff36e34b..d831531d7 100644 --- a/versions/1.8.9/src/main/resources/dolphinclient.mixins.json +++ b/versions/1.8.9/src/main/resources/dolphinclient.mixins.json @@ -44,6 +44,7 @@ "LocalRemoteClientPlayerEntityMixin", "MinecraftClientAccessor", "MinecraftClientMixin", + "MobRendererMixin", "ParticleAccessor", "ParticleManagerMixin", "PlayerEntityMixin",