How I Broke Hide-and-Seek (And Then Fixed It With 13 Lines of Java)
A Minecraft modding origin story nobody requested.
Let me set the scene.
It is a Friday night. Four friends, one LAN server, and the decision to play hide-and-seek in Minecraft. It sounds great. The kind of thing that makes you forget you are an adult with deadlines. Someone builds the map. Someone writes the rules. Someone hides behind a waterfall and feels clever for about ten seconds.
Then somebody looks up.
Floating above every player's head is a nametag, bright white and visible through walls, mountains, and basically everything else in the game. Player185 is behind the waterfall. Player694 is in the basement. Player39 is standing in a hole pretending to be a cactus.
Hide-and-seek does not really work when the game tells you where everyone is.
The “I’ll Just Make a Mod” Phase
A normal person would probably look for an existing mod that hides nametags. I did that first. There were a few older ones for versions like 1.19 and below, but nothing current for 1.21 on Fabric. The options were outdated, Forge-only, or bundled with features I did not need.
So, naturally, I thought: how hard can it be?
That is usually a bad sentence to say, but I said it anyway.
The timing was actually perfect. I had just started university in 2024, and my first module was Java. That meant I could study for my degree and work on a passion project in the same language. Which sounded efficient at the time.
First Contact With Fabric
If you have never modded Minecraft before, the ecosystem looks a bit chaotic from the outside. There are APIs, build tools, mapping layers, and community docs that all assume you already know what a Mixin is.
I did not know what a Mixin was.
Fabric's documentation is good, but it is written for people who already understand the jump from "I write Java" to "I inject bytecode into a game engine at runtime." That gap is bigger than it looks.
The setup took longer than I care to admit. JDK 21, Gradle 8.14, Fabric Loom, yarn mappings, and a fabric.mod.json file that had to be exactly right or the whole thing would quietly refuse to launch. At one point I deleted my .gradle folder, my .idea folder, and pretty much all patience I had left, then reimported the project from scratch.
It worked in the end.
The Actual Problem
The thing I did not know at the start was that Minecraft does not have a toggle for nametag visibility. There is no config option. No gamerule. No clean Fabric event that says, “a nametag is about to render, do you want to stop it?”
The nametag is drawn inside renderLabelIfPresent() on EntityRenderer, which handles the floating text above almost every entity in the game. Villagers, mobs, item frames, armour stands, players, everything goes through it.
You cannot just override it cleanly. Fabric also does not ship a nametag-specific hook for this.
That is where Mixin comes in.
SpongePowered Mixin
Mixin lets you modify Minecraft's compiled classes when the game launches. You do not copy the source and edit it. You inject your own code into the existing bytecode. You choose where the injection happens, write the hook, and let the framework weave it in.
It is powerful. It is also the kind of thing that can waste two hours of your life because one annotation parameter is wrong and the error message says Mixin apply failed and nothing useful after that.
After a lot of reading, trial and error, and one embarrassing evening where I realised I had been targeting the wrong method signature, I got it working.
Here is the whole thing:
@Mixin(EntityRenderer.class)
public abstract class EntityRendererMixin {
@Inject(
method = "renderLabelIfPresent",
at = @At("HEAD"),
cancellable = true
)
private void hidePlayerNametags(
Entity entity, Text text, MatrixStack matrices,
VertexConsumerProvider vertexConsumers,
int light, float distance, CallbackInfo ci
) {
if (entity instanceof PlayerEntity) {
ci.cancel();
}
}
}
Thirteen lines. The @Inject runs at the HEAD of the method, before any original code. If the entity is a PlayerEntity, ci.cancel() stops the method completely. No label calculation. No vertex buffer writes. No draw call. Mob names, item names, and everything else still render normally.
I remember staring at it for a while after it first worked, mostly because the result felt too simple. After all that setup, the actual fix was an instanceof check and one method call.
The “Wait, That’s It?” Moment
Every programmer knows this feeling. You spend days or weeks climbing toward a problem that feels huge, and then the answer turns out to be tiny. That does not mean the problem was easy. It usually means you did the hard part already, which is figuring out exactly where the change needs to happen.
The mod itself is seven files. One mixin class, one client initialiser, one mod entrypoint, two JSON config files, a build script, and a licence. That is the whole thing. It loads quickly, adds no overhead to the render loop, and works on any server without needing server-side installation.
Shipping It
I put it on Modrinth and GitHub. I also added a CI pipeline so every push triggers a GitHub Actions build on Ubuntu 24.04 with JDK validation, Gradle wrapper verification, and artifact capture. Nothing merges without a green build. Even for a thirteen-line mod, because good habits matter more than project size.
The README also includes a troubleshooting section, because Fabric development can break in a few very specific ways and I do not want anyone else deleting their .gradle folder at midnight.
The Pause Button
Here is the honest part.
My university course moved from Java to C#. And honestly, I prefer it. The workload is getting heavier, and splitting my attention between two languages and two ecosystems was not sustainable. I kept getting mixed up in the annoying way, not the useful way. The kind where you start writing Java syntax in a C# file and then wonder why Rider is angry with you.
So the mod is on pause until around 2027 or 2028. The codebase is open, documented, and MIT-licensed. The roadmap is still the same: command toggles for server operators, friend and team visibility lists, and ports from 1.20 upward. It is just a longer-term plan now.
What I Actually Learned
This was my first Fabric mod, my first serious encounter with bytecode injection, and my first time publishing something to a package registry that other people might actually install.
The technical lesson is simple: Mixin is the right tool when the framework does not give you an event hook, and where you inject matters more than how much code you write.
The bigger lesson is that small projects teach you a lot, especially because they are small. There is nowhere to hide. The architecture is either right or it is not. The mod works or it does not. Thirteen lines means thirteen lines of accountability.