Skip to content

4. Database Schema (MySQL) ​

All tables live in the same MySQL database. Schemas are created and migrated automatically on startup by SchemaMigrator (in common/.../Database/SchemaMigrator.java). The migrator:

  • Runs CREATE TABLE IF NOT EXISTS for every table.
  • Compares INFORMATION_SCHEMA.COLUMNS definitions to the in-code spec; emits ADD / MODIFY / DROP as needed.
  • Normalizes MySQL type strings so TINYINT(1)↔BOOLEAN, INT(11)↔INT, BIGINT(20)↔BIGINT, SMALLINT(5) UNSIGNED↔SMALLINT UNSIGNED, LONGTEXT DEFAULT '[]'↔JSON are treated as equal.
  • For ENUM modifications, only widens if no existing data contains values outside the new enum set (otherwise skips to preserve data).
  • All updates run via Database.runUpdate(...), which is async and retries on MySQL deadlock (SQLState == "40001") with a 1-second backoff.

4.1 appelSettings — global key/value ​

ColumnTypeNotes
keyVARCHAR(255) NOT NULLPK
valueTEXT NOT NULL

DAO: AppelSettingsDB.get(key), update(key, value).

4.2 playerData — per-player global state ​

ColumnTypeNotes
UuidVARCHAR(36) NOT NULLPK
NameVARCHAR(16) NOT NULLlast known username
IPVARCHAR(45)nullable
LastSeenTIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
IgnoredPlayersJSON NOT NULL DEFAULT '[]'List<UUID>
ReportsJSON NOT NULL DEFAULT '[]'List<Report>
NotesJSON NOT NULL DEFAULT '[]'List<String>
FriendsJSON NOT NULL DEFAULT '[]'List<UUID>
FriendNotificationsToggledBOOLEAN NOT NULL DEFAULT TRUE
FriendRequestsToggledBOOLEAN NOT NULL DEFAULT TRUE
LastMessagedBLOBJava-serialized MCPlayer
MessagesToggledBOOLEAN NOT NULL DEFAULT TRUE
StaffToggledBOOLEAN NOT NULL DEFAULT TRUE
StaffChatToggledBOOLEAN NOT NULL DEFAULT FALSE
StaffModeToggledBOOLEAN NOT NULL DEFAULT FALSE
StaffSpyToggledBOOLEAN NOT NULL DEFAULT FALSE

DAO: PlayerDataDB.getPlayerData(uuid), getPlayerDataByName(name), updatePlayerData(atPlayerData). JSON columns deserialized via Database/TypeTokens.java (STRING_LIST, UUID_LIST, REPORT_LIST).

4.3 playerServerData — per-player, per-server state ​

ColumnTypeNotes
UuidVARCHAR(36) NOT NULLPK (composite)
NameVARCHAR(16) NOT NULL
ServerUUIDVARCHAR(36) NOT NULLPK (composite)
LastSeenTIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
PaymentsToggledBOOLEAN NOT NULL DEFAULT TRUE
BalanceDECIMAL(10, 2) NOT NULL
FrozenBOOLEAN NOT NULL DEFAULT FALSE
StaffModeToggledBOOLEAN NOT NULL DEFAULT FALSE
TeleportAutoAcceptBOOLEAN NOT NULL DEFAULT FALSE
TeleportToggleBOOLEAN NOT NULL DEFAULT FALSE

DAO: PlayerServerDataDB.getTopBalances(serverUUID, limit) (orders by Balance DESC LIMIT n), getPlayerServerData(uuid, serverUUID), updatePlayerServerData(atp).

4.4 punishments — permanent punishments ​

ENUM values: 'BAN','MUTE','KICK','IP_BAN','IP_MUTE','IP_KICK'.

ColumnTypeNotes
IdVARCHAR(36) NOT NULLPK
TypeENUM(...)
PunisherNameVARCHAR(16) NOT NULL
PunisherUuidVARCHAR(36) NOT NULL
PunishedNameVARCHAR(16) NOT NULL
PunishedUuidVARCHAR(36) NOT NULL
PunishedIPVARCHAR(45)
ReasonTEXT NOT NULL
ActiveBOOLEAN
CreatedAtTIMESTAMP NOT NULL

4.5 tempPunishments — temporary punishments ​

ENUM values: 'TEMP_BAN','TEMP_MUTE','TEMP_IP_BAN','TEMP_IP_MUTE'. Same columns as punishments plus:

  • ExpiresAt TIMESTAMP NOT NULL
  • Duration BIGINT NOT NULL

4.6 undonePunishments — undo log ​

ENUM values: 'UNBAN','UNMUTE','IP_UNBAN','IP_UNMUTE'. Same columns as punishments.

4.7 staffInventory ​

ColumnTypeNotes
ServerVARCHAR(255) NOT NULLPK
UuidVARCHAR(36) NOT NULLPK
InventoryLONGTEXT NOT NULLBase64-encoded ItemStack[]

DAO: StaffInventoryDB.get(server, uuid), set(server, uuid, base64), delete(server, uuid).

4.8 servers ​

ColumnTypeNotes
NameVARCHAR(255) NOT NULLPK
IPVARCHAR(45) NOT NULL
PortSMALLINT UNSIGNED NOT NULL

DAO: ServerDataDB.getServerData(port), updateServerData(name, ip, port).

4.9 friendRequests ​

ColumnTypeNotes
senderUuidVARCHAR(36) NOT NULLPK
receiverUuidVARCHAR(36) NOT NULLPK
createdAtTIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP

DAO: FriendRequestDB.addFriendRequest (INSERT IGNORE), removeFriendRequest, getPendingRequests(receiver), getSentRequests(sender), hasFriendRequest, removeAllRequests, getAllFriendRequests.

4.10 homes ​

ColumnTypeNotes
ServerUUIDVARCHAR(36) NOT NULLPK
PlayerUUIDVARCHAR(36) NOT NULLPK
NameVARCHAR(64) NOT NULLPK
WorldVARCHAR(64) NOT NULL
XDOUBLE NOT NULL
YDOUBLE NOT NULL
ZDOUBLE NOT NULL
YawFLOAT NOT NULL
PitchFLOAT NOT NULL

DAO: HomeDB.getHomes, getHome, setHome (REPLACE), deleteHome, renameHome.

4.11 HikariCP Pool Configuration ​

Database.setup builds a HikariDataSource with these tunings:

  • prepStmtCache, useServerPrepStmts, useLocalSessionState, rewriteBatchedStatements
  • cacheResultSetMetadata, cacheServerConfiguration, elideSetAutoCommits, cacheCallableStmts
  • serverTimezone=UTC
  • maxLifetime=1800000 ms (30 min), keepaliveTime=300000 ms (5 min), connectionTimeout=5000 ms (5 s), idleTimeout=600000 ms (10 min)
  • maxPoolSize / minIdle from DatabaseConfig
  • JDBC URL: jdbc:mysql://{host}:{port}/{name} with optional ?requireSSL=true&useSSL=true if DatabaseConfig.ssl is true.

4.12 DAOs — Method Index ​

DAOKey methods
AppelSettingsDBget(key), update(key, value)
PlayerDataDBgetPlayerData(uuid), getPlayerDataByName(name), updatePlayerData(atPlayerData)
PlayerServerDataDBgetTopBalances(serverUuid, limit), getPlayerServerData(uuid, serverUuid), updatePlayerServerData(atp)
PunishmentDBsearchPunishments(filters, types, class), getActivePermPunishments(mcPlayer), getActiveIPTempPunishments(mcPlayer, ip), deactivatePunishments(...), deactivateExpiredPunishments(), generatePunishmentID()
UndonePunishmentDBgetUndonePunishment(id), searchUndonePunishments(filters), updateUndonePunishment(p)
ServerDataDBgetServerData(port), updateServerData(name, ip, port)
StaffInventoryDBget(server, uuid), set(server, uuid, base64), delete(server, uuid)
FriendRequestDBaddFriendRequest, removeFriendRequest, getPendingRequests, getSentRequests, hasFriendRequest, removeAllRequests, getAllFriendRequests
HomeDBgetHomes, getHome, setHome, deleteHome, renameHome

4.13 Punishment ID Generator ​

PunishmentDB.generatePunishmentID() returns a 6-digit zero-padded random string. Up to MAX_PUNISHMENT_ID_ATTEMPTS=10 retries on collision across all three punishment tables. If still colliding, falls back to the first 6 characters of UUID.randomUUID().toString(). Range: PUNISHMENT_ID_RANGE=1_000_000.

4.14 Query Filter System ​

common/.../Types/Query/ provides:

  • QueryKey enum mapping to columns: PUNISHED_UUID("PunishedUuid"), PUNISHED_IP("PunishedIP"), ACTIVE("Active").
  • QueryFilter (interface) with boolean isUseOr().
  • SingleQueryFilter (@Data) — QueryKey key, Object value, boolean useOr.
  • MultipleQueryFilter (@Data) — boolean useOr, List<SingleQueryFilter> filters. Varargs constructor.

PunishmentDB.searchPunishments(filters, types, class) unions punishments + tempPunishments + undonePunishments queries, casting each result to PermPunishment / TempPunishment / UndonePunishment based on the requested class. UUIDs are converted to strings before being bound.

4.15 ENUM Type Lists (from PunishmentType) ​

  • permanentTypes() → 'BAN','MUTE','KICK','IP_BAN','IP_MUTE','IP_KICK'
  • tempTypes() → 'TEMP_BAN','TEMP_MUTE','TEMP_IP_BAN','TEMP_IP_MUTE'
  • Undone enum (hardcoded in createUndonePunishmentsTable): 'UNBAN','UNMUTE','IP_UNBAN','IP_UNMUTE'

Built with VitePress.