Source: https://oliviercouthaud.com/nakedandafraid · Status: Working · 2026 · Tags: Java, Minecraft, Paper, Fabric # 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. ## 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 Figure: 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. Figure: 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 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. Figure: 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.** | Suite | What it covers | |---|---| | Gameplay, 50 checks | armor, regeneration, totems, death, chat, revive, new season, HOLD, and the BAN, KICK and SPECTATOR modes | | Mod check, 15 checks | signed reports, replays, bad signatures, deny and allow lists, hash pins | ## What I ran into, and the limits The tests do not cover the real Fabric client mod (mod scanning, the F3 lock), Simple Voice Chat audio, tab list contents, or how anything looks or sounds. The client mod, the voice-chat muting and the death sound are the parts that wait for 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. The death sound is currently the vanilla explosion at a lower pitch (0.6). The pack registers a custom sound name, and the audio file behind it is the next addition. ## 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](https://github.com/Forkei/NakedAndAfraid) [← All projects](https://oliviercouthaud.com/projects)