a35acea7e4
Private rooms were 100% client-simulated (the "friend" auto-accepted then bots filled invited seats). Now they're server-authoritative over SignalR: Server (GameManager.PrivateRooms + GameHub): - Room registry with create/invite/accept/decline/addBot/clearSeat/start/leave. - Invite pushes a `roomInvite` to that user (Clients.User); the seat stays "invited" (a pending guest with their real profile, resolved server-side) — it is NEVER replaced by a bot. - StartPrivate refuses while any invite is pending; only EMPTY seats fill with bots. Then it spins up a live GameRoom and matchFound → both devices enter. - Host leave / disconnect closes the room (roomClosed); members free their seat. Client: - signalr-service implements the room methods over the hub (+ room/roomInvite/ roomClosed events, room mapping, onRoomInvite); mock keeps offline no-ops. - online-store accept/declineInvite; RoomScreen blocks "Start" while an invite is pending and auto-enters the live game on matchFound (host + friend). - New global InviteModal (accept/decline) + i18n (invite.*, room.waitAccept). Addresses: (1) no bot replacement, (2) game waits for acceptance, (3) invited friend shown as a pending guest with their name/avatar. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>