
### Added
- **Velocity proxy support** - New `ezeconomy-velocity` module provides a Velocity proxy plugin for cross-server payment notifications and global player list broadcasting. Deploy `ezeconomy-velocity.jar` on your Velocity proxy alongside the main plugin on backend servers.
- **Cross-server messaging layer** - New `MessagingService`, `MessagingTransport`, and `MessageType` abstractions in core. Supports three transports: Velocity plugin messaging, BungeeCord plugin messaging, and Redis pub/sub.
- **Redis pub/sub messaging** - New `RedisMessagingTransport` in the `ezeconomy-redis` module enables proxy-independent cross-server messaging via Redis pub/sub. Ideal for multi-proxy setups or networks already running Redis.
- **Pending notifications** - Payment notifications for offline players are now stored in the database and delivered on next join. Implemented in all four storage backends (YML, MySQL, SQLite, MongoDB).
- **Player info persistence** - `StorageProvider.persistPlayerInfo()` stores UUID/name/display name on join, enabling `resolvePlayerByName()` for cross-server name lookups.
- **Configurable lock timing** - New `locking` section in `config.yml` with `ttl-ms`, `retry-ms`, and `max-attempts` settings, replacing hardcoded values.
- **VaultEconomyImpl distributed locking** - Withdraw and bank withdraw operations now acquire distributed locks (with local fallback) to prevent race conditions in multi-server environments.
- **Cross-server documentation** - New `docs/feature/cross-server.md` and `docs/integration/velocity.md` covering all three messaging transports, configuration, and deployment.
- **Velocity CI workflow** - GitHub Actions workflow for the `ezeconomy-velocity` module.
- **MessagingComponent** - Bootstrap component that initialises cross-server messaging during plugin startup.
- New message keys: `eco_give`, `baltop_footer`, `payment_cancelled`, `recipient_offline_queued`.
- `/pay` alias: `ezpay`.
- `MySQLStorageProvider.persistPlayerInfo()` implementation for explicit player data upserts.
### Changed
- **BungeeCord proxy overhaul** - `EzBungeeProxyPlugin` now implements `Listener`, registers both `ezeconomy:locks` and `ezeconomy:notify` channels, handles payment notification forwarding, sends `RECIPIENT_OFFLINE` responses, and broadcasts the global player list every 3 seconds.
- **BungeeCord proxy plugin.yml** - Fixed `main` class reference, added `description`, enabled resource filtering for `${project.version}`.
- All sub-module POM versions now inherit from the parent (removed explicit `<version>` tags).
- Updated README with cross-server messaging, Velocity integration, and distributed locking documentation links.
- Updated `docs/feature/proxy-network.md` and `docs/integration/bungeecord.md` to reflect Velocity support and cross-server messaging.
- `StorageProvider.transfer()` now uses configurable lock timing via `EzEconomyPlugin.getLockTtlMs/RetryMs/MaxAttempts()`.
### Fixed
- **BungeeCord proxy `plugin.yml`** - Main class was pointing to the wrong class (`EzBungeeProxy` instead of `EzBungeeProxyPlugin`).
- **BungeeCord channel mismatches** - Unified lock and notification channels across server and proxy modules.
- **PaymentExecutor cross-server notifications** - Offline recipients now receive payment notifications via cross-server messaging instead of silently dropping the message.
- **Cross-server `/pay` failing silently** - Payments to players on other backend servers failed because the recipient was looked up only in Bukkit's local player cache. `PayCommand` now checks `MessagingService.isNetworkPlayer()` and `StorageProvider.resolvePlayerByName()` when local lookups fail.
- **Incorrect UUID for cross-server recipients** - `PaymentExecutor` was using `Bukkit.getOfflinePlayer(name)` which generates an offline-mode UUID for players who have never joined the local server. It now resolves the correct UUID from the messaging service or shared database.
- **`MySQLStorageProvider.resolvePlayerByName()` not implemented** - The default no-op from the `StorageProvider` interface was being used. Now queries the `players` table by name to return the correct UUID.
---
Optional dependencies
PlaceholderAPI
Any compatible version
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" }