Skip to main content

Nexal Duels

Competitive 1v1 dueling with a ranked ladder. Arenas are saved as WorldEdit schematics and pasted into their own void world for every duel. Players challenge each other through kit and arena menus, join a casual queue, or climb the ranked ladder from Iron to Challenger across seasons. Duels can carry a Vault bet held in escrow, every match is recorded, and crash recovery gives players their inventory back if the server stops mid-duel.

Features​

  1. Schematic-Based Arenas - Arenas are saved as WorldEdit schematics and pasted into a void world (nexal_duel_arenas) per duel, then cleared afterwards
  2. In-Game Arena Editor - Hotbar tools for region selection, spawn points and a settings menu; arenas are enabled, edited and deleted from the admin menu
  3. Kit System - Admins save their inventory as a kit, with every enchantment, name and potion kept; players pick a kit before each duel
  4. Challenge System - Challenge any player with /duel \<player>, pick a kit and arena in menus, accept or decline with clickable chat buttons
  5. Queue Matchmaking - A per-kit FIFO queue that matches players automatically on a random arena
  6. Ranked Ladder - Five placement games, then LP (League Points) through Iron, Bronze, Silver, Gold, Platinum and Diamond (four divisions each), Master, and Challenger for the top Master players
  7. MMR Matchmaking - The ranked queue pairs players on the same kit with similar hidden MMR, widening the search the longer they wait
  8. Seasons - Create, start and end seasons by command or menu; each season has its own profiles and leaderboard
  9. Vault Betting - Bet money on a duel: both stakes are held in escrow, the winner takes the pot minus a configurable tax, and every cancelled or drawn duel is refunded
  10. Spectator Mode - Watch any duel in spectator mode and get your location, inventory and game mode back afterwards
  11. Stats and Match History - Wins, losses, win rate, K/D, streaks and betting totals, with paginated history menus for casual and ranked matches, and optional limits on how long and how many matches the history keeps
  12. Leaderboards - Casual and ranked leaderboard menus, and a leaderboard hologram placed with /duels admin hologram set
  13. Crash Recovery - Inventories and bets, including bets held for a challenge, are backed up to disk and restored when the player next joins; kit items cannot leave a duel through an ender chest
  14. Admin Dashboard - /duels admin shows active duels and queue sizes, and opens arena, kit, ranked, configuration and data management
  15. PlaceholderAPI Integration - 33 placeholders for stats, ranked standing, queues and leaderboards
  16. Dual Storage - JSON file or MySQL/MariaDB, with export and import to move between them
  17. Customizable Messages - Every message in messages.yml, with MiniMessage gradients, sounds and titles

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), arena cloning, help menus
  • WorldEdit or FastAsyncWorldEdit - Arena schematics (required)
  • Vault - Betting (optional)
  • PlaceholderAPI - Placeholder integration (optional)

Commands​

/duel, /d, /duels and /nexalduels are the same command: every subcommand below works with any of them.

Player Commands​

CommandDescriptionPermission
/duels help [page]Show the helpNone
/duel \<player>Challenge a player (opens the kit and arena menus)nexalduels.duel
/duel challenge \<player>Challenge a playernexalduels.duel
/duel bet \<player> \<amount>Challenge with a money betnexalduels.bet
/duel acceptAccept a challengenexalduels.duel
/duel declineDecline a challengenexalduels.duel
/duels queue \<kit>Join the queue for a kitnexalduels.queue
/duels leaveLeave the queuenexalduels.queue
/duels ranked \<kit>Join the ranked queue for a kitnexalduels.ranked
/duels ranked queue \<kit>Join the ranked queue for a kitnexalduels.ranked
/duels ranked leaveLeave the ranked queuenexalduels.ranked
/duels rankedYour ranked statsnexalduels.ranked
/duels ranked stats [player]Ranked stats menunexalduels.ranked
/duels ranked topRanked leaderboardnexalduels.ranked
/duels ranked history [player]Ranked match historynexalduels.ranked
/duels ranked seasonsAll seasons and their leaderboardsnexalduels.ranked
/duels stats [player]Stats menunexalduels.stats
/duels topLeaderboard menunexalduels.stats
/duels history [player]Match history menunexalduels.stats
/duels spectate \<player>Spectate a player's duelnexalduels.spectate
/duels spectate leaveStop spectatingnexalduels.spectate
/duels kitsList the available kitsnexalduels.duel

Stats, history and ranked commands also work for offline players who have played before.

Admin Commands​

CommandDescriptionPermission
/duels adminOpen the admin dashboard (admin help from the console)nexalduels.admin
/duels admin help [page]Admin helpnexalduels.admin
/duels admin arena create \<name>Create an arena and enter the editornexalduels.admin.arena
/duels admin arena edit \<name>Edit an arena in the editornexalduels.admin.arena
/duels admin arena delete \<name>Delete an arena and its schematicnexalduels.admin.arena
/duels admin arena listList all arenasnexalduels.admin.arena
/duels admin kit create \<name>Save your inventory as a kitnexalduels.admin.kit
/duels admin kit update \<name>Replace a kit with your inventorynexalduels.admin.kit
/duels admin kit delete \<name>Delete a kitnexalduels.admin.kit
/duels admin kit seticon \<name> \<material>Set a kit's menu iconnexalduels.admin.kit
/duels admin kit enable \<name>Enable a kitnexalduels.admin.kit
/duels admin kit disable \<name>Disable a kitnexalduels.admin.kit
/duels admin kit preview \<name>Preview a kitnexalduels.admin.kit
/duels admin kit listList all kitsnexalduels.admin.kit
/duels admin forcematch \<p1> \<p2> \<kit>Start a duel between two playersnexalduels.admin.forcematch
/duels admin forceendEnd every active duel (bets refunded)nexalduels.admin.forceend
/duels admin hologram setPlace the leaderboard hologram where you standnexalduels.admin.hologram
/duels admin hologram removeRemove the leaderboard hologramnexalduels.admin.hologram
/duels admin reloadReload config.yml and messages.ymlnexalduels.admin.reload
/duels admin exportExport all data to the export/ foldernexalduels.admin.export
/duels admin importImport data from the export/ foldernexalduels.admin.import
/duels admin rankedOpen the ranked admin menunexalduels.admin.ranked
/duels admin ranked season create \<id> \<name>Create a seasonnexalduels.admin.ranked
/duels admin ranked season start \<id>Start a season (ends the current one)nexalduels.admin.ranked
/duels admin ranked season endEnd the current seasonnexalduels.admin.ranked
/duels admin ranked setrating \<player> \<mmr>Set a player's MMRnexalduels.admin.ranked
/duels admin ranked reset \<player>Reset a player's ranked profilenexalduels.admin.ranked

The dashboard buttons check the same permissions as their commands; its Configuration screen needs nexalduels.config. Kit, arena, season and tier IDs are 1 to 32 letters, digits, _ or -.

Permissions​

PermissionDescriptionDefault
nexalduels.duelChallenge, accept and decline duelstrue
nexalduels.betBet on duelstrue
nexalduels.queueJoin the duel queuetrue
nexalduels.rankedJoin the ranked queue and view ranked statstrue
nexalduels.statsView stats, history and leaderboardstrue
nexalduels.spectateSpectate duelstrue
nexalduels.adminOpen the admin dashboard and admin helpop
nexalduels.admin.arenaManage arenasop
nexalduels.admin.kitManage kitsop
nexalduels.admin.forcematchForce matchesop
nexalduels.admin.forceendForce end all duelsop
nexalduels.admin.hologramPlace and remove the leaderboard hologramop
nexalduels.admin.reloadReload the configurationop
nexalduels.admin.exportExport dataop
nexalduels.admin.importImport dataop
nexalduels.admin.rankedManage seasons, tiers, ratings and profilesop
nexalduels.configEdit settings in the admin menuop

Placeholders​

Requires PlaceholderAPI.

PlaceholderDescription
%nexalduels_wins%Wins
%nexalduels_losses%Losses
%nexalduels_winrate%Win rate, e.g. 62.5%
%nexalduels_win_streak%Current win streak
%nexalduels_best_win_streak%Best win streak
%nexalduels_kills%Kills
%nexalduels_deaths%Deaths
%nexalduels_kdr%Kill/death ratio
%nexalduels_matches%Matches played
%nexalduels_bets_won%Total money won from bets
%nexalduels_bets_lost%Total money lost on bets
%nexalduels_bets_profit%Betting profit
%nexalduels_in_duel%true when in a duel
%nexalduels_in_queue%true when in the queue
%nexalduels_in_ranked_queue%true when in the ranked queue
%nexalduels_opponent%Current opponent's name
%nexalduels_active_duels%Duels in progress on the server
%nexalduels_ranked_queue_size%Players in the ranked queue
%nexalduels_ranked_rank%Rank this season, e.g. Gold II (Unranked in placements)
%nexalduels_ranked_tier%Tier name this season
%nexalduels_ranked_lp%LP this season
%nexalduels_ranked_mmr%Hidden MMR this season
%nexalduels_ranked_wins%Ranked wins this season
%nexalduels_ranked_losses%Ranked losses this season
%nexalduels_ranked_placement_games%Placement games played
%nexalduels_ranked_placed%true once placements are done
%nexalduels_top_wins_\<pos>_name%Name at a position of the wins leaderboard (1 to 10)
%nexalduels_top_wins_\<pos>_value%Wins at that position
%nexalduels_top_streak_\<pos>_name%Name at a position of the best streak leaderboard
%nexalduels_top_streak_\<pos>_value%Best win streak at that position
%nexalduels_top_ranked_\<pos>_name%Name at a position of this season's ranked leaderboard
%nexalduels_top_ranked_\<pos>_rank%Rank at that position
%nexalduels_top_ranked_\<pos>_lp%LP at that position

Configuration​

config.yml​

# Storage settings
storage:
type: "json" # "json" or "sql"
sql:
host: "localhost"
port: 3306
database: "minecraft"
username: "root"
password: ""

history: # match history retention; player stats are never removed
max-age-days: 0 # delete matches older than this (0 = keep forever)
max-matches: 0 # keep the newest N casual and N ranked matches (0 = unlimited)

duel:
countdown: 5 # seconds before the fight starts
ending-duration: 2 # seconds before players are sent back
request-timeout: 30 # seconds to accept a challenge
max-duration: 300 # time limit, then a draw with bets refunded (0 = none)
allow-block-break: false
allow-block-place: true # placed blocks are cleared with the arena
allow-item-drop: false
enable-hunger: false
block-commands: true # only /duel commands and the whitelist work in a duel
command-whitelist: ["msg", "r", "tell"]

queue:
match-interval: 40 # ticks between matchmaking checks

betting:
enabled: true
min-bet: 10.0
max-bet: 10000.0 # 0 = no limit
tax-percent: 5.0 # share of the pot the server keeps

spectator:
enabled: true

hologram:
enabled: true # place it with /duels admin hologram set
refresh-interval: 30 # seconds
entries: 10
# location: written by /duels admin hologram set

fireworks:
enabled: true
count: 3

ranked:
enabled: true
starting-mmr: 1000
min-mmr: 100
placement-games: 5
placement-multiplier: 2.0 # placement games move MMR this many times faster
demotion-shield-games: 3 # games protected from demotion after a promotion
base-lp: 20
min-lp-change: 10
max-lp-change: 35
lp-per-division: 100
demotion-lp: 75 # LP kept after dropping a division
k-factor: # how fast MMR moves: new, medium, veteran players
new-player-games: 10
medium-player-games: 30
k-new: 40
k-medium: 30
k-veteran: 20
tiers: # lowest to highest; edit in-game under /duels admin ranked
iron: &#123; display-name: "Iron", color: "&8", divisions: 4, min-mmr: 0, max-mmr: 799 &#125;
bronze: &#123; display-name: "Bronze", color: "&6", divisions: 4, min-mmr: 800, max-mmr: 949 &#125;
silver: &#123; display-name: "Silver", color: "&7", divisions: 4, min-mmr: 950, max-mmr: 1099 &#125;
gold: &#123; display-name: "Gold", color: "&e", divisions: 4, min-mmr: 1100, max-mmr: 1249 &#125;
platinum: &#123; display-name: "Platinum", color: "&b", divisions: 4, min-mmr: 1250, max-mmr: 1399 &#125;
diamond: &#123; display-name: "Diamond", color: "&9", divisions: 4, min-mmr: 1400, max-mmr: 1599 &#125;
master: &#123; display-name: "Master", color: "&5", divisions: 0, min-mmr: 1600, max-mmr: 99998 &#125;
challenger: &#123; display-name: "Challenger", color: "&c", divisions: 0, min-mmr: 99999, max-mmr: 99999 &#125;
matchmaking:
match-interval: 40
base-range: 100 # starting MMR search range
expansion-interval: 10 # seconds between widenings
expansion-step: 50
max-range: 500 # players further apart are never matched
queue-display:
type: "bossbar" # or "actionbar"
bossbar:
title: "<gradient:#9B59B6:#A569BD>\<bold>RANKED\</bold>\</gradient> \<gray>|\</gray> \<yellow>%time%\</yellow>"
color: "PURPLE"
style: "SOLID"
actionbar:
message: "<gradient:#9B59B6:#A569BD>\<bold>RANKED\</bold>\</gradient> \<gray>|\</gray> \<yellow>%time%\</yellow>"
top-tier: # Challenger: the top Master players
enabled: true
recalculate-interval: 300 # seconds
promote-from: "master"
promote-to: "challenger"
min-lp: 0 # 0 = use top-player-count
top-player-count: 10

Most settings can also be changed in-game from /duels admin, under Configuration and Ranked. /duels admin reload applies every file change, the matchmaking and Challenger intervals included.

Match history retention​

Every finished duel is recorded, so on a busy server the history keeps growing. Both limits are off by default. With max-age-days set, matches that ended longer ago are deleted; with max-matches set, only the newest that many casual matches and that many ranked matches are kept. Old records are removed at startup and after every finished duel, in the background, with JSON and SQL storage alike. Wins, losses, streaks, betting totals and ranked LP, rank and MMR are stored separately and never change. Both limits are also under Configuration in /duels admin.

Ranked in short​

A player's first five ranked matches each season are placement games: they move the hidden MMR (twice as fast as normal) and then place the player in the tier whose MMR range holds it. From there each win or loss moves LP, scaled by how likely the result was; 100 LP moves a player up a division, and dropping below 0 moves them down one. Master has no divisions and no LP cap, and the top ten Master players by LP are made Challenger every five minutes.

messages.yml​

All messages are customizable with MiniMessage gradients, sounds and titles, including the clickable accept and decline buttons (request-buttons) and the leaderboard hologram (leaderboard-hologram). Messages added in an update are taken from the bundled file until you add them to yours.

Database Schema​

When using SQL storage, the plugin creates the following tables. JSON storage keeps the same records in data.json.

duel_arena_templates​

ColumnTypeKeyDescription
idVARCHAR(64)PKArena ID
typeVARCHAR(32)Always SCHEMATIC
world_nameVARCHAR(255)Unused for schematic arenas
schematic_pathVARCHAR(255)Schematic file, relative to the plugin folder
target_worldVARCHAR(255)Void world the arena is pasted into
metadataTEXTJSON: origin, spawns (spawn1, spawn2, spectator), bounds
enabledBOOLEANWhether duels can use the arena
display_nameVARCHAR(64)Name shown in menus
icon_materialVARCHAR(64)Menu icon

duel_kits​

ColumnTypeKeyDescription
idVARCHAR(64)PKKit ID
display_nameVARCHAR(64)Name shown in menus
icon_materialVARCHAR(64)Menu icon
contents_base64TEXTInventory items, Bukkit-serialized and Base64-encoded
armor_base64TEXTArmor items
offhand_base64TEXTOffhand item
enabledBOOLEANWhether players can pick the kit
slot_orderINTPosition in the kit menu

duel_players​

ColumnTypeKeyDescription
idVARCHAR(36)PKPlayer UUID
player_nameVARCHAR(32)Last known name
winsINTWins
lossesINTLosses
win_streakINTCurrent win streak
best_win_streakINTBest win streak
killsINTKills
deathsINTDeaths
total_bets_wonDOUBLEMoney won from bets
total_bets_lostDOUBLEMoney lost on bets
last_playedBIGINTLast duel timestamp
created_atBIGINTFirst duel timestamp

duel_matches​

ColumnTypeKeyDescription
idVARCHAR(36)PKMatch ID
player1_idVARCHAR(36)First player (the challenger) UUID
player2_idVARCHAR(36)Second player UUID
winner_idVARCHAR(36)Winner UUID (empty for a draw)
arena_idVARCHAR(64)FK → duel_arena_templates.idArena
kit_idVARCHAR(64)FK → duel_kits.idKit
bet_amountDOUBLEBet per player
duration_secondsINTFight length
start_timeBIGINTStart timestamp
end_timeBIGINTEnd timestamp

duel_ranked_seasons​

ColumnTypeKeyDescription
idVARCHAR(64)PKSeason ID
display_nameVARCHAR(64)Season name
start_timeBIGINTStart timestamp
end_timeBIGINTEnd timestamp (0 while running)
activeBOOLEANWhether this is the current season

duel_ranked_profiles​

ColumnTypeKeyDescription
idVARCHAR(100)PK\<player UUID>:\<season ID>
player_idVARCHAR(36)Player UUID
player_nameVARCHAR(32)Last known name
season_idVARCHAR(64)FK → duel_ranked_seasons.idSeason
mmrINTHidden matchmaking rating
lpINTLeague Points
tierVARCHAR(32)Tier ID
divisionINTDivision (1 = I, highest)
demotion_shieldINTProtected games left
placement_gamesINTPlacement games played
placement_winsINTPlacement games won
placedBOOLEANPlacements done
peak_tierVARCHAR(32)Highest tier reached
peak_divisionINTHighest division reached
peak_lpINTLP at the peak
ranked_winsINTRanked wins
ranked_lossesINTRanked losses
ranked_win_streakINTCurrent ranked win streak
ranked_best_win_streakINTBest ranked win streak
ranked_killsINTRanked kills
ranked_deathsINTRanked deaths
last_lp_changeINTLP change of the last match
last_playedBIGINTLast ranked match timestamp

duel_ranked_matches​

ColumnTypeKeyDescription
idVARCHAR(36)PKMatch ID
season_idVARCHAR(64)FK → duel_ranked_seasons.idSeason
player1_idVARCHAR(36)Winner UUID
player2_idVARCHAR(36)Loser UUID
winner_idVARCHAR(36)Winner UUID
arena_idVARCHAR(64)Arena
kit_idVARCHAR(64)Kit
player1_lp_before / player1_lp_after / player1_lp_changeINTWinner's LP
player1_tier / player1_divisionVARCHAR(32) / INTWinner's rank after the match
player2_lp_before / player2_lp_after / player2_lp_changeINTLoser's LP
player2_tier / player2_divisionVARCHAR(32) / INTLoser's rank after the match
duration_secondsINTFight length
start_timeBIGINTStart timestamp
end_timeBIGINTEnd timestamp

Key Legend: PK = Primary Key, FK = Foreign Key, INDEX = Indexed column

Installation​

  1. Install WorldEdit (or FastAsyncWorldEdit); add Vault and an economy plugin for betting, and PlaceholderAPI for placeholders
  2. Place the plugin JAR in /plugins
  3. Start or restart the server
  4. Create an arena with /duels admin arena create \<name>: select the region, set both spawns, save
  5. Fill your inventory with the kit items and run /duels admin kit create \<name>
  6. For ranked play, create and start a season: /duels admin ranked season create season1 Season_1, then /duels admin ranked season start season1
  7. Adjust config.yml, or the settings under /duels admin, and apply file changes with /duels admin reload