ELO & Ranks
How ELO calculations function, details on the 34-tier rank progression tree, and rank customization guides.
How ELO Matchmaking Works
The ELO system measures a player's relative skill level. All players start at 1000 ELO. Following an eligible matchmaking duel, ratings are adjusted as follows:
Step-by-Step ELO Example Scenario
To see how ELO is calculated, let's look at a match between two players:
- WeThink: Overall ELO of 1200 (Gold V rank).
- ErRoR25: Overall ELO of 1000 (Silver V rank).
Step 1: Expected Score Calculation
First, the plugin calculates the Expected Score (win probability) for each player. Because WeThink has a higher rating, WeThink is expected to have a higher chance of winning:
-
WeThink's Expected Score:
Expected = 1 / (1 + 10^((1000 - 1200) / 400)) Expected = 1 / (1 + 10^(-0.5)) Expected = 1 / (1 + 0.316) ≈ 0.76 (76% chance of winning) -
ErRoR25's Expected Score:
Expected = 1 / (1 + 10^((1200 - 1000) / 400)) Expected = 1 / (1 + 10^(0.5)) Expected = 1 / (1 + 3.162) ≈ 0.24 (24% chance of winning)
Step 2: Calculate Rating Adjustments (Win / Loss Scenarios)
Ratings are computed using the default parameters: base-gain: 25, base-loss: 20, min-gain: 1, max-gain: 40, min-loss: 1, and max-loss: 40 (verified against EloSettings.java).
Scenario A: WeThink Wins (The expected outcome)
-
WeThink's Elo Gain:
Gain = base-gain * 2.0 * (1.0 - Expected) Gain = 25 * 2.0 * (1.0 - 0.76) = 50 * 0.24 = 12.0 Gain = clamp(min-gain, max-gain, round(12.0)) = +12 ELOWeThink's ELO increases from 1200 to 1212.
-
ErRoR25's Elo Loss:
Loss = base-loss * 2.0 * Expected Loss = 20 * 2.0 * 0.24 = 9.6 Loss = Loss * 0.70x (Silver V loss multiplier) = 6.72 Loss = clamp(min-loss, max-loss, round(6.72)) = -7 ELOErRoR25's ELO decreases from 1000 to 993.
Scenario B: ErRoR25 Wins (The upset outcome)
-
ErRoR25's Elo Gain:
Gain = base-gain * 2.0 * (1.0 - Expected) Gain = 25 * 2.0 * (1.0 - 0.24) = 50 * 0.76 = 38.0 Gain = clamp(min-gain, max-gain, round(38.0)) = +38 ELOErRoR25's ELO increases from 1000 to 1038 (a much larger gain since this was an upset victory).
-
WeThink's Elo Loss:
Loss = base-loss * 2.0 * Expected Loss = 20 * 2.0 * 0.76 = 30.4 Loss = Loss * 0.80x (Silver III loss multiplier) = 24.32 Loss = clamp(min-loss, max-loss, round(24.32)) = -24 ELOWeThink's ELO decreases from 1200 to 1176.
ELO cannot drop below the configured min-elo (default 0). FFA matches and deaths do not affect ELO ratings.
How Min/Max Gain & Loss Work Together
The min-gain, max-gain, min-loss, and max-loss settings act as a safety range for ELO adjustments. They prevent extreme rating swings (such as losing hundreds of points in a single match) while ensuring there is always a meaningful rating change. For example, a win against a far stronger player is capped at max-gain (40 ELO) instead of awarding a massive rating boost, and a defeat by a far weaker player is capped at max-loss (40 ELO) to avoid excessive penalty. Conversely, the min-gain and min-loss floors guarantee that players always receive a token rating change (at least 1 ELO) even for completely expected matches.
Overall ELO vs. Per-Kit ELO
The plugin tracks two types of Elo metrics:
- Overall ELO: Used as the primary skill seed for matchmaking. The expected score and the resulting delta (gain/loss) are calculated using the overall Elo of the participants.
- Per-Kit ELO: Tracks a player's skill specific to each kit category. When a player wins or loses a match, their per-kit Elo is updated by the exact same delta computed from the overall Elo matchmaking formula.
Per-kit ELO is tracked for all standard official kits (including player-edited loadouts of internal kits). ELO/Win-Loss for custom-kit-system fights depends on the elo.custom-kits toggle in ranks.yml. If this option is set to false, custom kit matches are unranked; Kill/Death still update so K/D stays meaningful. Which match types count as ranked is controlled separately via elo.match-types.*.
elo.rank-system, elo.enabled, elo.custom-kits, elo.match-types.*, elo.base-gain, elo.base-loss, elo.kill-gain, elo.opponent-based, elo.min-elo, elo.min-gain, elo.max-gain, elo.min-loss, elo.max-loss, elo.starting-elo, elo.anti-farm.*, and elo.progress-bar.*.
QueueType is RANKED / UNRANKED (accepts r / u / casual aliases). Which EloMatchTypes count as ranked is controlled by elo.match-types.* (keys: duel, queue, unranked-queue, unranked-duel, party, party-vs-party, party-split, team-battle, party-ffa (ffa alias), rematch, tournament). Defaults: duel and queue true, the rest false.
Anti-farm has two namespaces: elo.anti-farm.* (ELO gates: cooldown-hours default 3, max-wins-per-opponent default 2) and death-messages.kill-commands.anti-farm.* (kill-reward gates: SAME_IP / REPEAT_WINS via canAwardAntiFarmReward).
Default Ranks Database
The 34 default ranks below ship in the bundled ranks.yml resource and are copied to plugins/ShyamDuels/ranks.yml on first start, where you can edit them:
| Rank Tier | ELO Range | Loss Multiplier |
|---|---|---|
| Bronze V | 0 - 199 | 0.40x |
| Bronze IV | 200 - 399 | 0.45x |
| Bronze III | 400 - 599 | 0.50x |
| Bronze II | 600 - 799 | 0.55x |
| Bronze I | 800 - 999 | 0.60x |
| Silver V | 1000 - 1099 | 0.70x |
| Silver IV | 1100 - 1199 | 0.75x |
| Silver III | 1200 - 1299 | 0.80x |
| Silver II | 1300 - 1399 | 0.85x |
| Silver I | 1400 - 1499 | 0.90x |
| Gold V | 1500 - 1599 | 1.00x |
| Gold IV | 1600 - 1699 | 1.05x |
| Gold III | 1700 - 1799 | 1.10x |
| Gold II | 1800 - 1899 | 1.15x |
| Gold I | 1900 - 1999 | 1.20x |
| Platinum V | 2000 - 2099 | 1.25x |
| Platinum IV | 2100 - 2199 | 1.30x |
| Platinum III | 2200 - 2299 | 1.35x |
| Platinum II | 2300 - 2399 | 1.40x |
| Platinum I | 2400 - 2499 | 1.45x |
| Diamond V | 2500 - 2599 | 1.50x |
| Diamond IV | 2600 - 2699 | 1.55x |
| Diamond III | 2700 - 2799 | 1.60x |
| Diamond II | 2800 - 2899 | 1.65x |
| Diamond I | 2900 - 2999 | 1.70x |
| Crown V | 3000 - 3099 | 1.75x |
| Crown IV | 3100 - 3199 | 1.80x |
| Crown III | 3200 - 3299 | 1.85x |
| Crown II | 3300 - 3399 | 1.90x |
| Crown I | 3400 - 3499 | 1.95x |
| Ace | 3500 - 3999 | 2.00x |
| Ace Master | 4000 - 4499 | 2.25x |
| Ace Dominator | 4500 - 4999 | 2.50x |
| Conqueror | 5000+ | 3.00x |
Customizing Ranks
Define custom ranks inside ranks.yml under the elo.ranks section. The plugin orders ranks based on their minimum ELO thresholds:
yamlelo:
enabled: true
base-gain: 25
base-loss: 20
kill-gain: 5
min-elo: 0
ranks:
iron:
name: "Iron"
colored: "&7[Iron]"
min-elo: 0
max-elo: 499
loss-multiplier: 0.5
gold:
name: "Gold"
colored: "&6[Gold]"
min-elo: 500
max-elo: 999
loss-multiplier: 1.0
diamond:
name: "Diamond"
colored: "&b[Diamond]"
min-elo: 1000
max-elo: 9999
loss-multiplier: 1.5
The colored property supports Kyori MiniMessage components.
Tracked Player Statistics
The plugin tracks various player stats, accessible via /stats or PlaceholderAPI:
| Stat Key | Description |
|---|---|
| Kills | Total player kills across duels and FFA modes. |
| Deaths | Total player deaths. |
| K/D Ratio | Kills divided by deaths, formatted to two decimal positions. |
| Wins | Total match wins inside matchmaking duels. |
| Losses | Total match losses. |
| W/L Ratio | Wins divided by losses, formatted to two decimal positions. |
| Killstreak | Current consecutive kills (resets to 0 upon death). |
| Best Killstreak | All-time highest consecutive kill record. |
| Winstreak | Current consecutive matchmaking duel wins (resets to 0 upon loss). |
| Best Winstreak | All-time highest consecutive matchmaking win record. |
| ELO | Current matchmaking rating (defaults to 1000). |
| Playtime | Total time spent online on the server, formatted as Xh Xm Xs. |