enhance(reversi): 変則なしマッチングを可能に
This commit is contained in:
parent
2b6bf074c6
commit
5719a929ad
|
@ -9595,6 +9595,14 @@ export interface Locale extends ILocale {
|
||||||
* 相手が設定を変更しました
|
* 相手が設定を変更しました
|
||||||
*/
|
*/
|
||||||
"opponentHasSettingsChanged": string;
|
"opponentHasSettingsChanged": string;
|
||||||
|
/**
|
||||||
|
* 変則許可 (完全フリー)
|
||||||
|
*/
|
||||||
|
"allowIrregularRules": string;
|
||||||
|
/**
|
||||||
|
* 変則なし
|
||||||
|
*/
|
||||||
|
"disallowIrregularRules": string;
|
||||||
};
|
};
|
||||||
"_offlineScreen": {
|
"_offlineScreen": {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2556,6 +2556,8 @@ _reversi:
|
||||||
shareToTlTheGameWhenStart: "開始時に対局をタイムラインに投稿"
|
shareToTlTheGameWhenStart: "開始時に対局をタイムラインに投稿"
|
||||||
iStartedAGame: "対局を開始しました! #MisskeyReversi"
|
iStartedAGame: "対局を開始しました! #MisskeyReversi"
|
||||||
opponentHasSettingsChanged: "相手が設定を変更しました"
|
opponentHasSettingsChanged: "相手が設定を変更しました"
|
||||||
|
allowIrregularRules: "変則許可 (完全フリー)"
|
||||||
|
disallowIrregularRules: "変則なし"
|
||||||
|
|
||||||
_offlineScreen:
|
_offlineScreen:
|
||||||
title: "オフライン - サーバーに接続できません"
|
title: "オフライン - サーバーに接続できません"
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class Reversi61706081514499 {
|
||||||
|
name = 'Reversi61706081514499'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "reversi_game" ADD "noIrregularRules" boolean NOT NULL DEFAULT false`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "reversi_game" DROP COLUMN "noIrregularRules"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -85,6 +85,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
|
||||||
map: game.map,
|
map: game.map,
|
||||||
bw: game.bw,
|
bw: game.bw,
|
||||||
crc32: game.crc32,
|
crc32: game.crc32,
|
||||||
|
noIrregularRules: game.noIrregularRules,
|
||||||
} satisfies Partial<MiReversiGame>;
|
} satisfies Partial<MiReversiGame>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +139,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async matchAnyUser(me: MiUser, multiple = false): Promise<MiReversiGame | null> {
|
public async matchAnyUser(me: MiUser, options: { noIrregularRules: boolean }, multiple = false): Promise<MiReversiGame | null> {
|
||||||
if (!multiple) {
|
if (!multiple) {
|
||||||
// 既にマッチしている対局が無いか探す(3分以内)
|
// 既にマッチしている対局が無いか探す(3分以内)
|
||||||
const games = await this.reversiGamesRepository.find({
|
const games = await this.reversiGamesRepository.find({
|
||||||
|
@ -177,19 +178,29 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
|
||||||
2, // 自分自身のIDが入っている場合もあるので2つ取得
|
2, // 自分自身のIDが入っている場合もあるので2つ取得
|
||||||
'REV');
|
'REV');
|
||||||
|
|
||||||
const userIds = matchings.filter(id => id !== me.id);
|
const items = matchings.filter(id => !id.startsWith(me.id));
|
||||||
|
|
||||||
if (userIds.length > 0) {
|
if (items.length > 0) {
|
||||||
const matchedUserId = userIds[0];
|
const [matchedUserId, option] = items[0].split(':');
|
||||||
|
|
||||||
await this.redisClient.zrem('reversi:matchAny', me.id, matchedUserId);
|
await this.redisClient.zrem('reversi:matchAny',
|
||||||
|
me.id,
|
||||||
|
matchedUserId,
|
||||||
|
me.id + ':noIrregularRules',
|
||||||
|
matchedUserId + ':noIrregularRules');
|
||||||
|
|
||||||
const game = await this.matched(matchedUserId, me.id);
|
const game = await this.matched(matchedUserId, me.id, {
|
||||||
|
noIrregularRules: options.noIrregularRules || option === 'noIrregularRules',
|
||||||
|
});
|
||||||
|
|
||||||
return game;
|
return game;
|
||||||
} else {
|
} else {
|
||||||
const redisPipeline = this.redisClient.pipeline();
|
const redisPipeline = this.redisClient.pipeline();
|
||||||
|
if (options.noIrregularRules) {
|
||||||
|
redisPipeline.zadd('reversi:matchAny', Date.now(), me.id + ':noIrregularRules');
|
||||||
|
} else {
|
||||||
redisPipeline.zadd('reversi:matchAny', Date.now(), me.id);
|
redisPipeline.zadd('reversi:matchAny', Date.now(), me.id);
|
||||||
|
}
|
||||||
redisPipeline.expire('reversi:matchAny', 15, 'NX');
|
redisPipeline.expire('reversi:matchAny', 15, 'NX');
|
||||||
await redisPipeline.exec();
|
await redisPipeline.exec();
|
||||||
return null;
|
return null;
|
||||||
|
@ -203,7 +214,10 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async matchAnyUserCancel(user: MiUser) {
|
public async matchAnyUserCancel(user: MiUser) {
|
||||||
await this.redisClient.zrem('reversi:matchAny', user.id);
|
const redisPipeline = this.redisClient.pipeline();
|
||||||
|
redisPipeline.zrem('reversi:matchAny', user.id);
|
||||||
|
redisPipeline.zrem('reversi:matchAny', user.id + ':noIrregularRules');
|
||||||
|
await redisPipeline.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -265,7 +279,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async matched(parentId: MiUser['id'], childId: MiUser['id']): Promise<MiReversiGame> {
|
private async matched(parentId: MiUser['id'], childId: MiUser['id'], options: { noIrregularRules: boolean; }): Promise<MiReversiGame> {
|
||||||
const game = await this.reversiGamesRepository.insert({
|
const game = await this.reversiGamesRepository.insert({
|
||||||
id: this.idService.gen(),
|
id: this.idService.gen(),
|
||||||
user1Id: parentId,
|
user1Id: parentId,
|
||||||
|
@ -278,6 +292,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
|
||||||
map: Reversi.maps.eighteight.data,
|
map: Reversi.maps.eighteight.data,
|
||||||
bw: 'random',
|
bw: 'random',
|
||||||
isLlotheo: false,
|
isLlotheo: false,
|
||||||
|
noIrregularRules: options.noIrregularRules,
|
||||||
}).then(x => this.reversiGamesRepository.findOneOrFail({
|
}).then(x => this.reversiGamesRepository.findOneOrFail({
|
||||||
where: { id: x.identifiers[0].id },
|
where: { id: x.identifiers[0].id },
|
||||||
relations: ['user1', 'user2'],
|
relations: ['user1', 'user2'],
|
||||||
|
|
|
@ -61,6 +61,7 @@ export class ReversiGameEntityService {
|
||||||
canPutEverywhere: game.canPutEverywhere,
|
canPutEverywhere: game.canPutEverywhere,
|
||||||
loopedBoard: game.loopedBoard,
|
loopedBoard: game.loopedBoard,
|
||||||
timeLimitForEachTurn: game.timeLimitForEachTurn,
|
timeLimitForEachTurn: game.timeLimitForEachTurn,
|
||||||
|
noIrregularRules: game.noIrregularRules,
|
||||||
logs: game.logs,
|
logs: game.logs,
|
||||||
map: game.map,
|
map: game.map,
|
||||||
});
|
});
|
||||||
|
@ -105,6 +106,7 @@ export class ReversiGameEntityService {
|
||||||
canPutEverywhere: game.canPutEverywhere,
|
canPutEverywhere: game.canPutEverywhere,
|
||||||
loopedBoard: game.loopedBoard,
|
loopedBoard: game.loopedBoard,
|
||||||
timeLimitForEachTurn: game.timeLimitForEachTurn,
|
timeLimitForEachTurn: game.timeLimitForEachTurn,
|
||||||
|
noIrregularRules: game.noIrregularRules,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,11 @@ export class MiReversiGame {
|
||||||
})
|
})
|
||||||
public bw: string;
|
public bw: string;
|
||||||
|
|
||||||
|
@Column('boolean', {
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
|
public noIrregularRules: boolean;
|
||||||
|
|
||||||
@Column('boolean', {
|
@Column('boolean', {
|
||||||
default: false,
|
default: false,
|
||||||
})
|
})
|
||||||
|
|
|
@ -82,6 +82,10 @@ export const packedReversiGameLiteSchema = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
|
noIrregularRules: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
isLlotheo: {
|
isLlotheo: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -196,6 +200,10 @@ export const packedReversiGameDetailedSchema = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
|
noIrregularRules: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
isLlotheo: {
|
isLlotheo: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
|
|
@ -37,6 +37,7 @@ export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
userId: { type: 'string', format: 'misskey:id', nullable: true },
|
userId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
|
noIrregularRules: { type: 'boolean', default: false },
|
||||||
multiple: { type: 'boolean', default: false },
|
multiple: { type: 'boolean', default: false },
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
|
@ -57,7 +58,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
throw err;
|
throw err;
|
||||||
}) : null;
|
}) : null;
|
||||||
|
|
||||||
const game = target ? await this.reversiService.matchSpecificUser(me, target, ps.multiple) : await this.reversiService.matchAnyUser(me, ps.multiple);
|
const game = target
|
||||||
|
? await this.reversiService.matchSpecificUser(me, target, ps.multiple)
|
||||||
|
: await this.reversiService.matchAnyUser(me, { noIrregularRules: ps.noIrregularRules }, ps.multiple);
|
||||||
|
|
||||||
if (game == null) return;
|
if (game == null) return;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div class="_gaps" :class="{ [$style.disallowInner]: isReady }">
|
<div class="_gaps" :class="{ [$style.disallowInner]: isReady }">
|
||||||
<div style="font-size: 1.5em; text-align: center;">{{ i18n.ts._reversi.gameSettings }}</div>
|
<div style="font-size: 1.5em; text-align: center;">{{ i18n.ts._reversi.gameSettings }}</div>
|
||||||
|
|
||||||
|
<template v-if="game.noIrregularRules">
|
||||||
|
<div>{{ i18n.ts._reversi.disallowIrregularRules }}</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<div class="_panel">
|
<div class="_panel">
|
||||||
<div style="display: flex; align-items: center; padding: 16px; border-bottom: solid 1px var(--divider);">
|
<div style="display: flex; align-items: center; padding: 16px; border-bottom: solid 1px var(--divider);">
|
||||||
<div>{{ mapName }}</div>
|
<div>{{ mapName }}</div>
|
||||||
|
@ -75,6 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkSwitch v-model="game.canPutEverywhere" @update:modelValue="updateSettings('canPutEverywhere')">{{ i18n.ts._reversi.canPutEverywhere }}</MkSwitch>
|
<MkSwitch v-model="game.canPutEverywhere" @update:modelValue="updateSettings('canPutEverywhere')">{{ i18n.ts._reversi.canPutEverywhere }}</MkSwitch>
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
|
|
|
@ -157,6 +157,7 @@ if ($i) {
|
||||||
const invitations = ref<Misskey.entities.UserLite[]>([]);
|
const invitations = ref<Misskey.entities.UserLite[]>([]);
|
||||||
const matchingUser = ref<Misskey.entities.UserLite | null>(null);
|
const matchingUser = ref<Misskey.entities.UserLite | null>(null);
|
||||||
const matchingAny = ref<boolean>(false);
|
const matchingAny = ref<boolean>(false);
|
||||||
|
const noIrregularRules = ref<boolean>(false);
|
||||||
|
|
||||||
function startGame(game: Misskey.entities.ReversiGameDetailed) {
|
function startGame(game: Misskey.entities.ReversiGameDetailed) {
|
||||||
matchingUser.value = null;
|
matchingUser.value = null;
|
||||||
|
@ -182,6 +183,7 @@ async function matchHeatbeat() {
|
||||||
} else if (matchingAny.value) {
|
} else if (matchingAny.value) {
|
||||||
const res = await misskeyApi('reversi/match', {
|
const res = await misskeyApi('reversi/match', {
|
||||||
userId: null,
|
userId: null,
|
||||||
|
noIrregularRules: noIrregularRules.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
|
@ -199,10 +201,22 @@ async function matchUser() {
|
||||||
matchHeatbeat();
|
matchHeatbeat();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function matchAny() {
|
function matchAny(ev: MouseEvent) {
|
||||||
|
os.popupMenu([{
|
||||||
|
text: i18n.ts._reversi.allowIrregularRules,
|
||||||
|
action: () => {
|
||||||
|
noIrregularRules.value = false;
|
||||||
matchingAny.value = true;
|
matchingAny.value = true;
|
||||||
|
|
||||||
matchHeatbeat();
|
matchHeatbeat();
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
text: i18n.ts._reversi.disallowIrregularRules,
|
||||||
|
action: () => {
|
||||||
|
noIrregularRules.value = true;
|
||||||
|
matchingAny.value = true;
|
||||||
|
matchHeatbeat();
|
||||||
|
},
|
||||||
|
}], ev.currentTarget ?? ev.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelMatching() {
|
function cancelMatching() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* version: 2024.2.0-beta.4
|
* version: 2024.2.0-beta.6
|
||||||
* generatedAt: 2024-01-24T01:14:40.901Z
|
* generatedAt: 2024-01-24T07:32:10.455Z
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { SwitchCaseResponseType } from '../api.js';
|
import type { SwitchCaseResponseType } from '../api.js';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* version: 2024.2.0-beta.4
|
* version: 2024.2.0-beta.6
|
||||||
* generatedAt: 2024-01-24T01:14:40.899Z
|
* generatedAt: 2024-01-24T07:32:10.453Z
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* version: 2024.2.0-beta.4
|
* version: 2024.2.0-beta.6
|
||||||
* generatedAt: 2024-01-24T01:14:40.897Z
|
* generatedAt: 2024-01-24T07:32:10.452Z
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { operations } from './types.js';
|
import { operations } from './types.js';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* version: 2024.2.0-beta.4
|
* version: 2024.2.0-beta.6
|
||||||
* generatedAt: 2024-01-24T01:14:40.896Z
|
* generatedAt: 2024-01-24T07:32:10.450Z
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { components } from './types.js';
|
import { components } from './types.js';
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
/* eslint @typescript-eslint/no-explicit-any: 0 */
|
/* eslint @typescript-eslint/no-explicit-any: 0 */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* version: 2024.2.0-beta.4
|
* version: 2024.2.0-beta.6
|
||||||
* generatedAt: 2024-01-24T01:14:40.815Z
|
* generatedAt: 2024-01-24T07:32:10.370Z
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4493,6 +4493,7 @@ export type components = {
|
||||||
timeoutUserId: string | null;
|
timeoutUserId: string | null;
|
||||||
black: number | null;
|
black: number | null;
|
||||||
bw: string;
|
bw: string;
|
||||||
|
noIrregularRules: boolean;
|
||||||
isLlotheo: boolean;
|
isLlotheo: boolean;
|
||||||
canPutEverywhere: boolean;
|
canPutEverywhere: boolean;
|
||||||
loopedBoard: boolean;
|
loopedBoard: boolean;
|
||||||
|
@ -4528,6 +4529,7 @@ export type components = {
|
||||||
timeoutUserId: string | null;
|
timeoutUserId: string | null;
|
||||||
black: number | null;
|
black: number | null;
|
||||||
bw: string;
|
bw: string;
|
||||||
|
noIrregularRules: boolean;
|
||||||
isLlotheo: boolean;
|
isLlotheo: boolean;
|
||||||
canPutEverywhere: boolean;
|
canPutEverywhere: boolean;
|
||||||
loopedBoard: boolean;
|
loopedBoard: boolean;
|
||||||
|
@ -25800,6 +25802,8 @@ export type operations = {
|
||||||
/** Format: misskey:id */
|
/** Format: misskey:id */
|
||||||
userId?: string | null;
|
userId?: string | null;
|
||||||
/** @default false */
|
/** @default false */
|
||||||
|
noIrregularRules?: boolean;
|
||||||
|
/** @default false */
|
||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue