ELO and ranks
Nine ranks, per-kit and global ladders, placements, the demotion shield, boosts and the anti-boost checks.
Every ranked match moves two ratings for each player: their rating on that kit and their global
rating. Both are ordinary Elo, both live in practice_player_season_stats, and the global one is
stored under the kit id global.
The nine ranks
ranks.yml ships with nine, each starting at an MMR and carrying its own K-factor:
| Rank | Starts at | K | Divisions |
|---|---|---|---|
| Ember | 0 | 50 | IV, III, II, I |
| Copper | 400 | 50 | IV, III, II, I |
| Titanium | 800 | 35 | IV, III, II, I |
| Solar | 1200 | 35 | IV, III, II, I |
| Nova | 1600 | 25 | IV, III, II, I |
| Prism | 2000 | 25 | IV, III, II, I |
| Apex | 2400 | 20 | — |
| Elite | 2700 | 20 | — |
| Legend | 3000 | 20 | — |
Each division is 100 LP wide, so Copper spans 400–799 as Copper IV, III, II, I. The top three ranks have no divisions: their LP just counts up from the rank's floor.
New players start at elo.starting-mmr — 400, which is Copper IV.
The file is read as plain YAML, not as a fixed schema. Rename the ranks, change how many there are,
change the division count and LP width per rank, give each its own icon and gradient. A rank with no
divisions key simply has none. k-factor defaults to 30 and lp-per-division to 100 for the short
form.
The formula
Standard Elo with a few practice-server adjustments:
expected = 1 / (1 + 10^((opponentMmr - playerMmr) / 400))
delta = round(K × (result − expected))K is the K-factor of the rank the player is currently in — high at the bottom so new players find
their level quickly, low at the top so a Legend's rating is stable.
| Setting | Default | What it does |
|---|---|---|
elo.lp-max-per-game | 25 | Hard cap on the movement in either direction |
elo.placement-game-count | 10 | Ranked games before a player is out of placements |
elo.placement-k-multiplier | 2.0 | K is multiplied by this during placements |
elo.flat-elo-enabled | false | Replaces the whole formula with two fixed numbers |
elo.flat-elo-win | 25 | The win, when flat is on |
elo.flat-elo-loss | 25 | The loss, when flat is on |
Placements
The first placement-game-count ranked games on a kit — and separately on the global ladder — use a
doubled K and skip both the cap and the asymmetric reduction. A player's placement games move
their rating hard and fast, which is the point.
The asymmetric reduction
When a higher-rated player loses and the gap is more than
elo.asymmetric-threshold-mmr (200), the loss is reduced by
elo.asymmetric-reduction-per-100-mmr (5) for every 100 MMR of gap beyond the threshold, and the
result is bounded so it can never cost more than elo.asymmetric-min-loss (5).
In practice: with the defaults, a player more than 200 MMR above their opponent loses at most 5 LP, and by a gap of about 700 the loss reaches zero. It exists so that a high-rated player who keeps getting matched down is not punished for the matchmaker's choices.
The winner's gain is unaffected — only the loss is reduced.
The demotion shield
A loss can never drop a player below the floor of the division they are currently in. Losing at Copper II with 40 LP takes you to Copper II 0 LP, not into Copper III.
The shield only applies while you are above the floor. Once you are sitting exactly on it, the next loss demotes normally. It does not apply during placements.
Ratings also never go below 0.
Boosts
An admin can multiply a player's LP gains for a while.
| Command | What it does |
|---|---|
/epc boost add <player> <multiplier> <duration> | A personal boost |
/epc boost remove <player> | Cancel it |
/epc boost info <player> | Multiplier and time left |
/epc boost global add <multiplier> <duration> | Everybody at once |
/epc boost global remove | Cancel it |
/epc boost global info | Multiplier and time left |
Durations are written as 1d, 12h, 30m, 90s, or combined: 1d12h. The multiplier must be
greater than 1.
A player's effective multiplier is the higher of their personal boost and the global one — they do not stack. It only ever applies to a winner's positive delta; a loss is never boosted, and the bonus LP is reported separately in chat.
Personal boosts survive a restart. Both kinds are cancelled by a season rotation.
Anti-boosting
Two checks, both consulted before the queue pairs anybody:
| Setting | Default | What it does |
|---|---|---|
elo-security.enabled | true | The master switch for both |
elo-security.same-ip-protection | true | Two players from the same address are never paired |
elo-security.opponent-cooldown | true | The recent-opponent rule |
elo-security.opponent-cooldown-seconds | 300 | How long two players must wait before meeting again |
Both checks run at matchmaking time only. /duel bypasses them entirely, so two accounts can still
farm each other on a ranked kit through direct challenges. The duel does start the recent-opponent
cooldown for the queue, but nothing blocks the next duel.
Two clients on one machine will never be paired. Turn same-ip-protection off while you are setting
the server up.
Adjusting ratings by hand
/epc stats set <player> <kit|global> elo <value>
/epc stats add <player> <kit|global> elo <amount>See stats for the full field list. /epc → Data
Management also offers Reset kit ELO and Reset all ELO, which put every row of the current
season back to elo.starting-mmr. Previous seasons and the lifetime tier are untouched.
Something missing on this page? Tell us on Discord