컴퓨터 방 자동 정리 + 게스트 계정 삭제(로그아웃·하루 미접속) + 테스트 10분→1분
- 방 화면을 벗어나면(홈·규칙) detach: 자리·접속 상태는 유지, 방 접속 수에서 빠짐 - 컴퓨터가 있는 방: 사람이 모두 [방 나가기]하면 바로 닫고, 아무도 안 보면 대기실·끝난 방 1분, 게임 중 30분 뒤 정리 - 게스트 로그아웃 = 계정 삭제(자리·전적·세션·소켓), 사이트를 닫고 24시간 지난 게스트도 1분 주기로 삭제 - users.last_seen_at을 요청·소켓 종료 때 갱신(이전엔 갱신 안 됨), 003 마이그레이션에서 기존 게스트 시계 초기화 - 테스트: 무작위 대국 시드를 playoutSeeds()로 평소 1/10, test:full은 전부. bun test --parallel=4, 테스트 서버 종료 대기 1s→50ms Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,9 @@ export const MAX_ROOMS = 2000;
|
||||
export const MAX_ROOMS_PER_USER = 3;
|
||||
const EMPTY_LOBBY_TTL = 10 * 60_000;
|
||||
const FINISHED_TTL = 30 * 60_000;
|
||||
/** Rooms with computer players nobody is looking at: closed this long after the last person went away. */
|
||||
const PRACTICE_IDLE_TTL = 60_000;
|
||||
const PRACTICE_GAME_TTL = 30 * 60_000;
|
||||
|
||||
export interface ManagerDeps {
|
||||
store: RoomStore;
|
||||
@@ -42,6 +45,10 @@ export class RoomManager {
|
||||
log: this.deps.log,
|
||||
settings: this.deps.settings,
|
||||
botDelay: this.deps.botDelay,
|
||||
onEmpty: (room, why) => {
|
||||
// Everyone left a room with computer players on purpose: nobody can come back to it, close it now.
|
||||
if (why === 'leave' && this.byCode.get(room.code) === room && room.hasBots() && !room.hasPeopleSeated()) this.close(room);
|
||||
},
|
||||
onSeatChange: (room, userId, seated) => {
|
||||
if (isBotId(userId)) return;
|
||||
const set = this.seatIndex.get(userId) ?? new Set<string>();
|
||||
@@ -150,6 +157,11 @@ export class RoomManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Frees every seat the user holds (account removed). */
|
||||
leaveAll(userId: string): void {
|
||||
for (const code of [...(this.seatIndex.get(userId) ?? [])]) this.byCode.get(code)?.leave(userId);
|
||||
}
|
||||
|
||||
publicRooms(gameId: string | null): { code: string; gameId: string; host: string; seated: number; max: number; options: unknown }[] {
|
||||
return this.all()
|
||||
// Rooms with computer players are practice rooms: never listed, never filled by quick play.
|
||||
@@ -198,6 +210,13 @@ export class RoomManager {
|
||||
for (const room of this.all()) {
|
||||
if (room.conns.size > 0) continue;
|
||||
const idle = now - room.lastActiveAt;
|
||||
if (room.hasBots()) {
|
||||
// Practice rooms are private and only for the people who made them: once nobody is looking, close soon.
|
||||
const empty = now - (room.emptySince ?? now);
|
||||
const ttl = room.status === 'playing' || room.status === 'paused' ? PRACTICE_GAME_TTL : PRACTICE_IDLE_TTL;
|
||||
if (empty >= ttl) this.close(room);
|
||||
continue;
|
||||
}
|
||||
const closable =
|
||||
(room.status === 'lobby' && idle > EMPTY_LOBBY_TTL) ||
|
||||
(room.status === 'finished' && idle > FINISHED_TTL) ||
|
||||
|
||||
Reference in New Issue
Block a user