Nexal KOTL (King of the Ladder)
A competitive event where players race to a zone and hold it. Every game tick a player spends
inside counts; when the match ends, whoever held the zone longest wins and the arena's winner
commands run. Arenas, zones, schedules and rewards are managed from an in-game menu.
Features
- Tick-accurate time tracking - Every game tick (1/20 s) in the zone counts; a player who logs out or dies stops scoring at once
- Multiple concurrent arenas - Run several matches at the same time, each with its own zone, duration and schedule
- Flexible zone definition - Use the block you stand on, or left-click two corners
- Cron scheduling - Start matches automatically with cron expressions (
0 18 * * * for daily at 18:00)
- Custom winner rewards - Console commands per arena with
%player%, %arena%, %ticks% and %seconds%
- Tie handling - One random winner among the tied players, or all of them
- Boss bar - The current leader (or the players in the zone), how many are in the zone and the time left
- Countdown warnings - Chat warnings and final countdown titles before a match ends, all configurable
- Arena holograms - Floating text above each arena with the leader, the time left or the next scheduled match
- Statistics and leaderboards - Wins, matches played and time in the zone, per arena and overall
- Admin menu - A hub with a settings screen and an arena editor: zone, duration, schedule, rewards, rename, delete, start and stop
- PlaceholderAPI - Stats, match state, schedules and leaderboards for scoreboards and holograms
- Dual storage - JSON or MySQL/MariaDB, with export and import to move between them
- History retention - Optionally remove matches older than a number of days or beyond a maximum count; stats and leaderboards are kept
- Customizable messages - Every message, title and sound in
messages.yml
Technologies
- Paper API 1.20.1+ - Minecraft server API (Spigot compatible)
- Utilizer - Configuration, messages, menus, holograms, tasks
- ACF - Command framework
- nexal-core - JSON and SQL persistence (MariaDB/MySQL), help menus
- cron4j - Match schedules
- PlaceholderAPI - Placeholder integration (optional)
Commands
Player Commands
| Command | Description | Permission |
|---|
/kotl or /kotl help [page] | Show the help menu | None |
/kotl list | List all arenas and whether a match is running | None |
/kotl info [arena] | Show an arena, or the running matches when no arena is given | None |
/kotl stats [player] | Show wins, matches played, time in zones and win rate | None |
/kotl leaderboard \<arena> | Show the top 10 winners of an arena (aliases: lb, top) | None |
/kingoftheladder works in place of /kotl.
Admin Commands
| Command | Description | Permission |
|---|
/kotl admin | Open the admin menu (Configuration and Arenas); from the console, show the admin help | nexalkotl.admin |
/kotl admin help [page] | Show the admin commands in chat | nexalkotl.admin |
/kotl admin cancel | Cancel a zone selection in progress | nexalkotl.admin |
/kotl admin export | Export all data to JSON files in plugins/NexalKOTL/export/ | nexalkotl.admin |
/kotl admin import | Import the JSON files from plugins/NexalKOTL/export/ | nexalkotl.admin |
- Configuration - Default duration, multiple winners, countdown, the boss bar (on/off, color, style, who it shows) and the history limits. Saved to
config.yml; running matches keep going.
- Arenas - Create an arena where you stand, then edit it: set the zone, the duration (presets or typed), the cron schedule, the winner commands, rename it, delete it, and start or stop a match.
Arena names use letters, digits, - and _, up to 32 characters, and cannot be global. Typed
answers to menu prompts expire after 60 seconds; type cancel to stop early. Deleting an arena
stops its match and schedule; its match history (subject to the history limits) and the players'
stats are kept.
Permissions
| Permission | Description | Default |
|---|
nexalkotl.admin | Admin menu and admin commands | op |
Placeholders
Requires PlaceholderAPI. Arena names may contain _ and are matched ignoring case. Leaderboards are cached for 30 seconds.
Player Statistics
| Placeholder | Description |
|---|
%nexalkotl_wins% | Total wins |
%nexalkotl_wins_\<arena>% | Wins in one arena |
%nexalkotl_matches% | Matches played (matches where the player entered the zone) |
%nexalkotl_matches_\<arena>% | Matches played in one arena |
%nexalkotl_time% | Total time in zones, formatted (1m 30s) |
%nexalkotl_time_seconds% | Total time in zones, in seconds |
%nexalkotl_time_\<arena>% | Time in one arena's zone, formatted |
%nexalkotl_time_seconds_\<arena>% | Time in one arena's zone, in seconds |
%nexalkotl_winrate% | Win rate (42.5%) |
A running match counts toward matches played and time, with the time held so far.
Active Match
| Placeholder | Description |
|---|
%nexalkotl_active_\<arena>% | yes while a match is running |
%nexalkotl_remaining_\<arena>% | Time left, formatted |
%nexalkotl_remaining_seconds_\<arena>% | Time left, in seconds |
%nexalkotl_players_\<arena>% | Players in the zone |
%nexalkotl_inzone_\<arena>% | yes if the player is in the zone |
Schedule
| Placeholder | Description |
|---|
%nexalkotl_schedule_\<arena>% | Time until the next scheduled match (N/A without a schedule) |
%nexalkotl_schedule_seconds_\<arena>% | Seconds until the next scheduled match (-1 without a schedule) |
Leaderboards
| Placeholder | Description |
|---|
%nexalkotl_top_\<arena>_\<pos>_name% | Player name at a position (1 to 100) |
%nexalkotl_top_\<arena>_\<pos>_wins% | Wins at a position |
%nexalkotl_top_\<arena>_\<pos>_uuid% | Player UUID at a position |
%nexalkotl_top_global_\<pos>_name% | Name at a position, across all arenas |
%nexalkotl_top_global_\<pos>_wins% | Wins at a position, across all arenas |
%nexalkotl_top_global_\<pos>_uuid% | UUID at a position, across all arenas |
Arena Info
| Placeholder | Description |
|---|
%nexalkotl_arena_count% | Number of arenas |
Winner Command Placeholders
| Placeholder | Description |
|---|
%player% | Winner's name |
%arena% | Arena name |
%ticks% | Time at the top, in ticks |
%seconds% | Time at the top, in seconds |
Configuration
config.yml
storage:
type: json
sql:
host: localhost
port: 3306
database: minecraft
username: root
password: ""
history:
max-age-days: 0
max-entries: 0
default-duration: 300
allow-multiple-winners: false
bossbar:
enabled: true
color: YELLOW
style: SOLID
title: "<gradient:#FF6B00:#FF8C00>\<bold>KOTL\</bold>\</gradient> \<gray>|\</gray> \<white>%players%\</white> \<gray>|\</gray> \<white>%count% in zone\</white> \<gray>|\</gray> \<white>%time%s\</white>"
display-mode: leader
no-one-text: "No one"
countdown:
enabled: true
warnings: [60, 30, 10]
final: [5, 4, 3, 2, 1]
messages.yml
All messages are customizable with MiniMessage colors, sounds and titles, including the command
output, the menu prompts and the hologram lines. Messages added in an update fall back to the
defaults until you add them to your file.
Storage Migration
- Export your data:
/kotl admin export
- Change the storage type in
config.yml (e.g. type: json to type: sql)
- Restart the server
- Import your data:
/kotl admin import
Database Schema
When using SQL storage, the plugin creates the following tables:
kotl_arenas
| Column | Type | Key | Description |
|---|
id | VARCHAR(64) | PK | Arena name |
world | VARCHAR(64) | | World name |
minX, minY, minZ | INT | | Zone minimum corner |
maxX, maxY, maxZ | INT | | Zone maximum corner |
winnerCommands | TEXT | | JSON array of winner commands |
cronSchedule | VARCHAR(64) | | Cron expression for automatic matches |
defaultDurationTicks | INT | | Arena duration in ticks (null = default) |
kotl_matches
| Column | Type | Key | Description |
|---|
id | VARCHAR(36) | PK | Match UUID |
arena_id | VARCHAR(64) | FK → kotl_arenas.id | Arena |
start_time | BIGINT | | Start timestamp (ms) |
end_time | BIGINT | | End timestamp (ms) |
duration_ticks | INT | | Match duration in ticks |
winners | TEXT | | JSON array of winner UUIDs |
kotl_zone_events
| Column | Type | Key | Description |
|---|
id | VARCHAR(36) | PK | Event UUID |
match_id | VARCHAR(36) | FK → kotl_matches.id | Match |
player_id | VARCHAR(36) | | Player UUID |
event_type | VARCHAR(10) | | ENTER or EXIT |
tick | BIGINT | | Match tick of the event |
kotl_archived_stats
Totals from matches removed by the history limits, so stats do not change when history is removed.
| Column | Type | Key | Description |
|---|
id | VARCHAR(110) | PK | \<player UUID>:\<arena> |
player_id | VARCHAR(36) | | Player UUID |
arena_id | VARCHAR(64) | | Arena |
wins | INT | | Wins in removed matches |
matches_played | INT | | Removed matches played |
ticks | BIGINT | | Time in the zone in removed matches, in ticks |
Key Legend: PK = Primary Key, FK = Foreign Key
Installation
- Download the plugin JAR
- Place it in the
plugins folder (optionally with PlaceholderAPI)
- Start or restart the server
- Run
/kotl admin, open Arenas and create your first arena
- Adjust
config.yml or use Configuration in the admin menu