Summary
In upstream main, a single disconnection event in a 1v1 ranked match immediately and permanently terminates the game, awarding the win to whichever player happened to still be connected at that exact tick. This behaviour exposes a trivially exploitable ragequit-denial attack and simultaneously punishes legitimate players for transient ISP-level network hiccups that resolve within seconds.
This master issue covers two related PRs that together deliver a complete, hardened solution:
The Problem: Upstream Instant-Win on Disconnect
In src/core/execution/WinCheckExecution.ts, the upstream 1v1 ranked check is:
// ⚠️ Upstream (unpatched) — instant match termination on any disconnect
const humans = sorted.filter(
(p) => p.type() === PlayerType.Human && !p.isDisconnected(),
);
if (humans.length === 1) {
this.mg.setWinner(humans[0], this.mg.stats().stats()); // fires immediately
}
Failure Mode 1: Ragequit Denial (Exploitable)
A player who is losing a ranked 1v1 can intentionally kill their network connection the moment they recognise the match is unwinnable:
- Force a rematch by invalidating a clean win at the last moment.
- Manipulate ranked point calculations — the losing player may suffer a smaller penalty than a clean loss.
- Grief specific opponents repeatedly, exhausting their session time without recording a proper match result.
The exploit requires zero technical sophistication — any player with physical access to their network hardware can execute it reliably.
Failure Mode 2: Innocent Players Penalised for ISP Drops
A legitimate player experiencing a transient 5-second network interruption triggers the exact same code path as a deliberate ragequit. The match ends instantly with no reconnect window.
The Solution: A 300-Tick Deterministic Grace Period State Machine
PRs #3945 and #3972 replace the single-line instant-win with a four-state timer-based state machine:
State 0: Both connected → normal win-check proceeds
State 1: One disconnected → grace timer starts (300 ticks = 30 seconds)
State 2: Reconnected → grace timer resets; back to State 0
State 3: Grace expired → winner declared (or tiebreaker applied)
The "Both Disconnected" Tiebreaker (added in #3972):
If both players disconnect, holding the game indefinitely would create zombie game rooms that leak server resources. The timer continues from the first disconnect, and the player with more tiles at that moment is awarded the win — a deterministic, un-gameable metric.
Security Properties
| Threat |
Upstream |
With Grace Period |
| Ragequit to deny win |
Works — match ends unnaturally |
Opponent still wins after 30s |
| ISP drop (< 30s) |
Match ends, player loses |
Match resumes on reconnect |
| Mutual disconnect |
Race condition |
Deterministic tile-count tiebreaker |
| Grace period stalling |
N/A |
Not possible — 30s is fixed |
Test Coverage
- ✅ No winner declared immediately on first disconnect
- ✅ Winner declared exactly after 300-tick grace expiry
- ✅ Grace timer fully resets on reconnect
- ✅ Both-disconnected tiebreaker resolves by tile count
- ✅ Bots and nations correctly excluded from 1v1 human-only logic
- ✅ Normal FFA win-check unaffected when
rankedType !== OneVOne
Affected File
src/core/execution/WinCheckExecution.ts — checkWinnerFFA() method
Linked PRs
#3945 and #3972
Summary
In upstream
main, a single disconnection event in a 1v1 ranked match immediately and permanently terminates the game, awarding the win to whichever player happened to still be connected at that exact tick. This behaviour exposes a trivially exploitable ragequit-denial attack and simultaneously punishes legitimate players for transient ISP-level network hiccups that resolve within seconds.This master issue covers two related PRs that together deliver a complete, hardened solution:
The Problem: Upstream Instant-Win on Disconnect
In
src/core/execution/WinCheckExecution.ts, the upstream 1v1 ranked check is:Failure Mode 1: Ragequit Denial (Exploitable)
A player who is losing a ranked 1v1 can intentionally kill their network connection the moment they recognise the match is unwinnable:
The exploit requires zero technical sophistication — any player with physical access to their network hardware can execute it reliably.
Failure Mode 2: Innocent Players Penalised for ISP Drops
A legitimate player experiencing a transient 5-second network interruption triggers the exact same code path as a deliberate ragequit. The match ends instantly with no reconnect window.
The Solution: A 300-Tick Deterministic Grace Period State Machine
PRs #3945 and #3972 replace the single-line instant-win with a four-state timer-based state machine:
The "Both Disconnected" Tiebreaker (added in #3972):
If both players disconnect, holding the game indefinitely would create zombie game rooms that leak server resources. The timer continues from the first disconnect, and the player with more tiles at that moment is awarded the win — a deterministic, un-gameable metric.
Security Properties
Test Coverage
rankedType !== OneVOneAffected File
src/core/execution/WinCheckExecution.ts—checkWinnerFFA()methodLinked PRs
#3945 and #3972