Luckperms & PlaceholderAPI & NeoAuthme support has been added!
## Changelog
## [v1.2.1] - 2026-03-24
**Version: 1.2.1**
**Release Type: Major Feature Update**
### New Features
#### Account Per IP Limiting
- **Account Per IP Limit** — Limit simultaneous connections from the same IP address
- Configurable maximum accounts per IP (default: 3)
- Two enforcement modes: `BLOCK` or `KICK_OLDEST`
- `BLOCK` mode: Prevents new connections when limit reached
- `KICK_OLDEST` mode: Automatically kicks oldest session to allow new connection
- Whitelisted players automatically bypass limit
- Disabled by default for backward compatibility
- Thread-safe session tracking with automatic cleanup on disconnect
- Local/private IPs bypass tracking (127.0.0.1, 192.168.x.x, etc.)
#### PlaceholderAPI Integration
- **10 built-in placeholders** available out of the box — no expansion download required
- `%protectcord_warnings%` — total warning count
- `%protectcord_warnings_active%` — active warning count
- `%protectcord_is_banned%` — whether the player is currently banned (`true`/`false`)
- `%protectcord_ban_reason%` — active ban reason
- `%protectcord_ban_expiry%` — ban expiry (`Permanent` for permanent bans)
- `%protectcord_is_muted%` — whether the player is currently muted (`true`/`false`)
- `%protectcord_mute_reason%` — active mute reason
- `%protectcord_mute_expiry%` — mute expiry (`Permanent` for permanent mutes)
- `%protectcord_ip_count%` — number of unique IPs tracked for the player
- `%protectcord_trusted_ip_count%` — number of trusted IPs
- All placeholders support **offline players**
#### LuckPerms Integration
- **3 additional placeholders** when LuckPerms is present:
- `%protectcord_rank%` — player's primary LuckPerms group
- `%protectcord_prefix%` — player's LuckPerms prefix
- `%protectcord_suffix%` — player's LuckPerms suffix
- Soft dependency — returns empty string gracefully if LuckPerms is not installed
- LuckPerms hook registered automatically on startup when detected
#### Staff IP Protection
- New system to protect staff accounts from unauthorized IP access
- Monitors players with `protectcord.ip.protected` permission for IP changes
- Three configurable actions: `kick-and-alert`, `alert-only`, `auto-ban`
- Auto-trust first IP on login (`staff-ip-protection.auto-trust-first-ip`)
- `/pc trustip add|remove|list|clear <player> [ip]` command for managing trusted IPs
- New permission: `protectcord.trustip` for trustip command access
- New permission: `protectcord.ip.protected` to mark a player as monitored
- New permission: `protectcord.ip.full` to view unmasked IPs in alerts
#### NeoAuthMe Integration
- Staff IP Protection hooks into NeoAuthMe's login event when AuthMe is installed
- IP check is deferred until after NeoAuthMe authentication completes, preventing false positives
- Uses `RegisteredListener` + reflection to attach to `fr.xephi.authme.events.LoginEvent`'s `HandlerList` at runtime — no compile-time AuthMe dependency
- Enable with `staff-ip-protection.neoauthme-integration: true` in config
#### IP Masking
- New `ip-masking` config section to mask player IPs in alerts and commands
- Modes: `last-octet`, `last-two-octets`, `full-mask`
- Players with `protectcord.ip.full` permission always see full IPs
#### Storage & Database Improvements
- New `database` config section supporting `YAML` and `MYSQL` storage backends
- MySQL support with HikariCP connection pooling
- `/pc migrate <from> <to>` — migrate data between storage backends
- `/pc purge <days>` — purge old warning records
- `/pc alts <player>` — view alternate accounts sharing the same IP
#### Moderation Commands
- `/pc clear <player>` — clear all warnings for a player
- `/pc remove <player> <id>` — remove a specific warning by ID
- `/pc alts <player>` — list known alts sharing an IP
- Offline player support added to `/pc warn`, `/pc ban`, `/pc mute`, `/pc unmute`, `/pc warnings`
- Anonymous `/report` support (player can choose to report anonymously)
- Predefined reasons tab-complete for `/report`
### Bug Fixes
- **Fixed plugin crash on Paper 1.21+** — `NeoAuthMeHook` previously caused `IllegalPluginAccessException` by registering `@EventHandler` on the base `Event` class. Rewritten to use `RegisteredListener` with reflection
- **Fixed plugin disabling on fresh install** — Plugin no longer disables itself when the API key is unconfigured. A warning is logged and IP checking is skipped until the key is set
### Configuration Changes
New sections added to `config.yml` since v1.1.0:
```yaml
# Account Per IP Limit
account-per-ip-limit:
enabled: false
max-accounts: 3
enforcement: "BLOCK" # "BLOCK" or "KICK_OLDEST"
messages:
account-limit-reached: |
&c&lConnection Limit Reached
&7Maximum accounts per IP: &c{max}
&7Current connections from your IP: &c{current}
account-limit-kicked: |
&c&lYou have been disconnected
&7A new account connected from your IP address.
# Storage backend
database:
type: "YAML" # "YAML" or "MYSQL"
mysql:
host: "localhost"
port: 3306
database: "protectcord"
username: "root"
password: ""
table-prefix: "pc_"
pool:
max-pool-size: 10
min-idle: 2
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
# IP masking
ip-masking:
enabled: false
mask-format: "last-octet" # "last-octet", "last-two-octets", "full-mask"
# Staff IP Protection
staff-ip-protection:
enabled: false
action: "kick-and-alert" # "kick-and-alert", "alert-only", "auto-ban"
protected-permission: "protectcord.ip.protected"
auto-trust-first-ip: true
kick-message: "&cYour IP address has changed. Please contact a staff member."
alert-message: "&c[ProtectCord] &f{player} logged in from an untrusted IP: {ip}"
neoauthme-integration: false
```
### Compatibility
- Fully backward compatible with v1.1.0 YAML data
- softdepend updated: `[NeoAuthMe, PlaceholderAPI, LuckPerms]`
- PlaceholderAPI required for PAPI placeholders (optional soft dependency)
- LuckPerms required for rank/prefix/suffix placeholders (optional soft dependency)
- Tested on Paper 1.13 - 1.21+
Developer information
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle.
maven { name = "Modrinth"; url = "https://api.modrinth.com/maven" }