Skip to content

feat(adminbot): let create_game pin teams - #4944

Open
Zixer1 wants to merge 1 commit into
mainfrom
feat/adminbot-pin-teams
Open

feat(adminbot): let create_game pin teams#4944
Zixer1 wants to merge 1 commit into
mainfrom
feat/adminbot-pin-teams

Conversation

@Zixer1

@Zixer1 Zixer1 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Lets a bot say who plays with whom in a Team game.

GameManager.createGame already accepts matchmakingTeams, and GameServer already resolves a client's pinned slot from it — but the admin-bot route called createGame with three arguments, so it was always undefined. A bot could create a Team game but not control the pairings; the balancer decided, which is no use for a tournament running fixed pairs.

create_game now takes an optional teams: string[][] of publicIds, read alongside the config rather than through GameConfig — same treatment as listed, so it can't be set later via update_game_config.

Rejects two cases rather than accepting a request that can't do what it asks:

  • teams in FFA, where assignTeams never runs so the pin would be silently inert
  • a publicId in two teams, where findIndex takes the first and the caller would get a team they didn't ask for

Follow-up (not here): teams can't be amended after create, so a player added to the allowlist pre-start can't be pinned to their partner.

GameManager.createGame already accepts matchmakingTeams and GameServer already
resolves a client's pinned slot from it, but the admin-bot route called
createGame with three arguments, so it was always undefined. A bot could create
a Team game but not say who plays with whom — the balancer decided, which is no
use for a tournament running fixed pairs.

create_game now takes an optional teams: string[][] of publicIds. It is read
alongside the config rather than through GameConfig, for the same reason as
listed: the schema parse strips it, so it can never be set later via
update_game_config.

Rejects teams in FFA (that mode never runs assignTeams, so the pin would be
silently inert) and a publicId appearing in two teams (findIndex takes the
first, so the caller would get a team it did not ask for).
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The admin bot game creation route now accepts optional team assignments. It enforces team and member limits, requires Team mode, rejects duplicate public IDs, forwards valid assignments to gm.createGame, and adds focused tests.

Changes

Admin bot team pinning

Layer / File(s) Summary
Team assignment validation
src/server/AdminBotRoutes.ts
The route validates optional team assignments, enforces size limits, requires GameMode.Team, and rejects duplicate public IDs.
Game creation forwarding and coverage
src/server/AdminBotRoutes.ts, tests/server/AdminBotCreateTeams.test.ts
The route forwards validated teams to gm.createGame. Tests cover pinned, unpinned, invalid, malformed, and empty team inputs.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AdminBot
  participant AdminBotRoutes
  participant GameManager
  AdminBot->>AdminBotRoutes: Submit game creation request
  AdminBotRoutes->>AdminBotRoutes: Validate team assignments and game mode
  AdminBotRoutes->>GameManager: Call createGame with validated teams
  GameManager-->>AdminBotRoutes: Return created game
Loading

Possibly related PRs

Suggested reviewers: evanpelle

Poem

Teams line up, pins hold fast,
Limits guard the gateway passed.
Team mode opens, duplicates flee,
Valid groups reach the game manager tree.
Empty teams remain allowed and free.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: allowing the admin bot to pin teams during game creation.
Description check ✅ Passed The description directly explains team pinning, validation rules, affected APIs, and the scope of the change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/server/AdminBotRoutes.ts`:
- Around line 119-139: Update the validation and team-assignment error responses
in the route handler to pass all user-visible messages through translateText()
before returning them: the z.prettifyError(teamsParsed.error) result, the
gameMode Team message, and the duplicate publicId message. Preserve the existing
status codes and error content while using the established translation context
and interpolation pattern.

In `@tests/server/AdminBotCreateTeams.test.ts`:
- Around line 8-37: Replace the mock-based captureCreateHandler tests with
setup() using a map from tests/testdata/maps/. Create the game through the real
GameManager and route flow, then exercise the resulting game to verify
matchmakingTeams reaches the game and produces the expected team assignment.
Remove the mocked Express and GameManager objects while preserving the route
behavior under test.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 93a05d6d-88ff-4515-904a-1dc8319cc96a

📥 Commits

Reviewing files that changed from the base of the PR and between 49d52b0 and b570ea2.

📒 Files selected for processing (2)
  • src/server/AdminBotRoutes.ts
  • tests/server/AdminBotCreateTeams.test.ts

Comment on lines +119 to +139
if (!teamsParsed.success) {
return res
.status(400)
.json({ error: z.prettifyError(teamsParsed.error) });
}
const teams = teamsParsed.data.teams;
if (teams !== undefined) {
// FFA never runs assignTeams, so a pin there would be silently inert.
// Refuse rather than accept a request that cannot do what it asks.
if (config.gameMode !== GameMode.Team) {
return res.status(400).json({ error: "teams require gameMode Team" });
}
// A publicId in two teams has no single answer (findIndex takes the first),
// so the caller would get a team it did not ask for.
const seen = new Set<string>();
for (const team of teams) {
for (const publicId of team) {
if (seen.has(publicId)) {
return res
.status(400)
.json({ error: `publicId in more than one team: ${publicId}` });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Localize the new API error text.

Lines 122, 129, and 139 return user-visible text without translateText(). Localize the Zod validation response and both literal error responses before returning them.

As per coding guidelines, “All user-visible text must go through translateText().”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/AdminBotRoutes.ts` around lines 119 - 139, Update the validation
and team-assignment error responses in the route handler to pass all
user-visible messages through translateText() before returning them: the
z.prettifyError(teamsParsed.error) result, the gameMode Team message, and the
duplicate publicId message. Preserve the existing status codes and error content
while using the established translation context and interpolation pattern.

Source: Coding guidelines

Comment on lines +8 to +37
function captureCreateHandler() {
const routes: Record<string, (req: any, res: any) => void> = {};
const app: any = {
post(path: string, ...handlers: ((req: any, res: any) => void)[]) {
routes[path] = handlers[handlers.length - 1];
},
get() {},
};
const created: {
config?: Record<string, unknown>;
teams?: string[][];
} = {};
const gm: any = {
createGame(
_id: string,
config: Record<string, unknown>,
_creator?: string,
_startsAt?: number,
_publicGameType?: unknown,
matchmakingTeams?: string[][],
) {
created.config = config;
created.teams = matchmakingTeams;
return { setListed: vi.fn(), gameInfo: () => ({ gameID: "x" }) };
},
};
const log: any = { info: vi.fn(), warn: vi.fn(), error: vi.fn() };
registerAdminBotRoutes({ app, gm, workerId: 0, log });
return { handler: routes["/api/adminbot/create_game"], created };
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Test the route with a real game instance.

captureCreateHandler() replaces GameManager.createGame with a mock. The tests only verify captured arguments. They do not verify that matchmakingTeams reaches the game or affects team assignment.

Use setup() with a map from tests/testdata/maps/. Exercise the created game and team assignment without mocked GameManager or Express objects.

As per coding guidelines, “Use the setup() helper … and exercise core simulation directly rather than using mocks.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/server/AdminBotCreateTeams.test.ts` around lines 8 - 37, Replace the
mock-based captureCreateHandler tests with setup() using a map from
tests/testdata/maps/. Create the game through the real GameManager and route
flow, then exercise the resulting game to verify matchmakingTeams reaches the
game and produces the expected team assignment. Remove the mocked Express and
GameManager objects while preserving the route behavior under test.

Source: Coding guidelines

@github-project-automation github-project-automation Bot moved this from Triage to Development in OpenFront Release Management Aug 11, 2026
@Zixer1 Zixer1 added this to the v34 milestone Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Development

Development

Successfully merging this pull request may close these issues.

1 participant