PreventArmor
Stops players wearing armor they aren't supposed to. You choose what happens to them: armor bounces straight back into their inventory, or they keep it on and take damage until they take it off.
Heavily inspired by Naked and Afraid. Runs on Paper, Folia and Fabric, 1.21.1 to 26.3.
Two modes
| Mode | Setting | What happens |
| Refuse | block-equip: true | The piece goes back into the inventory the moment it's equipped, with a message in the action bar. No damage. New installs start here. |
| Punish | block-equip: false | The armor stays on. Every few ticks the player takes damage that armor and Resistance can't soften, plus a warning. Optionally the armor is pulled off and dropped. |
Features
- Block everything or a list. Anything in an armor slot, or only the items you name.
- Exempt items. Tag an item, or give it a certain lore line, and it's ignored completely. Good for event gear and kits.
- Whitelist. Only named players, or holders of a permission, may wear blocked armor.
- Per-piece permissions. Let ranks wear helmets but not chestplates, or netherite but not diamond.
- Folia support. Each player is checked on their own region thread.
- Fabric build with the same rules and permission nodes.
Where to go next
Install it, then read Who can wear what. That page explains how bypass, whitelist and per-piece permissions fit together.
Install
Download from Modrinth.
Requirements
| Platform | Versions | Jar |
| Paper (and forks) | 1.21.1 – 26.3 | PreventArmor-paper-<version>.jar |
| Folia | 1.21.4 – 26.3 | Same jar as Paper |
| Fabric | 1.21.11 | PreventArmor-fabric-1.21.11-<version>.jar + Fabric API |
| Fabric | 26.3 | PreventArmor-fabric-26.3-<version>.jar + Fabric API |
Java 21 for 1.21.x, Java 25 for 26.x. Spigot isn't supported.
Paper and Folia
- Put the Paper jar in
plugins/.
- Start the server.
plugins/PreventArmor/config.yml is created.
- Edit it, then run
/preventarmor reload.
Fabric
- Install Fabric API.
- Put the PreventArmor jar for your exact Minecraft version in
mods/.
- Start the server. Settings are in
config/preventarmor.json.
fabric-permissions-api is bundled, so LuckPerms for Fabric just works.
Updating from 2.0
Your old config.yml keeps working as it is. The new options are all off until you add them, so an upgraded server still punishes like before. block-equip in particular counts as false when it's missing. Fresh installs get the new default file, with block-equip: true.
config.yml
Paper and Folia. plugins/PreventArmor/config.yml, applied with /preventarmor reload.
Penalty
| Key | Default | Meaning |
block-equip | true (new installs) | Refuse mode. Unauthorized armor goes back into the inventory, or on the ground if it's full. While this is on, the four options below it do nothing. |
equip-denied-message | &cYou are not allowed to wear that. | Action bar text when a piece is refused. |
damage-amount | 2.0 | Punish mode. Damage per check, in half-hearts. 0 means no damage. Negative is treated as 0. |
warning-message | &cArmor is forbidden! | Punish mode. Action bar warning. & colour codes. |
remove-armor | false | Punish mode. Also drop the offending pieces at the player's feet. |
check-interval | 20 | Ticks between checks (20 = one second). Minimum 1. |
What counts as blocked
| Key | Default | Meaning |
block-specific | false | false: anything in an armor slot is blocked, elytra, pumpkins and mob heads included. true: only items in blocked-armor. |
blocked-armor | DIAMOND_HELMET, NETHERITE_CHESTPLATE | Bukkit material names, DIAMOND_HELMET or diamond_helmet. Leave off the minecraft: prefix. Bad names are logged and skipped. |
Exemptions and access
nbt-exempt and lore-exempt have their own page. permissions and whitelist are explained in Who can wear what.
Full default file
damage-amount: 2.0
warning-message: "&cArmor is forbidden!"
remove-armor: false
block-equip: true
equip-denied-message: "&cYou are not allowed to wear that."
check-interval: 20
block-specific: false
blocked-armor:
- DIAMOND_HELMET
- NETHERITE_CHESTPLATE
nbt-exempt:
enabled: true
key: "armor_exempt"
value: "true"
lore-exempt:
enabled: true
lines:
- "ONLY NETHERITE ARMOR"
permissions:
enabled: false
whitelist:
enabled: false
players: []
permissions:
- preventarmor.whitelisted
Who can wear what
Every armor piece a player has on is checked against these steps, top to bottom. The first one that gives an answer wins.
The order
- Not blocked (see
block-specific) or exempt? Allowed.
- Has
preventarmor.bypass? Allowed.
whitelist.enabled and the player isn't on it? Refused.
permissions.enabled? Allowed only with preventarmor.wear.<slot> or preventarmor.wear.<item>.
- Otherwise: allowed if the whitelist is on (so the player got past step 3), refused if it's off.
With both whitelist and permissions off, you get the classic behaviour: nobody wears blocked armor, except preventarmor.bypass.
Whitelist
whitelist:
enabled: true
players:
- Steve
- 069a79f4-44e9-4726-a5be-fca90e38aaf5
permissions:
- preventarmor.whitelisted
- myserver.rank.knight
A player is on the list if their name (any capitalisation) or UUID is listed, or if they hold any node under permissions. Names are checked as they are now, so after a name change, either update the list or use the UUID.
This is the strict option. Off the list means no blocked armor, full stop, whatever else they have. An empty enabled list leaves only preventarmor.bypass holders able to wear anything, and the console warns you about it on reload.
Per-piece permissions
permissions:
enabled: true
Now a player needs one of these for each piece they wear:
| Node | Covers |
preventarmor.wear.head | Any blocked item in the helmet slot |
preventarmor.wear.chest | Chest slot (chestplates, elytra) |
preventarmor.wear.legs | Leggings slot |
preventarmor.wear.feet | Boots slot |
preventarmor.wear.<item id> | One item anywhere, e.g. preventarmor.wear.netherite_chestplate. Lower-case ids, no minecraft:. |
Either node is enough. Slot nodes are the quick way. Item nodes are for when only certain materials should be allowed.
Combining them
Whitelist on plus permissions on: you must be whitelisted and hold the piece's node. That's how you get “only staff, and only leather”.
whitelist:
enabled: true
permissions: [ myserver.staff ]
permissions:
enabled: true
# then: /lp group staff permission set preventarmor.wear.leather_chestplate true
Examples
| You want | Set |
| No armor for anyone | Both off. Hand out preventarmor.bypass for exceptions. |
| Only VIPs wear armor | whitelist.enabled: true, whitelist.permissions: [ group.vip ] |
| Everyone can wear helmets, nothing else | permissions.enabled: true, give the default group preventarmor.wear.head |
| Diamond is fine, netherite needs a rank | block-specific: true, list only the netherite pieces, permissions.enabled: true, give the rank preventarmor.wear.netherite_chestplate and the rest |
Exempt items
An exempt item is ignored completely: no damage, no warning, never removed or refused. Exemptions are checked before any permission or whitelist rule.
NBT tag
nbt-exempt:
enabled: true
key: "armor_exempt"
value: "true"
The item needs a string tag in the plugin's persistent data, under the preventarmor namespace. With the defaults that's preventarmor:armor_exempt = "true", compared without caring about case.
/give @s diamond_chestplate[custom_data={PublicBukkitValues:{"preventarmor:armor_exempt":"true"}}]
The key may only contain lower-case letters, digits and . _ - /. A bad key is logged and the NBT exemption turns itself off.
Lore line
lore-exempt:
enabled: true
lines:
- "ONLY NETHERITE ARMOR"
- "Event Gear"
Any lore line that matches an entry, as a whole line, makes the item exempt. Colours and formatting are ignored, so is capitalisation, and spaces at either end are trimmed. Blank entries are skipped. Otherwise every item with an empty lore line would slip through.
Anyone with creative mode or an item-editing plugin can write lore. If that matters on your server, use the NBT tag instead.
On Fabric
The Fabric build looks for the same tag in the same place, custom_data.PublicBukkitValues."preventarmor:armor_exempt", so gear made on Paper keeps working. It also accepts the key at the top level of custom_data:
/give @s diamond_chestplate[custom_data={"preventarmor:armor_exempt":"true"}]
Fabric config
config/preventarmor.json. Same options as Paper with camel-case names. Rewritten on load so new options appear, applied with /preventarmor reload.
Options
| JSON key | Paper equivalent | Default |
damageAmount | damage-amount | 2.0 |
warningMessage | warning-message | &cArmor is forbidden! |
removeArmor | remove-armor | false |
blockEquip | block-equip | true |
equipDeniedMessage | equip-denied-message | &cYou are not allowed to wear that. |
checkInterval | check-interval | 20 |
blockSpecific | block-specific | false |
blockedArmor | blocked-armor | ["diamond_helmet", "netherite_chestplate"] |
nbtExemptEnabled / nbtExemptKey / nbtExemptValue | nbt-exempt.* | true / armor_exempt / true |
loreExemptEnabled / loreExemptLines | lore-exempt.* | true / ["ONLY NETHERITE ARMOR"] |
permissionsEnabled | permissions.enabled | false |
whitelistEnabled | whitelist.enabled | false |
whitelistPlayers | whitelist.players | [] |
whitelistPermissions | whitelist.permissions | ["preventarmor.whitelisted"] |
blockedArmor takes item ids. Mod items work too: "examplemod:steel_helmet".
Permissions without a mod
With no permissions mod, every preventarmor.* node except reload is off for everyone, ops included. So bypass, whitelist nodes and per-piece nodes all need LuckPerms or similar. Whitelisting by name or UUID needs nothing extra.
Commands
Reference
| Command | Permission | What it does |
/preventarmor | preventarmor.reload | Shows usage. |
/preventarmor reload | preventarmor.reload | Reloads the config and restarts the checks for everyone online, so a new check-interval takes effect right away. |
A reload swaps the whole config in one go. Checks running at that moment see either the old settings or the new ones, never half of each.
Permissions
All permissions
| Permission | Default | What it allows |
preventarmor.reload | op | /preventarmor and /preventarmor reload. |
preventarmor.bypass | nobody | Wear anything. Skips the whitelist and per-piece rules. |
preventarmor.wear.head / chest / legs / feet | nobody | Wear blocked items in that slot. Needs permissions.enabled. |
preventarmor.wear.<item id> | nobody | Wear that one item. Needs permissions.enabled. |
preventarmor.whitelisted | nobody | The default whitelist node. Only means something while it's listed under whitelist.permissions. |
preventarmor.bypass is not given to ops by default. That's on purpose, so you can test the plugin on yourself. Grant it explicitly if staff should be immune.
Creative and spectator
Players in creative or spectator are skipped entirely: no checks, no refusals, no damage. Neither mode can be hurt, so punishing them would only strip their armor for no reason.
How it works
The check
Each online player gets their own repeating task, every check-interval ticks, starting one tick after they join. On Paper and Folia it runs on the player's entity scheduler, which on Folia is their region thread. So the plugin never touches a player from the wrong thread.
- Refuse mode: each unauthorized piece is taken off and added to the inventory. Whatever doesn't fit is dropped at the player's feet. One action bar message per check, however many pieces came off.
- Punish mode: if any piece is unauthorized, the player takes
damage-amount damage once and sees the warning. With remove-armor, each offending piece is dropped.
Refusing on equip
In refuse mode, armor isn't left on until the next check. On Paper and Folia the plugin listens to PlayerArmorChangeEvent. When a new piece goes on, it schedules a strip for the next tick on that player's own scheduler. The next tick, because the slot is still mid-update while the event fires. On Fabric, the equipment-change event queues the player, and they're handled at the end of the same server tick.
Right-click equipping, shift-clicking, dispensers, /item replace: every route ends in the slot changing, so every route is covered. The periodic check catches anything already worn when a player logs in.
The damage
Punish damage uses the starve damage type. Minecraft treats it as ignoring armor points, Protection enchantments and Resistance, so the full amount always lands. The catch: a player killed by it sees “starved to death”.
Thread safety
The config is parsed into one read-only object. A reload builds a new one and swaps it in. Checks on different Folia regions read whichever object is current, and nothing they read can change underneath them.
Integration
There's no Java API or custom event yet. Other plugins hook in through items and permissions, which is usually what you want anyway.
Making items exempt from code
Write the exempt tag yourself. Use the preventarmor namespace and whatever key the server has in nbt-exempt.key:
NamespacedKey exempt = new NamespacedKey("preventarmor", "armor_exempt");
ItemStack chest = new ItemStack(Material.DIAMOND_CHESTPLATE);
chest.editMeta(meta -> meta.getPersistentDataContainer()
.set(exempt, PersistentDataType.STRING, "true"));
Works from kits, crates, MythicMobs drops, anything that can set persistent data or custom_data. The value has to be a string. An integer or byte tag with the same name won't count.
Granting access from code
Everything else is permissions, so use your permissions plugin's API. With LuckPerms:
LuckPerms lp = LuckPermsProvider.get();
lp.getUserManager().modifyUser(player.getUniqueId(), user ->
user.data().add(Node.builder("preventarmor.wear.chest")
.expiry(Duration.ofMinutes(10))
.build()));
That gives ten minutes of chestplate access, as long as permissions.enabled is on. A timed preventarmor.bypass works the same way for a temporary free pass.
Cancelling a refusal
Not possible yet. The plugin doesn't fire an event before it acts. If you need per-situation control, exempt the item or grant a timed permission as shown above.
Building from source
JDK 21 and JDK 25 required.
git clone https://github.com/eyman26/PreventArmor.git
cd PreventArmor
./gradlew buildAll
Jars go to build/dist/. ./gradlew :paper:check also compiles the Paper code against the newest Paper API. :paper:runServer and :paper:runFolia start a dev server.
FAQ
Why can't players wear elytra or pumpkins?
With block-specific: false, anything in an armor slot counts. Switch to block-specific: true and list only the real armor pieces, or give everyone preventarmor.wear.chest with per-piece permissions on. Note that also allows chestplates.
I'm op and it still blocks me
That's expected. Ops don't get preventarmor.bypass automatically. Grant it, or switch to creative.
Why does the death message say “starved to death”?
The penalty uses starvation damage, because it's the one damage type that armor, enchantments and Resistance can't reduce. Use refuse mode if the message bothers you, since it deals no damage at all.
Does refuse mode drop armor on the ground?
Only when the inventory is full. Normally the piece goes straight back into the inventory.
Is there a bypass for a whole world?
Not built in. Most permissions plugins can grant preventarmor.bypass per world. LuckPerms does it with a world= context.
Do mob armor or armor stands count?
No. Only players are checked.
Troubleshooting
Common problems
| Problem | Try this |
| Nothing happens at all | Check the player isn't in creative or spectator, and doesn't have preventarmor.bypass. Then check whether the item is exempt by lore or tag. |
| Players take damage instead of having armor removed | block-equip is missing or false. Upgraded configs keep the old behaviour. Add block-equip: true and reload. |
| Whitelisted player still refused | With permissions.enabled on, they also need the piece's preventarmor.wear.* node. And for a whitelist by name, check they haven't renamed. |
| “whitelist is enabled but empty” in the console | Add players or nodes under whitelist, or turn it off. |
| “Invalid material in blocked-armor” | A typo or an item that doesn't exist in your version. The rest of the list still works. |
| “Invalid nbt-exempt.key” | Only lower-case letters, digits and . _ - / are allowed. The NBT exemption is off until it's fixed. |
| Fabric: bypass or per-piece nodes ignored | Fabric needs a permissions mod for those. Without one they're off for everyone. |
| Won't load on Folia | Needs 2.2.0 or newer. Older versions aren't marked Folia-compatible. |
Known limits
- No per-world settings in the plugin itself.
- No Java API or events.
- Item nodes use the vanilla id without the namespace, so two mods with an item of the same name share a node.
Support
Questions and bug reports go through the Discord.
Join the Discord
What to include in a report
- Server software and version (Paper, Folia, a fork, or Fabric)
- Your PreventArmor version
- Your
config.yml or preventarmor.json
- The permissions the affected player has (
/lp user <name> permission info)
- The console log from start-up