EssentialsC / MySQL Database Expansion

MySQL Database Expansion

Cross-server synchronisation for economy balances, punishments, nicknames, and kit cooldowns using MySQL.

Overview

The MySQL Database Expansion connects multiple EssentialsC server instances through a shared MySQL database, keeping player data in sync across your network in near real-time. It hooks directly into EssentialsC's economy, punishment, nickname, and kit managers to push and pull changes at regular intervals.

Four synchronisation channels are provided out of the box:

Channel Sync Interval Direction
Economy Balances 1 second Bidirectional
Network Punishments 2 seconds Bidirectional
Network Nicknames Configurable (default 2s) Bidirectional
Network Kit Cooldowns On claim Push on claim, pull on check
Each server must have a unique server-id. If two servers share the same ID, sync conflicts will not be detected and data may be lost.

Installation

The MySQL expansion is a separate JAR that depends on EssentialsC being loaded first.

1

Install EssentialsC

Make sure EssentialsC is installed and running on every server in your network. The expansion requires EssentialsC's economy module to be enabled.

2

Create the MySQL database

Create a database and user on your MySQL server. The expansion will create its tables automatically on first connection - no manual schema setup is required.

3

Download the expansion JAR

Download MySQLDatabaseExpansion-x.x.x.jar from the GitHub Releases page.

4

Drop it into your plugins folder

Place the JAR into each server's /plugins directory alongside EssentialsC.

5

Configure your connection

Edit /plugins/EssentialsC-MySQLExpansion/config.yml with your MySQL credentials and a unique server-id for each server instance.

6

Restart the server

Restart all servers in your network. Check the console for the startup banner confirming each sync channel has initialised successfully.


Configuration

All settings live in /plugins/EssentialsC-MySQLExpansion/config.yml. A default file is generated on first run.

config.yml
# Unique identifier for this server instance.
# IMPORTANT: Each server in your network MUST have a different server-id.
server-id: "server-1"

mysql:
  host: "localhost"
  port: 3306
  database: "essc_sync"
  username: "root"
  password: ""
  pool-size: 10

network-kits:
  enabled: true

network-nicknames:
  enabled: true
  poll-interval-ticks: 40
Key Type Default Description
server-id String server-1 Unique identifier for this server. Must be different on every server in the network.
mysql.host String localhost MySQL server hostname or IP address.
mysql.port Integer 3306 MySQL server port.
mysql.database String essc_sync Database name for all sync tables.
mysql.username String root MySQL authentication username.
mysql.password String "" MySQL authentication password.
mysql.pool-size Integer 10 HikariCP connection pool size. For aggressive 1-second sync, use 10–20.
network-kits.enabled Boolean true Enable cross-server kit cooldown sync.
network-nicknames.enabled Boolean true Enable cross-server nickname sync.
network-nicknames.poll-interval-ticks Integer 40 Nickname poll interval in ticks (20 ticks = 1 second).
The config can be reloaded at runtime with /mysqlsync reload - no server restart needed.

Economy Sync

The economy sync engine runs a push and a pull cycle every 1 second, aggressive enough to keep balances consistent across the network even on busy servers.

Push Cycle

Every second, the push cycle iterates all players currently online on this server. For each player, it compares their current local balance to the last known balance stored in memory. If the balance has changed, it writes the new value to the economy_sync table in MySQL. Offline player balances that were modified locally are also pushed during this cycle.

Pull Cycle

Every second, the pull cycle queries the economy_sync table for rows that were updated since the last poll, excluding changes that originated from this server. For each changed player who is online on this server and not currently in a pending sync state, the local balance is updated to match the remote value.

Join & Quit Handling

When a player joins, the manager fetches their remote balance from MySQL. If a remote balance exists and differs from the local balance, the local balance is updated. If no remote record exists, the local balance is pushed to MySQL as the initial value. When a player quits, their final balance is pushed immediately.

Players with pending transactions (marked as pendingSync) are excluded from pull updates to prevent overwriting a balance that is about to change. This avoids race conditions during rapid balance updates.

Network Punishments

Bans, IP bans, and mutes are synchronised across all servers in the network. A punishment issued on any server is applied everywhere within 2 seconds.

How It Works

When a punishment is issued locally (via EssentialsC's punishment commands), the NetworkPunishmentSyncManager inserts a record into the network_punishments table. A background poll cycle runs every 2 seconds, fetching any punishments that were created or modified by other servers since the last poll. Each new remote punishment is applied locally:

  • Bans - the player is banned locally and kicked if they are currently online on this server.
  • IP bans - the IP address is banned locally and any matching online players are kicked.
  • Mutes - the player is muted locally. An active mute blocks chat via AsyncPlayerChatEvent.

Unban & Unmute

When a punishment is deactivated (unban, unmute), the deactivation is also pushed to MySQL and polled by other servers, which deactivate the punishment locally in turn.

Punishments applied by the MySQL expansion are tracked by their database ID to prevent re-application. A punishment originating from this server is never applied a second time on the same server.

On player login, the NetworkPunishmentListener checks for active bans and IP bans at AsyncPlayerPreLoginEvent (priority HIGHEST) and denies the connection with a formatted kick message if a punishment is active. Chat is intercepted at AsyncPlayerChatEvent for mute checks.


Network Nicknames

Player nicknames set on one server are propagated to all other servers in the network. The sync runs in both directions - local nickname changes are pushed immediately, and remote changes are pulled on a configurable poll interval.

Push

When a player sets or clears their nickname, the NetworkNicknameSyncManager is called by EssentialsC's nickname manager. The nickname (or null, for cleared) is upserted into the network_nicknames table along with the current timestamp and the server ID.

Pull

On the configured poll interval (default 40 ticks = 2 seconds), the manager iterates all online players and fetches the remote nickname from MySQL. If the remote nickname differs from the locally cached nickname, the local nickname is updated.

Conflict Resolution

Nickname conflicts use a last-writer-wins strategy based on the last_updated timestamp. An upsert only overwrites the existing value if the incoming timestamp is newer than the stored one, ensuring the most recent change always wins regardless of which server made it.

When a player joins any server, their nickname is also pulled immediately from the database and applied, so a nickname set on one server is visible on another as soon as the player logs in.


Network Kits

Kit cooldowns are shared across the entire network. A player who claims a kit on one server cannot claim it again on another server until the cooldown expires.

How It Works

Each kit claim records the claim timestamp in the network_kit_cooldowns table, keyed by player UUID and kit name. When EssentialsC checks whether a player can claim a kit, it queries the network table for the most recent claim timestamp. If the network cooldown is still active, the claim is denied even if the local cooldown has expired.

The cooldown value used is the maximum of the last claim time stored in the database and the local claim time, ensuring a player cannot bypass the cooldown by switching servers.

Kit cooldown sync requires network-sync: true on the individual kit in kits.yml. Only kits with this option enabled will have their cooldowns synchronised across the network.

Commands

Command Description Permission
/mysqlsync status Shows the current server ID, last poll time, and poll interval for the economy sync engine. esscmysql.admin
/mysqlsync forcepush Immediately pushes the balance of every online player to MySQL. esscmysql.admin
/mysqlsync forcepull Immediately pulls the balance of every online player from MySQL. esscmysql.admin
/mysqlsync reload Reloads the config.yml file without restarting the server. esscmysql.admin

Permissions

Permission Description Default
esscmysql.admin Access to all /mysqlsync sub-commands - status, forcepush, forcepull, and reload. Operators

Database Schema

The expansion creates and manages four tables in the configured MySQL database. All tables use the InnoDB engine and are created automatically on the first connection.

economy_sync

Stores the latest known balance for every player across the network.

SQL
CREATE TABLE `economy_sync` (
  `uuid`         VARCHAR(36)   NOT NULL,
  `username`     VARCHAR(64),
  `balance`      DECIMAL(20,2),
  `last_updated` BIGINT,
  `updated_by`   VARCHAR(64),
  PRIMARY KEY (`uuid`),
  INDEX (`last_updated`)
) ENGINE=InnoDB;

network_punishments

Stores active and historical punishments (bans, IP bans, mutes) issued by any server.

SQL
CREATE TABLE `network_punishments` (
  `id`         BIGINT       NOT NULL AUTO_INCREMENT,
  `type`       VARCHAR(10)   NOT NULL,
  `target`     VARCHAR(64)  NOT NULL,
  `target_name` VARCHAR(64),
  `reason`     VARCHAR(512),
  `punisher`   VARCHAR(64),
  `server_id`  VARCHAR(64),
  `time`       BIGINT,
  `expires`    BIGINT,
  `active`     TINYINT(1),
  PRIMARY KEY (`id`),
  INDEX (`target`),
  INDEX (`type`, `active`),
  INDEX (`time`)
) ENGINE=InnoDB;

network_nicknames

Stores the latest nickname for each player, with timestamp-based conflict resolution.

SQL
CREATE TABLE `network_nicknames` (
  `uuid`         VARCHAR(36)   NOT NULL,
  `nickname`     VARCHAR(256),
  `last_updated` BIGINT,
  `server_id`   VARCHAR(64),
  PRIMARY KEY (`uuid`),
  INDEX (`last_updated`)
) ENGINE=InnoDB;

network_kit_cooldowns

Stores the last claim timestamp for each player-kit pair across the network.

SQL
CREATE TABLE `network_kit_cooldowns` (
  `uuid`       VARCHAR(36)  NOT NULL,
  `kit_name`   VARCHAR(64)  NOT NULL,
  `claimed_at` BIGINT,
  `server_id`  VARCHAR(64),
  PRIMARY KEY (`uuid`, `kit_name`),
  INDEX (`kit_name`)
) ENGINE=InnoDB;