Olivier Couthaud

Naked and Afraid

Rules, anonymity and a mod check for a no-armor, one-life Minecraft server, built as a data pack, a Paper plugin and a Fabric client mod.

Why I made it

I made this for my friends. We play Minecraft together, and I wanted a version of survival where you are properly nervous the whole time. Mostly I wanted the tension of not knowing. In normal Minecraft a death shows up in chat for everybody, with your name on it. Here, when someone dies, a loud boom plays for every player and that is all. You hear it, you look around, and you start wondering whether it was the person who was standing next to you ten minutes ago. Nobody has played it yet. It might get used for a challenge later on, but for now it is built and waiting.

What it is

It is a set of rules for a Minecraft server. Wearing armor hurts you, a little every second, so nobody does. Nobody can use a totem of undying, the item that saves you from dying once. You get one life. And nobody knows who is online, who is talking, or who just died. The player who died is out, and can't talk to the living, in text or in voice, so they can't tell you either.

How it works

Minecraft lets you change some rules with a small add-on called a data pack. That covers the armor, the totems and the single life. It can't hide the list of who is online, though, or block private messages. So there is a second piece on the server, a plugin, that does the hiding and the muting. A third, optional piece runs on each player's own computer and tells the server which mods they have installed, so nobody quietly plays with a cheat mod, like an X-ray one that lets them see through walls.

The rules armor hurts, no totems, one life The secrecy no names, no chat, no join messages The check which mods does a player have installed runs on the server on the player's computer, optional
Diagram. The three parts and where each one runs.

The mod check works a bit like a bag check at a door, except on the honour system. The server asks what is in your bag, your computer answers, and a seal on the answer shows it came from you and not from someone else. A player who really wants to cheat can still lie, so I think of it as a deterrent. It stops honest mistakes and lazy cheating.

You play no names above heads Someone dies no message in chat A boom plays for everyone They are out and can't talk to you
Diagram. What happens in the game when someone dies.

What happened when I tried it

Since nobody has played it yet, I tested the server side with a script. It starts a real server, joins a fake player, and has that player put on armor, chat, die, get revived and then start a fresh round for everyone. I wrote a long list of automatic checks for it. It can't test what a human would notice, like how the boom sounds, and the player-side program and the voice-chat muting haven't been tried with real people yet.

There are things the rules can't do. A dead player can float around as a ghost and watch the living, so you would need to limit how far they can go, or stop them talking, if that matters to you. And hiding the third-person camera is up to each player's own computer, so I can't force it.

What's next

The next step is a proper try-out with my friends on a real server, including the player-side program, and a custom death sound. For now the boom is the normal Minecraft explosion, played a bit lower. If it goes well, I may run it as a proper challenge after that, but nothing is planned yet.

The problem

I built this for my friends, and it might get used for a challenge later on. Nobody has played it yet. I wanted a multiplayer survival challenge where nobody can wear armor, nobody gets a second life, and nobody knows who is online or who just died. A sound plays for everyone when someone dies, but nothing says who. A vanilla data pack can enforce the rules, but it can't hide the tab list, block /msg, or stop a dead player from talking. So the project is three parts, each doing what the others can't. Everything is configurable. It targets Minecraft Java 26.3.

What is a data pack?

A data pack is a folder of plain text files that Minecraft loads from a world's datapacks/ directory. It can add commands as .mcfunction files, run them every game tick, and change loot tables and item tags. It can't add new code or touch networking, which is why chat, the tab list and the server list need a plugin.

How it works

Paper server Data pack naa: armor damage, totem purge death -> tag naa.eliminated Plugin NakedAndAfraid chat mode, blocked commands hidden join/quit, tab list tag Plugin: elimination action SPECTATOR (default) | KICK or BAN after delay-seconds Client mod naa-client reports installed mods locks the F3 screen challenge report Simple Voice Chat (separate plugin, optional)
Diagram. Which part does what. The data pack and plugin run on the server; only the optional client mod runs on a player's computer. Simple Voice Chat is a separate plugin whose microphone packets the plugin can drop for eliminated players.
Plugin or mod, Paper or Fabric?

Paper is a Minecraft server that runs plugins, which can change what the server does and sends but never touch a player's game. Fabric is a mod loader for the game itself, so a Fabric mod runs on the player's computer. That is why the mod check needs a client mod. The server alone can't see what a player has installed.

The data pack does the rules in plain .mcfunction files. Every armor_interval ticks (default 20) it damages anyone wearing an item from the naa:worn_banned tag, by default 2.0 (one heart) of magic damage. Creative and spectator players are skipped. Totems of undying are cleared from every inventory each tick (creative players excepted). A deathCount scoreboard objective spots a death, tags the player naa.eliminated and sets spectator mode. The death sound is played at each listener's own position, so it has no origin you could walk towards. Settings live in a scoreboard and in naa:config storage, so they survive /reload.

Alivearmor hurts, no totems Diesnaa.deaths rises boom for everyone no message, no name Eliminatedtag naa.eliminated Plugin actsSPECTATOR | KICK | BAN Revive or new seasonrevive, revive_all, /naa revive, new_season
Diagram. What happens to a player from death to coming back. The data pack does the first three steps, the plugin does the fourth.

The armor rule is the one you feel straight away, and it is two numbers. Here is what they do together.

Armor dial

10 hits, up to 10 s

With the defaults (20 ticks, 2.0 damage) a full health bar of 20 points takes 10 hits, which is up to 10 seconds in armor. A tick is a twentieth of a second. This is plain arithmetic on a full health bar, with no food, potions or healing. Natural regeneration is off by default, so health does not come back between hits. Below about 10 ticks Minecraft's short invulnerability after a hit can swallow some hits, so the real rate would be lower, which is why the dial stops at 10. The timer is shared by every player, so the first hit can land any time up to one interval after you put armor on.

A new season is just a counter. new_season bumps it, and each player is reset the first time the pack sees them with an old number, which also covers people who are offline when it starts. Reset means elimination removed, back to survival, healed and fed, effects cleared, inventory and XP cleared, and teleported to the start point if I set one.

The plugin adds what a data pack can't: chat disabled or proximity-only, blocked /msg /me /say and the other whisper commands, hidden join and leave messages, tab list and server-list names, and muted chat and voice for the dead. In v1.1.0 it can also kick or ban an eliminated player after a delay, so the boom has time to play. It remembers only the bans it made, so a revive or a new season never lifts a ban an admin made by hand.

The mod check

The most interesting engineering is the optional mod check. Two seconds after a player joins, the plugin sends a random challenge. The client mod replies with its mod list (id, version, SHA-256 of each jar), signed with an Ed25519 key it generated on first run, and tells the player in chat what it sent.

What is a nonce, and what does the signature do?

A nonce is a random number used once. The server makes a new one for each join, the client signs it together with the mod list, and the server checks that the same number came back. That stops anyone replaying an old, clean report. The signature is made with a private key that never leaves the player's computer, so the server can tell the report was not changed on the way.

Plugin Client mod 1 naa:challenge { nonce (24 random bytes), policy } Scan mods SHA-256 of each jar, F3 policy applied 2 3 envelope { pub, payload { nonce, uuid, mods[] }, sig } Plugin checks sig valid | nonce matches | uuid matches | key = stored (or first use) 4 Then: mods against verification.yml, and run LOG, TELL, HOLD, KICK ... 5
Diagram. The challenge-response exchange between the plugin and the client mod. Tap or click a step and its explanation appears under the diagram. The first key seen for a player is stored (trust on first use) and a different key later is flagged. The mod list is then checked against a deny list or allowlist in verification.yml, and each outcome maps to configurable actions.

Step through one join

1. Challenge. 40 ticks after the player joins, the plugin makes 24 random bytes and sends them on the naa:challenge channel, together with the policy: whether to block F3, which F3 keys stay allowed, and a notice to show. It starts a 15 second clock.

2. The client scans. The mod applies the F3 policy straight away. Then, off the render thread, it lists every loaded mod with its id, version and the SHA-256 of its jar.

3. The signed reply. The mod puts the nonce, the player's uuid, the game version and the mod list into one payload, signs NAA1 plus the payload with its Ed25519 key, and sends it in chunks of 8000 characters. It then tells the player in chat how many mods it reported.

4. The checks. The plugin verifies the signature, that the nonce is the one it issued, and that the uuid is that player's. Then it looks up the key. The first time, it stores it. A different key later runs the on-key-changed actions.

5. The verdict. The mods are matched against denied-mods and, if the allowlist is on, allowed-mods. Anything wrong runs the on-violation actions, which by default end in a blank hold screen until an admin runs /naa release. If nothing answers within the 15 seconds, on-no-report runs instead.

What is trust on first use?

The plugin can't know a player's key before it has seen it, so it believes whatever key shows up first and writes it down. If a different key turns up for that player later, it flags it. A new computer or a reinstall does the same, which is why there is a /naa keyreset command for an admin to accept the new one.

This is a deterrent, not proof. The client belongs to the player, so a modified client can send a clean list under its own key. It catches honest mistakes and lazy cheating, and ties each report to one install.

The decision logic is simple enough to play with. This box follows the same order of checks and the same wildcard matching as the plugin, leaving out hash pins, bundled mods and the op exemption. The mod names and the player are made up, and it is a toy, not the plugin.

Try the mod check

Default responses

Mods installed (made-up list)

With the default responses, a clean report is logged as OK. A banned mod, a bad signature, a replayed report or a report for the wrong player runs on-violation: log it, tell the player, tell the admins, and freeze them behind a blank "Mod check failed" screen until an admin runs /naa release. No answer in 15 seconds runs on-no-report (log, tell the player, tell the admins). A changed key runs on-key-changed (log, tell the admins).

The game, the loader, Fabric API and the NAA mod are always allowed, as in the default always-allowed list. The deny list here is the example from the config file. In the plugin the deny list starts empty and the allowlist starts off.

The same client mod removes the F3 debug screen and most F3+key shortcuts while connected, if the server asks. F3+H stays allowed by default. It does nothing in singleplayer or on a server that doesn't ask.

What I tested

I wrote two scripted suites, 50 checks and 15 checks. Each starts its own Paper 26.3 server and drives it with a fake player built on MCProtocolLib, so nobody has to open the game.

The two test suites.
SuiteWhat it covers
Gameplay, 50 checksarmor, regeneration, totems, death, chat, revive, new season, HOLD, and the BAN, KICK and SPECTATOR modes
Mod check, 15 checkssigned reports, replays, bad signatures, deny and allow lists, hash pins

What didn't work, and the limits

The tests can't cover the real Fabric client mod (mod scanning, the F3 lock), Simple Voice Chat audio, tab list contents, or how anything looks or sounds. Nobody has played it yet, so the client mod, the voice-chat muting and the death sound haven't been tried with real players.

Hiding F5 third-person view is client-side and can't be enforced. With the data pack alone, chat, join and leave messages and the tab list aren't restricted. Spectators can fly around and watch the living, so you would need a world border or the plugin's chat rules if that matters. Tab list hiding relies on Paper's unlistPlayer, and some clients may still show names in-world.

There is no custom death sound yet. The pack registers the sound name but has no audio file behind it, so what plays is the vanilla explosion at a lower pitch (0.6).

Status and what's next

Two releases are out: v1.0.0 on 11 October 2026 and v1.1.0, with ban mode, the day after. The next step is a real try-out with my friends on a server, which would test the parts the scripts can't reach, and a proper death sound. If that goes well it may become a proper challenge later, but that isn't planned yet.

Stack
Minecraft data pack (format 121), Paper plugin on Java 25 (Maven), Fabric client mod (Gradle), Python test runners
Targets
Minecraft Java 26.3
Status
Working
License
MIT
Code
github.com/Forkei/NakedAndAfraid

← All projects