Skip to content

Commit

Permalink
enhance(reversi): more robust matching process
Browse files Browse the repository at this point in the history
  • Loading branch information
syuilo committed Jan 24, 2024
1 parent cc420c2 commit 65557d5
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 24 deletions.
62 changes: 51 additions & 11 deletions packages/backend/src/core/ReversiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import * as Redis from 'ioredis';
import { ModuleRef } from '@nestjs/core';
import * as Reversi from 'misskey-reversi';
import { IsNull } from 'typeorm';
import { IsNull, LessThan } from 'typeorm';
import type {
MiReversiGame,
ReversiGamesRepository,
Expand All @@ -24,7 +24,7 @@ import { Serialized } from '@/types.js';
import { ReversiGameEntityService } from './entities/ReversiGameEntityService.js';
import type { OnApplicationShutdown, OnModuleInit } from '@nestjs/common';

const MATCHING_TIMEOUT_MS = 1000 * 15; // 15sec
const MATCHING_TIMEOUT_MS = 1000 * 10; // 10sec

@Injectable()
export class ReversiService implements OnApplicationShutdown, OnModuleInit {
Expand Down Expand Up @@ -89,11 +89,27 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
}

@bindThis
public async matchSpecificUser(me: MiUser, targetUser: MiUser): Promise<MiReversiGame | null> {
public async matchSpecificUser(me: MiUser, targetUser: MiUser, multiple = false): Promise<MiReversiGame | null> {
if (targetUser.id === me.id) {
throw new Error('You cannot match yourself.');
}

if (!multiple) {
// 既にマッチしている対局が無いか探す(3分以内)
const games = await this.reversiGamesRepository.find({
where: [
{ id: LessThan(this.idService.gen(Date.now() - 1000 * 60 * 3)), user1Id: me.id, user2Id: targetUser.id, isStarted: false },
{ id: LessThan(this.idService.gen(Date.now() - 1000 * 60 * 3)), user1Id: targetUser.id, user2Id: me.id, isStarted: false },
],
relations: ['user1', 'user2'],
order: { id: 'DESC' },
});
if (games.length > 0) {
return games[0];
}
}

//#region 相手から既に招待されてないか確認
const invitations = await this.redisClient.zrange(
`reversi:matchSpecific:${me.id}`,
Date.now() - MATCHING_TIMEOUT_MS,
Expand All @@ -106,19 +122,35 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
const game = await this.matched(targetUser.id, me.id);

return game;
} else {
this.redisClient.zadd(`reversi:matchSpecific:${targetUser.id}`, Date.now(), me.id);
}
//#endregion

this.globalEventService.publishReversiStream(targetUser.id, 'invited', {
user: await this.userEntityService.pack(me, targetUser),
});
this.redisClient.zadd(`reversi:matchSpecific:${targetUser.id}`, Date.now(), me.id);

return null;
}
this.globalEventService.publishReversiStream(targetUser.id, 'invited', {
user: await this.userEntityService.pack(me, targetUser),
});

return null;
}

@bindThis
public async matchAnyUser(me: MiUser): Promise<MiReversiGame | null> {
public async matchAnyUser(me: MiUser, multiple = false): Promise<MiReversiGame | null> {
if (!multiple) {
// 既にマッチしている対局が無いか探す(3分以内)
const games = await this.reversiGamesRepository.find({
where: [
{ id: LessThan(this.idService.gen(Date.now() - 1000 * 60 * 3)), user1Id: me.id, isStarted: false },
{ id: LessThan(this.idService.gen(Date.now() - 1000 * 60 * 3)), user2Id: me.id, isStarted: false },
],
relations: ['user1', 'user2'],
order: { id: 'DESC' },
});
if (games.length > 0) {
return games[0];
}
}

//#region まず自分宛ての招待を探す
const invitations = await this.redisClient.zrange(
`reversi:matchSpecific:${me.id}`,
Expand Down Expand Up @@ -169,6 +201,14 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
await this.redisClient.zrem('reversi:matchAny', user.id);
}

@bindThis
public async cleanOutdatedGames() {
await this.reversiGamesRepository.delete({
id: LessThan(this.idService.gen(Date.now() - 1000 * 60 * 10)),
isStarted: false,
});
}

@bindThis
public async gameReady(gameId: MiReversiGame['id'], user: MiUser, ready: boolean) {
const game = await this.get(gameId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import type { Config } from '@/config.js';
import { ReversiService } from '@/core/ReversiService.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';

Expand All @@ -32,6 +33,7 @@ export class CleanProcessorService {
private roleAssignmentsRepository: RoleAssignmentsRepository,

private queueLoggerService: QueueLoggerService,
private reversiService: ReversiService,
private idService: IdService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('clean');
Expand Down Expand Up @@ -65,6 +67,8 @@ export class CleanProcessorService {
});
}

this.reversiService.cleanOutdatedGames();

this.logger.succ('Cleaned.');
}
}
3 changes: 2 additions & 1 deletion packages/backend/src/server/api/endpoints/reversi/match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const paramDef = {
type: 'object',
properties: {
userId: { type: 'string', format: 'misskey:id', nullable: true },
multiple: { type: 'boolean', default: false },
},
required: [],
} as const;
Expand All @@ -56,7 +57,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw err;
}) : null;

const game = target ? await this.reversiService.matchSpecificUser(me, target) : await this.reversiService.matchAnyUser(me);
const game = target ? await this.reversiService.matchSpecificUser(me, target, ps.multiple) : await this.reversiService.matchAnyUser(me, ps.multiple);

if (game == null) return;

Expand Down
6 changes: 4 additions & 2 deletions packages/frontend/src/pages/reversi/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,9 @@ if ($i) {
const connection = useStream().useChannel('reversi');

connection.on('matched', x => {
startGame(x.game);
if (matchingUser.value != null || matchingAny.value) {
startGame(x.game);
}
});

connection.on('invited', invitation => {
Expand Down Expand Up @@ -222,7 +224,7 @@ async function accept(user) {
}
}

useInterval(matchHeatbeat, 1000 * 10, { immediate: false, afterMounted: true });
useInterval(matchHeatbeat, 1000 * 5, { immediate: false, afterMounted: true });

onMounted(() => {
misskeyApi('reversi/invitations').then(_invitations => {
Expand Down
4 changes: 2 additions & 2 deletions packages/misskey-js/src/autogen/apiClientJSDoc.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* version: 2024.2.0-beta.3
* generatedAt: 2024-01-23T01:22:13.177Z
* version: 2024.2.0-beta.4
* generatedAt: 2024-01-24T01:14:40.901Z
*/

import type { SwitchCaseResponseType } from '../api.js';
Expand Down
4 changes: 2 additions & 2 deletions packages/misskey-js/src/autogen/endpoint.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* version: 2024.2.0-beta.3
* generatedAt: 2024-01-23T01:22:13.175Z
* version: 2024.2.0-beta.4
* generatedAt: 2024-01-24T01:14:40.899Z
*/

import type {
Expand Down
4 changes: 2 additions & 2 deletions packages/misskey-js/src/autogen/entities.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* version: 2024.2.0-beta.3
* generatedAt: 2024-01-23T01:22:13.173Z
* version: 2024.2.0-beta.4
* generatedAt: 2024-01-24T01:14:40.897Z
*/

import { operations } from './types.js';
Expand Down
4 changes: 2 additions & 2 deletions packages/misskey-js/src/autogen/models.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* version: 2024.2.0-beta.3
* generatedAt: 2024-01-23T01:22:13.172Z
* version: 2024.2.0-beta.4
* generatedAt: 2024-01-24T01:14:40.896Z
*/

import { components } from './types.js';
Expand Down
6 changes: 4 additions & 2 deletions packages/misskey-js/src/autogen/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
/* eslint @typescript-eslint/no-explicit-any: 0 */

/*
* version: 2024.2.0-beta.3
* generatedAt: 2024-01-23T01:22:13.093Z
* version: 2024.2.0-beta.4
* generatedAt: 2024-01-24T01:14:40.815Z
*/

/**
Expand Down Expand Up @@ -25799,6 +25799,8 @@ export type operations = {
'application/json': {
/** Format: misskey:id */
userId?: string | null;
/** @default false */
multiple?: boolean;
};
};
};
Expand Down

0 comments on commit 65557d5

Please sign in to comment.