threesome fixed
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
# Unity dedicated server — match lifecycle (escrow + always finish)
|
||||
|
||||
Contract for the Kick Kings dedicated server build against the matchmaker API.
|
||||
Header on all `/internal/*` calls: `X-Dedicated-Server-Secret: <secret from settings>`.
|
||||
|
||||
## Goals
|
||||
|
||||
1. Collect entry fees into a **per-match escrow** only after **both** players are connected.
|
||||
2. Pay the winner (and house fee) **only from that escrow**.
|
||||
3. **Never leave a match unfinished** — always report a winner, including disconnect forfeits.
|
||||
|
||||
## Sequence
|
||||
|
||||
```
|
||||
1. Match row exists (matchmaking or rematch) with match_id, user_red, user_blue, entry_fee
|
||||
2. Red connects → PATCH /internal/match/:matchId { "red_joined_at": "<ISO-8601 UTC>" }
|
||||
3. Blue connects → PATCH /internal/match/:matchId { "blue_joined_at": "<ISO-8601 UTC>" }
|
||||
→ When both join timestamps are set, the API collects entry fees into escrow
|
||||
4. Start the match only after collect succeeds (see responses below)
|
||||
5. Match ends (score / disconnect / crash path) → PATCH /internal/match/:matchId/winner
|
||||
{ "winner": "red" | "blue" }
|
||||
```
|
||||
|
||||
## Join PATCH
|
||||
|
||||
`PATCH /internal/match/:matchId`
|
||||
|
||||
Example bodies:
|
||||
|
||||
```json
|
||||
{ "red_joined_at": "2026-08-12T18:00:00.000Z" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "blue_joined_at": "2026-08-12T18:00:01.000Z" }
|
||||
```
|
||||
|
||||
### Success when both have joined (collect ran)
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"id": 123,
|
||||
"entries_collected": true,
|
||||
"already_collected": false,
|
||||
"escrow_user_id": 456
|
||||
}
|
||||
```
|
||||
|
||||
### Success when only one side joined yet
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"id": 123,
|
||||
"entries_collected": false
|
||||
}
|
||||
```
|
||||
|
||||
### Collect failure (fatal — do not start the match)
|
||||
|
||||
Typical cases:
|
||||
|
||||
| HTTP | Meaning |
|
||||
|------|---------|
|
||||
| 402 | One or both players lack enough RC for `entry_fee` |
|
||||
| 409 | Players/joins incomplete (should not happen if you set both joins) |
|
||||
| 503 | DB / economy RPCs not installed on the server |
|
||||
|
||||
On any non-2xx after the second join, **abort kickoff**, disconnect both clients, and do **not** call winner (fees were not escrowed).
|
||||
|
||||
Idempotent: repeating the join PATCH after a successful collect returns `entries_collected: true` (or `already_collected: true`).
|
||||
|
||||
## Winner PATCH
|
||||
|
||||
`PATCH /internal/match/:matchId/winner`
|
||||
|
||||
```json
|
||||
{ "winner": "red" }
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```json
|
||||
{ "winner": "blue" }
|
||||
```
|
||||
|
||||
Requires entry fees already collected. Response includes economy summary (`entry_fee_rc`, `rc_prize`, `participant_cc`).
|
||||
|
||||
| HTTP | Meaning |
|
||||
|------|---------|
|
||||
| 200 | Settled (or already settled for the same winner) |
|
||||
| 409 | Entries not collected yet, or winner already set to the other side |
|
||||
| 500 | Escrow inconsistency (should be rare; escalate) |
|
||||
|
||||
Call winner **exactly once** when the match outcome is known. Retries with the **same** side are safe if the first call succeeded.
|
||||
|
||||
## Always finish — disconnect / forfeit rules
|
||||
|
||||
Matches must **never** end without a winner call after fees were collected.
|
||||
|
||||
Track disconnect order on the dedicated server (timestamps or a queue).
|
||||
|
||||
1. **One player disconnects mid-match**
|
||||
Remaining player wins.
|
||||
Example: blue disconnects → `{ "winner": "red" }`.
|
||||
|
||||
2. **Both players disconnect**
|
||||
The **last player who disconnected is the winner**.
|
||||
Example: red disconnects first, then blue → blue was last to leave → `{ "winner": "blue" }`.
|
||||
|
||||
3. **Process crash / forced shutdown**
|
||||
If fees were collected, choose a winner with the same rules (remaining player, or last disconnect) and call winner **before** exit whenever possible.
|
||||
|
||||
4. **Normal score / time win**
|
||||
Report the actual winning side as today.
|
||||
|
||||
Do **not** leave escrow locked with no winner. Do **not** invent a draw path that skips the winner endpoint.
|
||||
|
||||
## Rematch
|
||||
|
||||
`POST /internal/rematch` still only creates the room/match row. Entry fees are **not** taken there. Collect still happens when both `*_joined_at` are set on the new `match_id`.
|
||||
|
||||
## Checklist for the Unity build
|
||||
|
||||
- [ ] Set `red_joined_at` / `blue_joined_at` in UTC ISO when each client is fully connected
|
||||
- [ ] Treat 402/4xx/5xx on the second join as fatal — no kickoff
|
||||
- [ ] Gate gameplay start on `entries_collected: true`
|
||||
- [ ] Always call winner after collect (score, forfeit, or last-disconnect-wins)
|
||||
- [ ] Persist disconnect order until winner is acknowledged
|
||||
Reference in New Issue
Block a user