## Description:
Implements 2v2 ranked matchmaking end-to-end against the matchmaking
API's 2v2 queues (API PR #419): core team pinning, the server's second
checkin loop and game creation, and the client UI.
## Core — deterministic team pinning
The matcher's assignment specifies exactly who plays with whom (`teams:
[[a,d],[b,c]]`), but team assignment previously only did clan/friend
balancing and could scramble the ELO-balanced split.
- `PlayerSchema`/`PlayerInfo` gain an optional **`teamIndex`** — a
server-stamped index into the game's team list, part of `GameStartInfo`
so it's identical on every client (same category as `clanTag`/`friends`,
which already feed deterministic team assignment).
- `assignTeams` honors pins **unconditionally** — before clan/friend
grouping and past `maxTeamSize` (the matcher's balancing is
authoritative) — and seeds the counts that balancing of any unpinned
players sees. Pinned players still participate in the friend graph, so
an unpinned friend is pulled toward a pinned player's team.
- publicIds never enter core: the game server resolves publicId →
teamIndex per client at game start.
## Server
- **One checkin long-poll per mode.** Both loops send `mode` explicitly
(the API deployed ahead of the client, so no omit-for-back-compat
needed).
- **`get2v2Config()`**: Team mode, `playerTeams: 2`, `maxPlayers: 4`,
always-compact map, donations enabled (matching public team games),
`rankedType: "2v2"` (the API's 2v2 ingestion has shipped; `RankedType`
gains `TwoVTwo`).
- **The assignment payload is now used** (it was previously discarded):
`players` → `allowedPublicIds` so only the matched accounts can take the
slots (also hardens 1v1), and `teams` → `teamIndex` stamps at game
start. A malformed assignment logs a warning and falls back to creating
the game without pins rather than stranding matched players.
- The 3-clients-per-IP cap on public games applies to matchmade games
too (an allowlist doesn't stop one person multi-tabbing multiple
accounts). It is now skipped in dev, where local testing (multi-tab, the
4-player e2e) is inherently same-IP — matching the existing dev/prod
gating of Turnstile and the duplicate-account kick.
## Client
- Ranked modal's 2v2 card is enabled; it passes the mode through
`open-matchmaking` (dispatchers without a detail — homepage button,
requeue URL — still mean 1v1).
- Matchmaking modal joins with `&mode=1v1`/`&mode=2v2`, shows a 2v2
title (`matchmaking_modal.title_2v2` in en.json), and shows the real 2v2
ELO from the new `leaderboard.twoVtwo` field in `/users/@me` (the ranked
modal's 2v2 card does too).
- WinModal shows requeue for any ranked game and carries the mode back
into the right queue (`/?requeue=2v2`).
- 2v2 ranked stats surface in the player stats tree (labeled via
`player_stats_tree.ranked_2v2`).
## Harnesses (`tests/matchmaking/`)
- Contained: the fake server captures the `mode` query param; asserts
each queue sends its mode explicitly. **10/10.**
- E2E: `MM_MODE=2v2` runs four real browser players through the real
local worker's 2v2 queue and rides the flow into the started game.
Asserts same gameId for all four, the 2v2 config, allowlist admission,
and a **deterministic 2 vs 2 in-game split read from each client's
GameView** (the software-WebGL gate is spoofed in test pages only).
**8/8.** 1v1 e2e still **6/6.**
## Verification
- `npm test`: 2,053 tests pass, including 7 new (6 `assignTeams` pinning
unit tests + a full-game pinned-split test through `setup()`).
- `npx tsc --noEmit`, ESLint clean.
- Live e2e against a local `wrangler dev` API worker: 1v1 (6/6) and 2v2
(8/8) as above.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
(UI changes — the ranked modal's 1v1/2v2 cards — were verified with
before/after screenshots in the live app during development.)
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Fixes#3911
## Description:
- Server captures `publicId` and `friends` from `getUserMe()` and
includes each player's in-game friend `clientID`s in `PlayerSchema` on
game start
- Team assignment treats friends as a **soft preference** (best-effort):
a non-clan player goes to the team where the most of their friends
already are; if that team is full they spill to the next-emptiest team
rather than getting kicked
- Clans remain strict (kick overflow) since clan membership is an
explicit opt-in; friends are implicit, so a friend-of-friend chain that
doesn't fit shouldn't bench anyone
- Friendship is symmetric — an edge from either direction counts, which
keeps things working when one side's `getUserMe` is stale
- Lobby preview unchanged — friend grouping only takes effect once the
game actually starts (avoids exposing friend lists in the lobby payload)
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
In commit
https://github.com/openfrontio/OpenFrontIO/commit/bbf72bd14f7f31146c687523aea8fc0aff31bbe1#diff-ee2fcbca50d87cc09d2c7d2667210defe2e3e111239820c89c40283be5385b64
it was added that startManpower in DefaultConfig used
playerInfo.nation.strength to set the starting troops for a Nation. In
ExecutionManager, param nation of PlayerInfo was set during the
instantiation of a new FakeHumanExecution.
However in commit
https://github.com/openfrontio/OpenFrontIO/commit/d6a412aa50dd86d474d80c216fd9ba36e7426ef9#diff-2d0a5d8b171d8b504f934891025e42742e142ef0964d6e17712bfdcd30bf050c
the changes made it so that **param nation of PlayerInfo was never
set**. While startManpower in DefaultConfig still checked for
playerinfo.nation.strength. Since it was always undefined, it would use
a multiplier of 1 instead of the actual nation strength.
This PR fixes it by passing the nation strength to param nationStrength
in PlayerInfo. Removing param strength from class Nation. Strength isn't
used anywhere else so this isn't a problem and it also consolidates
human player info and nation player info even more. We could have also
used the Nation.strength directly, but that would have required more
code in addPlayers and addPlayer in GameImpl, especially for Teams
games. So this PR has the simplest solution.
- I did add a config setting useNationStrengthForStartManpower with a
comment that explains its reason for being. Namely that in the months
that startManpower didn't get to use nation strength because of the bug,
FakeHumans have become much harder to fight. Re-enabling higher starting
troops from this fix would make them even harder to fight, and i think
rebalancing is needed before that.
- Or we could decide to scrap Nation strength altogether, as it is only
ever used to set starting troops anyway. This would make map making a
little easier as a bycatch.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
tryout33
## Description:
Remove Cosmetics from PlayerInfo. The game engine should have no
knowledge of cosmetics since they shouldn't affect game play at all.
Instead pass player cosmetics into the GameView.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
This is meant to give players more customization options.
Permission handling hasn’t really been implemented yet.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors
## Please put your Discord username so you can be contacted if a bug or
regression is found:
aotumuri
## Description:
Changes the team order to Red, Blue, Yellow, Green, Orange, Purple, Teal
instead of what it originally was (Red, Blue, Teal, Purple, Yellow,
Orange, Green)
Teal and Blue look similar, so I think it's better to move them further
apart. Instead, the primary colors are chosen first.
Fixes#614
In the long run, I think it would be nice to have the teams randomly
chosen from available colors and not always in the same order - e.g. a
three-team game isn't always Red, Blue, and Yellow, it could be any mix
of the colors. However, my programming skills aren't great enough to do
that.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors
## Please put your Discord username so you can be contacted if a bug or
regression is found:
Spartan Oligarchy/Loymdayddaud
## Description:
Implement Duos team mode.
Also assign teams to nations at the start instead of assigning them
randomly on spawn. This gives more consistent team sizes
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors
## Please put your Discord username so you can be contacted if a bug or
regression is found:
<DISCORD USERNAME>
Co-authored-by: evan <openfrontio@gmail.com>
## Description:
- Allow up to seven teams for players, and one for bots.
- Add team count selection to the single player dialog.
- Select random number of teams in server rotation.
Fixes#456
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors


## Please put your Discord username so you can be contacted if a bug or
regression is found:
fake.neo
---------
Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
## Description:
Teams are not sorted based on clans, clan members always stay on the
same team.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors
## Please put your Discord username so you can be contacted if a bug or
regression is found:
<DISCORD USERNAME>