Captcha & anti-bot#
Registration is the expensive operation — it creates a row and runs BCrypt. The captcha makes a bot prove it can read a message before it can trigger either.
Configuration#
| Option | Meaning |
|---|---|
enabled |
Off by default |
on-register |
Only on registration, never on login — a returning player is never made to solve one |
max-attempts |
Wrong answers before the player is kicked (0 = never kick) |
How a player solves it#
On join they get a four-digit code:
It is passed as a third argument to /register, so it can never be mistaken for the password:
A wrong answer gives a new code and counts an attempt:
[Slime] Wrong captcha! You have 2 attempts remaining.
[Slime] Please solve the captcha to continue: 7390
The rest of the anti-bot stack#
The captcha is one layer. These do more work in practice:
| Setting | Effect |
|---|---|
title.kick-after-seconds: 30 |
A bot that never registers stops holding a connection slot |
security.max-accounts-per-ip: 3 |
Caps mass registration from one address |
security.command-cooldown-seconds: 2 |
Rate-limits /login and /register spam per player |
security.max-login-attempts + lockout-duration-seconds |
Turns password guessing into a slow crawl |
protection.hide-unlogged: true |
Keeps bot names out of the player list while they wait |
Against a real attack
A dedicated bot attack is a network problem, not a plugin problem — it saturates connection slots before any plugin logic runs. For that, put a proper anti-bot in front of the proxy. This plugin's job is to make sure nothing that gets through can do anything.
Messages#
All of them are editable in messages.yml:
captcha-prompt: "&ePlease solve the captcha to continue: &6{captcha}"
captcha-required: "&cInclude the captcha code: &6/register <password> <confirm> <captcha>"
captcha-success: "&aCaptcha solved!"
captcha-wrong: "&cWrong captcha! You have &6{attempts} &cattempts remaining."
captcha-kicked: "&cToo many failed captcha attempts."