Releases: SrCodexStudio/IPDynamic
IPDynamic v2.0.3 - Folia Support, Memory Leak Prevention & Fairy-Library Audit
Folia Support
IPDynamic is now fully compatible with Folia (Paper's multi-threaded regionized server). A single JAR works across Paper, Spigot, Purpur, and Folia
without separate builds.
- Runtime detection of Folia via SchedulerHelper that automatically routes scheduler calls to the correct API
- All Bukkit.getScheduler() calls replaced with Folia-safe alternatives: getAsyncScheduler(), getGlobalRegionScheduler(), and entity.getScheduler()
- Player kicks dispatched on the entity's region thread via runOnEntity() instead of the global main thread
- Removed MCCoroutine Bukkit dispatcher dependency entirely. All coroutines now run on a plugin-owned CoroutineScope(SupervisorJob() + Dispatchers.IO)
that is properly cancelled on disable - Added folia-supported: true to plugin.yml
Memory Leak Prevention (Fairy-Library Audit)
Comprehensive audit against fairy-library best practices for long-running servers (weeks/months uptime).
- onDisable() now performs a complete 6-step shutdown: coroutine join, task cancellation, listener unregistration, rate monitor reset, cache clearing, and
database pool closure in strict dependency order - Coroutine scope uses cancelAndJoin() instead of fire-and-forget cancel() to prevent interrupted database writes and SQLite WAL corruption
- Added clearCaches() methods to BanService, WhitelistService, and CountryService for proper heap release on disable
- GeoIP cache now runs periodic cleanup every 5 minutes to evict expired entries instead of only cleaning on new insertions
- All scheduled Bukkit tasks are explicitly cancelled on disable via server.scheduler.cancelTasks()
- All event listeners are unregistered on disable via HandlerList.unregisterAll()
- Zero GlobalScope usage across the entire codebase. All async work is tied to the plugin lifecycle
Compatibility
┌────────────────────┬───────────┐
│ Platform │ Status │
├────────────────────┼───────────┤
│ Paper 1.17 - 1.21 │ Supported │
├────────────────────┼───────────┤
│ Spigot 1.17 - 1.21 │ Supported │
├────────────────────┼───────────┤
│ Purpur 1.17 - 1.21 │ Supported │
├────────────────────┼───────────┤
│ Folia │ Supported │
├────────────────────┼───────────┤
│ Floodgate / Geyser │ Supported │
├────────────────────┼───────────┤
│ Java 17+ │ Required │
└────────────────────┴───────────┘
Webhook Fixer & more
Discord Webhook System
- Added Discord webhook notifications for alt account detection with rich embeds showing full player profile, IP history with connection counts, detailed
alt list with spoilered IPs, country, and last seen timestamps - Added Discord webhook notifications for banned IP connection attempts showing player name, ban type (OP1/OP2/CIDR), pattern, reason, banned by, duration
remaining, and known alt accounts - Webhook configuration lives in its own discordwebhook.yml file with per-event toggles, separate embed colors, and full reload support via /ipdy reload
- Implemented global rate-limited queue system (configurable max-per-minute and queue size) that respects Discord's rate limits and automatically backs
off on 429 responses - Thread-safe cooldown using AtomicLong.compareAndSet to prevent duplicate webhook sends under concurrent events
- Webhook async tasks are tied to the plugin lifecycle via Bukkit scheduler, preventing orphan coroutines on reload
Emergency Performance Patch (16 fixes)
- Fixed AlertService calling Bukkit API (getOnlinePlayers, sendMessage, playSound) from async threads. All broadcasts now scheduled to the main thread via
runTask - Fixed BanService cache reload allowing banned IPs to connect during the reload window. Cache now uses atomic swap strategy with Volatile references so
readers never see a partially loaded state - Fixed the entire ConnectionListener join pipeline running on the main thread between suspend points. The full 8-step pipeline now executes on
Dispatchers.IO, freeing the main thread completely - Increased SQLite HikariCP connection pool from 3 to 10 and reduced connection timeout from 10s to 5s to prevent pool exhaustion under burst joins (50+
simultaneous connections) - Fixed GeoIP service caching failed lookups with full TTL duration, poisoning country restrictions and VPN detection. Failed results now cache for only
60 seconds - Simplified ExpiringCache by removing the ConcurrentLinkedDeque LRU tracker which had a race condition causing unbounded memory growth from duplicate
keys. Now uses TTL-only eviction which is simpler and thread-safe - Fixed WhitelistService cache reload with the same atomic swap strategy as BanService to prevent whitelisted players from being subject to ban checks
during reload - Fixed WhitelistService calling Bukkit.getOfflinePlayer() without IO dispatcher, potentially blocking the coroutine thread
- Fixed DiscordWebhookService cooldown check having a time-of-check-time-of-use race condition on the volatile field
- Replaced CopyOnWriteArrayList in RateMonitorTask with ConcurrentLinkedQueue to eliminate full array copies on every bot connection during attack waves
- Added player.isOnline guards before alt detection and VPN enforcement steps in the join pipeline to prevent processing stale Player references after
disconnect - Fixed DataRetentionTask silently swallowing all exceptions, which could cause the connections table to grow unboundedly without any log indication
- Fixed operator precedence bug in BanService.getEstimatedIPCount for CIDR bans where coerceAtMost was applied to the shift amount instead of the result
- Replaced non-atomic getOrPut with computeIfAbsent on ConcurrentHashMap in RateMonitorTask for safe concurrent insertion
- Eliminated SELECT queries inside the PlayerRepository write transaction by using INSERT OR IGNORE pattern, reducing SQLite write lock duration
- Fixed loadCaches running on main thread between IO dispatches during plugin startup
Bedrock/Floodgate Support
- Full support for Bedrock player names with prefix characters like .Steve or *Steve
- Smart UUID resolution for whitelist: checks online player first, then own database, then Mojang API (skipped for Bedrock names), then Bukkit fallback
- Enhanced tab completion across all player commands so typing Ste suggests both Steve and .Steve
- Added PlayerUtils utility with isBedrockName, stripBedrockPrefix, and matchPlayerName helpers
Database Hardening
- Added missing index on connections.username (COLLATE NOCASE) fixing full table scans on the largest table for every history query
- Replaced all LOWER(username) = LOWER(?) queries with username = ? COLLATE NOCASE across PlayerRepository and ConnectionRepository to enable proper index
usage - Added per-connection SQLite PRAGMAs via HikariCP connectionInitSql so WAL mode, foreign keys, and busy timeout apply to all pool connections, not just
the first one - Fixed race condition in PlayerRepository.recordConnection where two simultaneous first-joins for the same UUID could cause a primary key violation. Now
uses idempotent INSERT OR IGNORE plus UPDATE - Added audit log retention with configurable audit-max-days (default 90) to prevent unbounded growth of the audit_log table
- Country rules added via commands now persist to the database and survive reloads and server restarts
bStats Integration
- Added bStats metrics with 6 custom charts: active bans, whitelisted players, GeoIP cache size, VPN detection status, country restriction status, and
country restriction mode
First-Join Console Notification
- Professional formatted console notification when a brand-new player joins for the first time showing username, IP, country, and detected alt accounts
- Disabled by default, enable with first-join-console in config.yml
- Fully customizable message template in messages.yml
DiscordWebhook [Alts Support]
BEDROCK FIX
Bedrock/Floodgate Support
- Full support for Bedrock player names with prefix characters (
.Steve,*Steve). - Smart UUID resolution: online player > own database > Mojang API (skipped for Bedrock) > Bukkit fallback.
- Enhanced tab completion: typing
Stesuggests bothSteveand.Steve. - All SQL queries handle special characters safely via prepared statements.
IPDynamic v2.0.0
Changelog
All notable changes to IPDynamic will be documented in this file.
[2.0.0] - 2026-03-24
Complete Rewrite - Premium Edition
This release is a complete ground-up rewrite of IPDynamic. Every file has been redesigned for production-grade quality, performance, and extensibility. The plugin has been upgraded from 13 source files to 78 source files across a clean modular architecture.
Backward Compatible: Existing v1.x databases are automatically upgraded on first startup. No data loss, no manual migration required.
Architecture
Database Layer
- HikariCP Connection Pool - Replaced single raw JDBC connection with HikariCP (pool size 3). Eliminates race conditions and
database is lockederrors under load. - Repository Pattern - Split the monolithic 462-line
DatabaseManagerinto 7 focused repositories (PlayerRepository,ConnectionRepository,IPRecordRepository,BanRepository,WhitelistRepository,AuditRepository,CountryRuleRepository), each under 150 lines. - Transactional Writes - Multi-statement operations (player registration, IP record upsert) now use proper database transactions with commit/rollback.
- Idempotent Player Registration - Replaced SELECT-then-INSERT pattern with
INSERT OR IGNORE+UPDATEto prevent race conditions when two connections for the same new player arrive simultaneously. - Per-Connection PRAGMAs - SQLite
journal_mode=WAL,foreign_keys=ON, andbusy_timeout=5000are now applied to every pool connection viaconnectionInitSql, not just the first one. - Auto-Adaptive Schema -
SchemaManagercreates missing tables, adds missing columns, and creates missing indexes without ever dropping data. Seamless upgrade from any v1.x database.
Service Layer
- 9 dedicated services with single responsibility:
GeoIPService,BanService,WhitelistService,AltDetectionService,VPNDetectionService,CountryService,AuditService,AlertService,DataRetentionService. - lateinit initialization - All services use
lateinit varwith proper error handling. If initialization fails, the plugin disables itself cleanly instead of running in a broken state with null references. - Coroutine Scoping - All async operations use
MCCoroutine.launchwith proper dispatchers. Database I/O runs onDispatchers.IO, never blocking the main server thread.
Command System
- SubCommand Pattern - Replaced the 560-line monolithic command class with
CommandRegistry+ 15 individual command implementations, each focused and under 100 lines. - Adventure API - All command output uses clickable and hoverable text components. Player names link to
/ipdy check, IP addresses link to/ipdy ip, UUIDs are copy-to-clipboard. - Pagination - List commands (
history,banlist,whitelist list,audit) feature clickable[<< Prev]/[Next >>]page navigation.
Caching
- ExpiringCache - Generic TTL cache with LRU eviction. Configurable max size and time-to-live. Replaces the old unbounded
ConcurrentHashMapthat could cause memory leaks. - BanPatternIndex - Trie-based data structure for O(1) IP ban pattern matching. Replaces the old O(n) linear scan that iterated all bans for every player login.
New Features
VPN/Proxy Detection
- Detect VPN, proxy, and hosting provider connections on player join.
- Configurable action:
ALERT(notify staff),KICK(disconnect player), orBAN(auto-ban range). - Uses extended GeoIP API fields (
proxy,hosting,isp) from ip-api.com. - Dedicated VPN results cache with separate TTL (default 120 minutes).
- Whitelisted players can bypass VPN checks (configurable).
Country Restriction System
- Block or allow players by country with
BLACKLISTorWHITELISTmode. - Use full country names in config:
"Chile","Russia","United States"(no cryptic ISO codes). - Runtime management via
/ipdy country add <name>and/ipdy country remove <name>. - Persistent storage - Country rules added via commands are saved to the database and survive reloads/restarts.
- Config.yml countries serve as base rules; database rules are merged on startup.
Staff Alert System
- Real-time notifications to online staff with the
ipdynamic.alertspermission. - Alert types: alt detected, VPN detected, new IP, country change.
- Optional alert sound (configurable
plingnotification). - Each alert type individually toggleable in config.
First-Join Console Notification
- Professional formatted console notification when a brand-new player joins for the first time.
- Shows: username, IP address, country, and detected alt accounts.
- Disabled by default (
first-join-console: falsein config). - Fully customizable message template in
messages.yml.
Audit Log
- Immutable audit trail for all administrative actions.
- Tracks: who performed the action, what action, target, details, timestamp.
- Actions logged: ban add/remove, whitelist add/remove, country add/remove, reload, export, import, VPN kick, country kick.
- Viewable via
/ipdy audit [page]with pagination. - Configurable retention: auto-cleanup entries older than
audit-max-days(default 90 days).
Data Retention
- Automatic cleanup of old connection records to prevent unbounded database growth.
- Configurable:
connections-max-days(default 180 days) andaudit-max-days(default 90 days). - Runs on a configurable interval (default every 24 hours).
- Disabled by default - enable in
config.ymlwhen needed.
Rate Monitoring
- Detects potential bot attacks by monitoring connection rates per /24 IP range.
- Sliding window algorithm with configurable threshold and window size.
- Fires
SuspiciousActivityEventwhen threshold exceeded for webhook/alert integration.
CIDR Ban Support
- New ban type alongside OP1/OP2: CIDR notation (e.g.,
/ipdy ban cidr/24 192.168.1.0 reason). - Bitwise prefix matching for accurate CIDR range calculations.
- Estimated IP count display for all ban types.
Export/Import
- Export bans or whitelist to JSON files:
/ipdy export bans,/ipdy export whitelist. - Import from JSON files:
/ipdy import bans,/ipdy import whitelist. - Files saved in the plugin data folder.
Public API
IPDynamicAPIinterface for other plugins to integrate with.- Access via Bukkit's
ServicesManager. CompletableFuturereturns for async operations (profiles, alts, GeoIP).- Direct synchronous returns for cached data (ban checks, whitelist checks).
PlaceholderAPI Integration
- 9 placeholders under the
%ipdynamic_*%namespace:%ipdynamic_country%- Player's country%ipdynamic_country_code%- Country ISO code%ipdynamic_connections%- Total connection count%ipdynamic_alts%- Number of alt accounts%ipdynamic_is_banned%- Whether player's IP is banned%ipdynamic_is_whitelisted%- Whether player is whitelisted%ipdynamic_is_vpn%- Whether player is on VPN/proxy%ipdynamic_ban_count%- Total active bans%ipdynamic_whitelist_count%- Total whitelisted players
Event System
- 6 custom async Bukkit events for other plugins to listen to:
AltDetectedEvent- Alt account detected on joinVPNDetectedEvent- VPN/proxy detected on joinNewIPDetectedEvent- Player connected from new IPFirstJoinEvent- Brand-new player's first connectionCountryBlockedEvent- Player blocked by country restrictionSuspiciousActivityEvent- Rate anomaly detected
New Configuration
config.yml (NEW)
Full plugin configuration file with sections for:
- Database (pool size, filename)
- GeoIP (providers, cache TTL, max size, request timeout)
- VPN detection (enabled, action, providers, cache TTL, whitelist bypass)
- Country restriction (enabled, mode, country names, kick message)
- Staff alerts (enabled, per-type toggles, permission, sound)
- Data retention (enabled, max days for connections and audit, cleanup interval)
- Pagination (page size)
- Rate monitoring (enabled, threshold, window)
messages.yml (EXPANDED)
- All existing v1 messages preserved.
- New sections: VPN messages, country restriction messages, audit messages, alert formats, pagination format, first-join console notification, export/import messages.
New Commands
| Command | Permission | Description |
|---|---|---|
/ipdy country add <name> |
ipdynamic.country |
Add country to restriction list |
/ipdy country remove <name> |
ipdynamic.country |
Remove country from restriction list |
/ipdy country list |
ipdynamic.country |
View restricted countries |
/ipdy audit [page] |
ipdynamic.audit |
View audit log with pagination |
/ipdy export <bans|whitelist> |
ipdynamic.export |
Export data to JSON file |
/ipdy import <bans|whitelist> |
ipdynamic.import |
Import data from JSON file |
New Permissions
| Permission | Default | Description |
|---|---|---|
ipdynamic.country |
OP | Manage country restrictions |
ipdynamic.audit |
OP | View audit log |
ipdynamic.export |
OP | Export plugin data |
ipdynamic.import |
OP | Import plugin data |
ipdynamic.alerts |
OP | Receive staff alerts |
Performance Improvements
| Area | Before (v1) | After (v2) | Improvement |
|---|---|---|---|
| Ban checking | O(n) linear scan all bans | O(1) trie lookup | Constant time regardless of ban count |
| GeoIP cache | Unbounded, no TTL, no eviction | TTL + LRU eviction + max size | No more memory leaks |
| GeoIP rate limiting | None (429 errors under load) | Token bucket 40 req/min | No more API bans |
| DB connections | Single shared JDBC connection | H... |
IPDynamic v1.0.0
🌐 IPDynamic
Advanced IP Tracking & Alt Detection System for Minecraft Servers
Protect your server from ban evaders and manage player connections like never before.
📋 Table of Contents
- ✨ Features
- 📥 Installation
- 🎮 Commands
- 🔐 Permissions
- ⚙️ Configuration
- 🗄️ Database
- 🔧 Technical Details
- 📸 Screenshots
- ❓ FAQ
✨ Features
🔍 IP Tracking
Track every player connection with detailed information including IP address, country, and timestamps.
👥 Alt Account Detection
Automatically detect alt accounts by analyzing shared IP addresses across your player database.
🚫 IP Range Banning
Powerful IP banning system with two operation modes:
| Mode | Pattern | Example | IPs Affected |
|---|---|---|---|
| OP1 | x.x.x.* |
192.168.1.* |
~256 IPs |
| OP2 | x.x.*.* |
192.168.*.* |
~65,536 IPs |
✅ Whitelist System
Allow specific players to bypass IP bans with UUID and username verification via Mojang API.
🌍 GeoIP Integration
Automatic country detection for every connection using free GeoIP services.
💾 Persistent Storage
SQLite database with auto-adaptive schema - your data survives plugin updates seamlessly.
⚡ Async Operations
Built with Kotlin Coroutines for non-blocking database operations that won't lag your server.
🎨 Customizable Messages
Full message customization via messages.yml - translate or customize every plugin message.
🔒 Granular Permissions
Fine-grained permission system allowing you to give moderators exactly the access they need.
📥 Installation
- Download the latest
IPDynamic.jarfrom Releases - Place the JAR file in your server's
pluginsfolder - Restart your server
- Configure the plugin in
plugins/IPDynamic/
Requirements
| Requirement | Version |
|---|---|
| Minecraft Server | 1.17.x - 1.21.x |
| Server Software | Paper or Spigot |
| Java | 17 or higher |
🎮 Commands
Base command: /ipdynamic (aliases: /ipdy, /ipd)
📊 Information Commands
| Command | Description |
|---|---|
/ipdy check <player> |
View complete player profile with IP history and alt accounts |
/ipdy history <player> [limit] |
View player's connection history |
/ipdy alts <player> |
List all detected alt accounts for a player |
/ipdy ip <address> |
Find all accounts that have used a specific IP |
/ipdy stats |
View plugin statistics |
🚫 Ban Management
| Command | Description |
|---|---|
/ipdy ban <op1|op2> <ip> [reason] [duration] |
Ban an IP range |
/ipdy unban <pattern> |
Remove an IP ban |
/ipdy banlist |
View all active IP bans |
Duration Examples:
30m- 30 minutes12h- 12 hours7d- 7 days4w- 4 weeks- No duration = Permanent ban
✅ Whitelist Management
| Command | Description |
|---|---|
/ipdy whitelist add <player> [reason] |
Add player to whitelist |
/ipdy whitelist remove <player> |
Remove player from whitelist |
/ipdy whitelist list |
View all whitelisted players |
⚙️ Administration
| Command | Description |
|---|---|
/ipdy reload |
Reload plugin configuration |
/ipdy help |
Display available commands |
🔐 Permissions
Master Permission
| Permission | Description | Default |
|---|---|---|
ipdynamic.* |
Full access to all commands | OP |
Individual Permissions
| Permission | Commands | Recommended For |
|---|---|---|
ipdynamic.check |
/ipdy check |
Moderators |
ipdynamic.history |
/ipdy history |
Moderators |
ipdynamic.alts |
/ipdy alts |
Moderators |
ipdynamic.ip |
/ipdy ip |
Moderators |
ipdynamic.ban |
/ipdy ban |
Administrators |
ipdynamic.unban |
/ipdy unban |
Administrators |
ipdynamic.banlist |
/ipdy banlist |
Administrators |
ipdynamic.whitelist |
/ipdy whitelist |
Administrators |
ipdynamic.stats |
/ipdy stats |
Administrators |
ipdynamic.reload |
/ipdy reload |
Administrators |
Example Permission Setup
For Moderators (LuckPerms):
/lp group moderator permission set ipdynamic.check true
/lp group moderator permission set ipdynamic.history true
/lp group moderator permission set ipdynamic.alts true
/lp group moderator permission set ipdynamic.ip true
For Administrators:
/lp group admin permission set ipdynamic.* true
⚙️ Configuration
📁 messages.yml
Customize all plugin messages with full color code support:
# Prefix for all messages
prefix: "&b&lIPDynamic &8|"
# Ban screen shown to banned players
ban-screen:
- "&c&l⚠ IP BANNED ⚠"
- ""
- "&7Your IP: &f{ip}"
- "&7Matched Pattern: &c{pattern}"
- "&7Reason: &f{reason}"
- "&7Banned by: &f{banned_by}"
- "&7Date: &f{banned_date}"
- "&7Duration: &f{duration}"
- ""
- "&7Contact an administrator if you believe this is an error."
# Duration formats
duration:
permanent: "&cPermanent"
expired: "&aExpired"
# Command messages
messages:
no-permission: "&cYou don't have permission to use this command."
player-not-found: "&cPlayer not found in database."
invalid-ip: "&cInvalid IP address format."
# ... more messages🎨 Color Codes
| Code | Color | Code | Color |
|---|---|---|---|
&0 |
Black | &8 |
Dark Gray |
&1 |
Dark Blue | &9 |
Blue |
&2 |
Dark Green | &a |
Green |
&3 |
Dark Aqua | &b |
Aqua |
&4 |
Dark Red | &c |
Red |
&5 |
Dark Purple | &d |
Pink |
&6 |
Gold | &e |
Yellow |
&7 |
Gray | &f |
White |
&l |
Bold | &o |
Italic |
&n |
Underline | &m |
🗄️ Database
IPDynamic uses SQLite for data storage with automatic schema management.
Database Location
plugins/IPDynamic/ipdynamic.db
Tables Structure
📦 ipdynamic.db
├── 📋 players - Player profiles and first connection data
├── 📋 connections - Complete connection history log
├── 📋 ip_records - IP address records per player
├── 📋 ip_bans - Active IP range bans
└── 📋 whitelist - Whitelisted players
Auto-Adaptive Schema
The database automatically adapts to plugin updates:
- ✅ Creates missing tables on startup
- ✅ Adds new columns without data loss
- ✅ Preserves all existing data during updates
🔧 Technical Details
Built With
| Technology | Purpose |
|---|---|
| Kotlin 1.9 | Primary language |
| MCCoroutine | Async operations on Bukkit |
| Paper API | Server integration |
| Adventure API | Modern text components |
| SQLite + JDBC | Data persistence |
| Mojang API | UUID verification |
Performance Optimizations
- 🚀 In-memory caching for bans and whitelist
- 🚀 Async database operations prevent server lag
- 🚀 WAL mode for SQLite concurrent access
- 🚀 Connection pooling for efficient queries
- 🚀 Lazy loading for player profiles
Version Compatibility
| Server Version | Status |
|---|---|
| 1.17.x | ✅ Supported |
| 1.18.x | ✅ Supported |
| 1.19.x | ✅ Supported |
| 1.20.x | ✅ Supported |
| 1.21.x | ✅ Supported |
📸 Screenshots
Player Check
╭─────────────────────────────────────────╮
│ IPDynamic | Player Information │
├─────────────────────────────────────────┤
│ Username: ExamplePlayer │
│ UUID: 12345678-1234-1234-1234-1234567 │
│ First Connection: 01/01/2024 12:00:00 │
│ Total Connections: 150 │
│ │
│ First IP │
│ Address: 192.168.1.100 │
│ Country: United States │
│ │
│ IP History (3) │
│ - 192.168.1.100 (First) │
│ Country: United States │
│ Connections: 100 │
│ │
│ Alt Accounts Detected (2) │
│ - AltAccount1 │
│ Shared IP: 192.168.1.100 │
│ - AltAccount2 │
│ Shared IP: 10.0.0.50 │
╰─────────────────────────────────────────╯
Ban Screen
╭─────────────────────────────────────────╮
│ ⚠ IP BANNED ⚠ │
├─────────────────────────────────────────┤
│ │
│ Your IP: 192.168.1.100 │
│ Matched Pattern: 192.168.1.* │
│ Reason: Ban evasion │
│ Banned by: AdminName │
│ Date: 01/01/2024 15:30:00 │
│ Duration: 7d 0h │
│ │
│ Contact an administrator if you │
│ believe this is an error. ...
