7.3 KiB
Prompt for Unity Cursor — no CC on forfeit (including skip/auto-forfeit)
Copy everything below the line into Unity Cursor. Do not change join/collect, rematch, hidden MMR display, or player-facing UI.
You are working in the Kick Kings dedicated server Unity project.
The matchmaker already awards 100 CC only to players still connected at settle. That is not enough: a skip-forfeit / auto-forfeit player stays connected, so they still received CC. The API now also withholds CC from anyone who forfeited.
Your only job: on PATCH /internal/match/{matchId}/winner, send forfeit: true for every forfeit path — leave, disconnect, last-disconnect-wins, and skip/auto-forfeit (two skipped turns). Keep red_connected / blue_connected accurate. Do not infer them from winner. Keep the existing header X-Dedicated-Server-Secret. Call winner exactly once when the outcome is known.
Do not reimplement CC. The API decides who gets it from the flags you send.
Endpoint (unchanged URL)
PATCH /internal/match/{matchId}/winner
JSON body (already exists — fix forfeit on skip/auto-forfeit)
Always send all six fields. JSON types must be real booleans and integers (true not "true", 3 not "3").
{
"winner": "red",
"red_connected": true,
"blue_connected": true,
"red_score": 3,
"blue_score": 1,
"forfeit": false
}
| Field | Type | Rules |
|---|---|---|
winner |
"red" or "blue" |
Winning side. RC prize still goes here. Unchanged. |
red_connected |
bool | Red still had a live connection at settle. Do not infer from winner. |
blue_connected |
bool | Blue still had a live connection at settle. |
red_score |
int | Rounds/sets red has won (first-to-3). Send on every settle. |
blue_score |
int | Same for blue. |
forfeit |
bool | true for leave, disconnect, last-disconnect-wins, and skip/auto-forfeit. false only for a normal score win. |
What changed (this is the bug)
Previous contract: skip-forfeit sent forfeit: false because the skipper was still connected. The API then paid them 100 CC.
New contract: skip-forfeit / auto-forfeit is still not a disconnect (*_connected stays true if they are in the match), but it is a forfeit. Send forfeit: true. The API then pays CC to the connected non-forfeiting side only.
How the API awards CC (do not reimplement)
Let amount = 100. Do not infer connectedness from winner. A last-disconnect winner can be disconnected: they get RC, not CC.
On any forfeit, CC is never paid to the loser.
red_connected |
blue_connected |
forfeit |
CC credit |
|---|---|---|---|
| true | true | false | +amount to both |
| true | true | true | +amount to the winner only (skip/auto-forfeit) |
| true | false | true | +amount to red only |
| false | true | true | +amount to blue only |
| false | false | true | nobody |
RC prize / escrow is unchanged: still paid to winner.
Connected flags (unchanged meaning)
Set a side to false when:
- Mirror reports disconnect and reconnect grace expired
- That player pressed Leave (even if TCP is still up for a moment)
true = still in the match when you report the outcome.
Skip-forfeit is not a disconnect. If that player is still in the match, keep their *_connected: true and send forfeit: true.
What to send in each end condition
1. Normal score win (first to 3)
Both still in the match. forfeit: false. Both get CC.
{ "winner": "red", "red_connected": true, "blue_connected": true, "red_score": 3, "blue_score": 1, "forfeit": false }
2. One player leaves or disconnects
Remaining player wins. Leaver *_connected: false. forfeit: true. Only the remaining connected player gets CC.
{ "winner": "red", "red_connected": true, "blue_connected": false, "red_score": 1, "blue_score": 0, "forfeit": true }
3. Both disconnect (last-disconnect-wins)
Last player who disconnected is winner. Both *_connected: false. forfeit: true. Nobody gets CC. RC still goes to winner.
{ "winner": "blue", "red_connected": false, "blue_connected": false, "red_score": 0, "blue_score": 2, "forfeit": true }
4. Skip-forfeit / auto-forfeit (two skipped turns) — this is the change
Not a disconnect. Keep *_connected: true if they are still in the match. Send forfeit: true. Winner is whoever the game rules already pick. The skipper (loser) does not get CC. The opponent gets CC if connected.
{ "winner": "red", "red_connected": true, "blue_connected": true, "red_score": 1, "blue_score": 0, "forfeit": true }
5. Process crash / forced shutdown after fees were collected
Same as remaining-player or last-disconnect. Send accurate *_connected, scores, and forfeit: true if it was a disconnect/forfeit path. Call winner before exit whenever possible.
Never skip the winner call after collect. Never invent a draw.
Success response (use cc_awarded_* as today)
{
"ok": true,
"id": 123,
"winner": "red",
"winner_id": 456,
"already_settled": false,
"economy": {
"entry_fee_rc": 21,
"rc_prize": 34,
"participant_cc": 100,
"cc_awarded_red": true,
"cc_awarded_blue": false
},
"mmr_delta": 27,
"forfeit": true
}
- Keep using
economy.cc_awarded_red/cc_awarded_blueas today. mmr_deltaandforfeiton the response are for server logs only. Do not forward them to player clients. Do not show them in UI.- Honor response
winner/winner_id. On simultaneous Leave/skip, a secondforfeit: truePATCH returns 200already_settled: truewith the first settle’s winner, not 409.
HTTP: 200 = settled, already settled same winner, or a second forfeit that lost the race. 409 = entries not collected, or winner already set to the other side on a non-forfeit conflict.
Where to change
Find the dedicated-server method that PATCHes /internal/match/{id}/winner.
Today skip-forfeit / two skipped turns likely sets forfeit = false because the player is still connected. Change that path so forfeit = true.
Leave, disconnect, and last-disconnect should already send forfeit: true. Do not regress those.
Payload shape is unchanged:
public sealed class WinnerReportBody
{
public string winner; // "red" | "blue"
public bool red_connected;
public bool blue_connected;
public int red_score;
public int blue_score;
public bool forfeit;
}
Do not
- Set
forfeit: falsefor skip-forfeit / auto-forfeit - Set
*_connected: falsefor skip-forfeit just to kill CC (the player is still in the match; that would also switch hidden MMR to the 20% disconnect path) - Infer
red_connected/blue_connectedfrom who won - Change join PATCH (
red_joined_at/blue_joined_at) or rematch - Show MMR,
mmr_delta, or CC award flags in player UI unless they are already shown - Skip the winner call after fees were collected
Done when
- Normal score wins still send
forfeit: false - Leave / disconnect / last-disconnect still send
forfeit: trueand*_connected: falseon leavers - Skip-forfeit / auto-forfeit sends
forfeit: truewith accurate*_connected(usually bothtrue) - Skipper no longer receives CC; opponent still does if connected
- Winner is still always called after fees were collected