wip
This commit is contained in:
parent
2133d0552c
commit
67e6184a75
|
@ -26,6 +26,7 @@ COPY --link ["packages/sw/package.json", "./packages/sw/"]
|
||||||
COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
|
COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
|
||||||
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]
|
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]
|
||||||
COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bubble-game/"]
|
COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bubble-game/"]
|
||||||
|
COPY --link ["packages/misskey-mahjong/package.json", "./packages/misskey-mahjong/"]
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
|
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
|
||||||
pnpm i --frozen-lockfile --aggregate-output
|
pnpm i --frozen-lockfile --aggregate-output
|
||||||
|
@ -56,6 +57,7 @@ COPY --link ["packages/backend/package.json", "./packages/backend/"]
|
||||||
COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
|
COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
|
||||||
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]
|
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]
|
||||||
COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bubble-game/"]
|
COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bubble-game/"]
|
||||||
|
COPY --link ["packages/misskey-mahjong/package.json", "./packages/misskey-mahjong/"]
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
|
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
|
||||||
pnpm i --frozen-lockfile --aggregate-output
|
pnpm i --frozen-lockfile --aggregate-output
|
||||||
|
@ -85,10 +87,12 @@ COPY --chown=misskey:misskey --from=target-builder /misskey/packages/backend/nod
|
||||||
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules
|
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules
|
||||||
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-reversi/node_modules ./packages/misskey-reversi/node_modules
|
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-reversi/node_modules ./packages/misskey-reversi/node_modules
|
||||||
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-bubble-game/node_modules ./packages/misskey-bubble-game/node_modules
|
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-bubble-game/node_modules ./packages/misskey-bubble-game/node_modules
|
||||||
|
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-mahjong/node_modules ./packages/misskey-mahjong/node_modules
|
||||||
COPY --chown=misskey:misskey --from=native-builder /misskey/built ./built
|
COPY --chown=misskey:misskey --from=native-builder /misskey/built ./built
|
||||||
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-js/built ./packages/misskey-js/built
|
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-js/built ./packages/misskey-js/built
|
||||||
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-reversi/built ./packages/misskey-reversi/built
|
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-reversi/built ./packages/misskey-reversi/built
|
||||||
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-bubble-game/built ./packages/misskey-bubble-game/built
|
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-bubble-game/built ./packages/misskey-bubble-game/built
|
||||||
|
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-mahjong/built ./packages/misskey-mahjong/built
|
||||||
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/built ./packages/backend/built
|
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/built ./packages/backend/built
|
||||||
COPY --chown=misskey:misskey --from=native-builder /misskey/fluent-emojis /misskey/fluent-emojis
|
COPY --chown=misskey:misskey --from=native-builder /misskey/fluent-emojis /misskey/fluent-emojis
|
||||||
COPY --chown=misskey:misskey . ./
|
COPY --chown=misskey:misskey . ./
|
||||||
|
|
|
@ -9604,6 +9604,32 @@ export interface Locale extends ILocale {
|
||||||
*/
|
*/
|
||||||
"disallowIrregularRules": string;
|
"disallowIrregularRules": string;
|
||||||
};
|
};
|
||||||
|
"_mahjong": {
|
||||||
|
/**
|
||||||
|
* 麻雀
|
||||||
|
*/
|
||||||
|
"mahjong": string;
|
||||||
|
/**
|
||||||
|
* ルームに参加
|
||||||
|
*/
|
||||||
|
"joinRoom": string;
|
||||||
|
/**
|
||||||
|
* ルームを作成
|
||||||
|
*/
|
||||||
|
"createRoom": string;
|
||||||
|
/**
|
||||||
|
* 準備完了
|
||||||
|
*/
|
||||||
|
"ready": string;
|
||||||
|
/**
|
||||||
|
* 準備を再開
|
||||||
|
*/
|
||||||
|
"cancelReady": string;
|
||||||
|
/**
|
||||||
|
* 退室
|
||||||
|
*/
|
||||||
|
"leave": string;
|
||||||
|
};
|
||||||
"_offlineScreen": {
|
"_offlineScreen": {
|
||||||
/**
|
/**
|
||||||
* オフライン - サーバーに接続できません
|
* オフライン - サーバーに接続できません
|
||||||
|
|
|
@ -2559,6 +2559,14 @@ _reversi:
|
||||||
allowIrregularRules: "変則許可 (完全フリー)"
|
allowIrregularRules: "変則許可 (完全フリー)"
|
||||||
disallowIrregularRules: "変則なし"
|
disallowIrregularRules: "変則なし"
|
||||||
|
|
||||||
|
_mahjong:
|
||||||
|
mahjong: "麻雀"
|
||||||
|
joinRoom: "ルームに参加"
|
||||||
|
createRoom: "ルームを作成"
|
||||||
|
ready: "準備完了"
|
||||||
|
cancelReady: "準備を再開"
|
||||||
|
leave: "退室"
|
||||||
|
|
||||||
_offlineScreen:
|
_offlineScreen:
|
||||||
title: "オフライン - サーバーに接続できません"
|
title: "オフライン - サーバーに接続できません"
|
||||||
header: "サーバーに接続できません"
|
header: "サーバーに接続できません"
|
||||||
|
|
|
@ -13,7 +13,8 @@
|
||||||
"packages/sw",
|
"packages/sw",
|
||||||
"packages/misskey-js",
|
"packages/misskey-js",
|
||||||
"packages/misskey-reversi",
|
"packages/misskey-reversi",
|
||||||
"packages/misskey-bubble-game"
|
"packages/misskey-bubble-game",
|
||||||
|
"packages/misskey-mahjong"
|
||||||
],
|
],
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class Mahjong1706234054207 {
|
||||||
|
name = 'Mahjong1706234054207'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`CREATE TABLE "mahjong_game" ("id" character varying(32) NOT NULL, "startedAt" TIMESTAMP WITH TIME ZONE, "endedAt" TIMESTAMP WITH TIME ZONE, "user1Id" character varying(32), "user2Id" character varying(32), "user3Id" character varying(32), "user4Id" character varying(32), "isEnded" boolean NOT NULL DEFAULT false, "winnerId" character varying(32), "timeLimitForEachTurn" smallint NOT NULL DEFAULT '90', "logs" jsonb NOT NULL DEFAULT '[]', CONSTRAINT "PK_77db54c0a9785d387e3fbbdd2f0" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "mahjong_game" ADD CONSTRAINT "FK_b98c78761a845b69e6540401264" FOREIGN KEY ("user1Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "mahjong_game" ADD CONSTRAINT "FK_f17b0ba519ae28f188a7915ad6f" FOREIGN KEY ("user2Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "mahjong_game" ADD CONSTRAINT "FK_64314ffd3cb59475b0d06330058" FOREIGN KEY ("user3Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "mahjong_game" ADD CONSTRAINT "FK_58a75f1ea2a810ae3986f72a0e3" FOREIGN KEY ("user4Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "mahjong_game" DROP CONSTRAINT "FK_58a75f1ea2a810ae3986f72a0e3"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "mahjong_game" DROP CONSTRAINT "FK_64314ffd3cb59475b0d06330058"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "mahjong_game" DROP CONSTRAINT "FK_f17b0ba519ae28f188a7915ad6f"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "mahjong_game" DROP CONSTRAINT "FK_b98c78761a845b69e6540401264"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "mahjong_game"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -134,6 +134,7 @@
|
||||||
"mime-types": "2.1.35",
|
"mime-types": "2.1.35",
|
||||||
"misskey-js": "workspace:*",
|
"misskey-js": "workspace:*",
|
||||||
"misskey-reversi": "workspace:*",
|
"misskey-reversi": "workspace:*",
|
||||||
|
"misskey-mahjong": "workspace:*",
|
||||||
"ms": "3.0.0-canary.1",
|
"ms": "3.0.0-canary.1",
|
||||||
"nanoid": "5.0.4",
|
"nanoid": "5.0.4",
|
||||||
"nested-property": "4.0.0",
|
"nested-property": "4.0.0",
|
||||||
|
|
|
@ -67,6 +67,7 @@ import { FanoutTimelineService } from './FanoutTimelineService.js';
|
||||||
import { ChannelFollowingService } from './ChannelFollowingService.js';
|
import { ChannelFollowingService } from './ChannelFollowingService.js';
|
||||||
import { RegistryApiService } from './RegistryApiService.js';
|
import { RegistryApiService } from './RegistryApiService.js';
|
||||||
import { ReversiService } from './ReversiService.js';
|
import { ReversiService } from './ReversiService.js';
|
||||||
|
import { MahjongService } from './MahjongService.js';
|
||||||
|
|
||||||
import { ChartLoggerService } from './chart/ChartLoggerService.js';
|
import { ChartLoggerService } from './chart/ChartLoggerService.js';
|
||||||
import FederationChart from './chart/charts/federation.js';
|
import FederationChart from './chart/charts/federation.js';
|
||||||
|
@ -205,6 +206,7 @@ const $FanoutTimelineEndpointService: Provider = { provide: 'FanoutTimelineEndpo
|
||||||
const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService', useExisting: ChannelFollowingService };
|
const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService', useExisting: ChannelFollowingService };
|
||||||
const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService };
|
const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService };
|
||||||
const $ReversiService: Provider = { provide: 'ReversiService', useExisting: ReversiService };
|
const $ReversiService: Provider = { provide: 'ReversiService', useExisting: ReversiService };
|
||||||
|
const $MahjongService: Provider = { provide: 'MahjongService', useExisting: MahjongService };
|
||||||
|
|
||||||
const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService };
|
const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService };
|
||||||
const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart };
|
const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart };
|
||||||
|
@ -344,6 +346,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
ChannelFollowingService,
|
ChannelFollowingService,
|
||||||
RegistryApiService,
|
RegistryApiService,
|
||||||
ReversiService,
|
ReversiService,
|
||||||
|
MahjongService,
|
||||||
|
|
||||||
ChartLoggerService,
|
ChartLoggerService,
|
||||||
FederationChart,
|
FederationChart,
|
||||||
|
@ -479,6 +482,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
$ChannelFollowingService,
|
$ChannelFollowingService,
|
||||||
$RegistryApiService,
|
$RegistryApiService,
|
||||||
$ReversiService,
|
$ReversiService,
|
||||||
|
$MahjongService,
|
||||||
|
|
||||||
$ChartLoggerService,
|
$ChartLoggerService,
|
||||||
$FederationChart,
|
$FederationChart,
|
||||||
|
@ -615,6 +619,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
ChannelFollowingService,
|
ChannelFollowingService,
|
||||||
RegistryApiService,
|
RegistryApiService,
|
||||||
ReversiService,
|
ReversiService,
|
||||||
|
MahjongService,
|
||||||
|
|
||||||
FederationChart,
|
FederationChart,
|
||||||
NotesChart,
|
NotesChart,
|
||||||
|
@ -749,6 +754,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
$ChannelFollowingService,
|
$ChannelFollowingService,
|
||||||
$RegistryApiService,
|
$RegistryApiService,
|
||||||
$ReversiService,
|
$ReversiService,
|
||||||
|
$MahjongService,
|
||||||
|
|
||||||
$FederationChart,
|
$FederationChart,
|
||||||
$NotesChart,
|
$NotesChart,
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import * as Reversi from 'misskey-reversi';
|
import * as Reversi from 'misskey-reversi';
|
||||||
|
import * as Mahjong from 'misskey-mahjong';
|
||||||
import type { MiChannel } from '@/models/Channel.js';
|
import type { MiChannel } from '@/models/Channel.js';
|
||||||
import type { MiUser } from '@/models/User.js';
|
import type { MiUser } from '@/models/User.js';
|
||||||
import type { MiUserProfile } from '@/models/UserProfile.js';
|
import type { MiUserProfile } from '@/models/UserProfile.js';
|
||||||
|
@ -192,6 +193,28 @@ export interface ReversiGameEventTypes {
|
||||||
userId: MiUser['id'];
|
userId: MiUser['id'];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MahjongRoomEventTypes {
|
||||||
|
changeReadyStates: {
|
||||||
|
user1: boolean;
|
||||||
|
user2: boolean;
|
||||||
|
user3: boolean;
|
||||||
|
user4: boolean;
|
||||||
|
};
|
||||||
|
tsumo: {
|
||||||
|
house: Mahjong.Engine.House;
|
||||||
|
tile: Mahjong.Engine.Tile;
|
||||||
|
};
|
||||||
|
dahai: {
|
||||||
|
house: Mahjong.Engine.House;
|
||||||
|
tile: Mahjong.Engine.Tile;
|
||||||
|
};
|
||||||
|
dahaiAndTsumo: {
|
||||||
|
house: Mahjong.Engine.House;
|
||||||
|
dahaiTile: Mahjong.Engine.Tile;
|
||||||
|
tsumoTile: Mahjong.Engine.Tile;
|
||||||
|
};
|
||||||
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
// 辞書(interface or type)から{ type, body }ユニオンを定義
|
// 辞書(interface or type)から{ type, body }ユニオンを定義
|
||||||
|
@ -290,6 +313,10 @@ export type GlobalEvents = {
|
||||||
name: `reversiGameStream:${MiReversiGame['id']}`;
|
name: `reversiGameStream:${MiReversiGame['id']}`;
|
||||||
payload: EventUnionFromDictionary<SerializedAll<ReversiGameEventTypes>>;
|
payload: EventUnionFromDictionary<SerializedAll<ReversiGameEventTypes>>;
|
||||||
};
|
};
|
||||||
|
mahjongRoom: {
|
||||||
|
name: `mahjongRoomStream:${string}`;
|
||||||
|
payload: EventUnionFromDictionary<SerializedAll<MahjongRoomEventTypes>>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// API event definitions
|
// API event definitions
|
||||||
|
@ -389,4 +416,9 @@ export class GlobalEventService {
|
||||||
public publishReversiGameStream<K extends keyof ReversiGameEventTypes>(gameId: MiReversiGame['id'], type: K, value?: ReversiGameEventTypes[K]): void {
|
public publishReversiGameStream<K extends keyof ReversiGameEventTypes>(gameId: MiReversiGame['id'], type: K, value?: ReversiGameEventTypes[K]): void {
|
||||||
this.publish(`reversiGameStream:${gameId}`, type, typeof value === 'undefined' ? null : value);
|
this.publish(`reversiGameStream:${gameId}`, type, typeof value === 'undefined' ? null : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public publishMahjongRoomStream<K extends keyof MahjongRoomEventTypes>(roomId: string, type: K, value?: MahjongRoomEventTypes[K]): void {
|
||||||
|
this.publish(`mahjongRoomStream:${roomId}`, type, typeof value === 'undefined' ? null : value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,350 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import * as Redis from 'ioredis';
|
||||||
|
import { ModuleRef } from '@nestjs/core';
|
||||||
|
import { IsNull, LessThan, MoreThan } from 'typeorm';
|
||||||
|
import * as Mahjong from 'misskey-mahjong';
|
||||||
|
import type {
|
||||||
|
MiMahjongGame,
|
||||||
|
MahjongGamesRepository,
|
||||||
|
} from '@/models/_.js';
|
||||||
|
import type { MiUser } from '@/models/User.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { CacheService } from '@/core/CacheService.js';
|
||||||
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
import { NotificationService } from '@/core/NotificationService.js';
|
||||||
|
import { Serialized } from '@/types.js';
|
||||||
|
import { Packed } from '@/misc/json-schema.js';
|
||||||
|
import { ReversiGameEntityService } from './entities/ReversiGameEntityService.js';
|
||||||
|
import type { OnApplicationShutdown, OnModuleInit } from '@nestjs/common';
|
||||||
|
|
||||||
|
const INVITATION_TIMEOUT_MS = 1000 * 20; // 20sec
|
||||||
|
const PON_TIMEOUT_MS = 1000 * 10; // 10sec
|
||||||
|
const DAHAI_TIMEOUT_MS = 1000 * 30; // 30sec
|
||||||
|
|
||||||
|
type Room = {
|
||||||
|
id: string;
|
||||||
|
user1Id: MiUser['id'];
|
||||||
|
user2Id: MiUser['id'] | null;
|
||||||
|
user3Id: MiUser['id'] | null;
|
||||||
|
user4Id: MiUser['id'] | null;
|
||||||
|
user1: Packed<'UserLite'> | null;
|
||||||
|
user2: Packed<'UserLite'> | null;
|
||||||
|
user3: Packed<'UserLite'> | null;
|
||||||
|
user4: Packed<'UserLite'> | null;
|
||||||
|
user1Ai?: boolean;
|
||||||
|
user2Ai?: boolean;
|
||||||
|
user3Ai?: boolean;
|
||||||
|
user4Ai?: boolean;
|
||||||
|
user1Ready: boolean;
|
||||||
|
user2Ready: boolean;
|
||||||
|
user3Ready: boolean;
|
||||||
|
user4Ready: boolean;
|
||||||
|
user1Offline?: boolean;
|
||||||
|
user2Offline?: boolean;
|
||||||
|
user3Offline?: boolean;
|
||||||
|
user4Offline?: boolean;
|
||||||
|
isStarted?: boolean;
|
||||||
|
|
||||||
|
gameState?: Mahjong.Engine.MasterState;
|
||||||
|
};
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MahjongService implements OnApplicationShutdown, OnModuleInit {
|
||||||
|
private notificationService: NotificationService;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private moduleRef: ModuleRef,
|
||||||
|
|
||||||
|
@Inject(DI.redis)
|
||||||
|
private redisClient: Redis.Redis,
|
||||||
|
|
||||||
|
//@Inject(DI.mahjongGamesRepository)
|
||||||
|
//private mahjongGamesRepository: MahjongGamesRepository,
|
||||||
|
|
||||||
|
private cacheService: CacheService,
|
||||||
|
private userEntityService: UserEntityService,
|
||||||
|
private globalEventService: GlobalEventService,
|
||||||
|
private reversiGameEntityService: ReversiGameEntityService,
|
||||||
|
private idService: IdService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
async onModuleInit() {
|
||||||
|
this.notificationService = this.moduleRef.get(NotificationService.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async saveRoom(room: Room) {
|
||||||
|
await this.redisClient.set(`mahjong:room:${room.id}`, JSON.stringify(room), 'EX', 60 * 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async createRoom(user: MiUser): Promise<Room> {
|
||||||
|
const room: Room = {
|
||||||
|
id: this.idService.gen(),
|
||||||
|
user1Id: user.id,
|
||||||
|
user2Id: null,
|
||||||
|
user3Id: null,
|
||||||
|
user4Id: null,
|
||||||
|
user1: await this.userEntityService.pack(user),
|
||||||
|
user1Ready: false,
|
||||||
|
user2Ready: false,
|
||||||
|
user3Ready: false,
|
||||||
|
user4Ready: false,
|
||||||
|
};
|
||||||
|
await this.saveRoom(room);
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async getRoom(id: Room['id']): Promise<Room | null> {
|
||||||
|
const room = await this.redisClient.get(`mahjong:room:${id}`);
|
||||||
|
if (!room) return null;
|
||||||
|
const parsed = JSON.parse(room);
|
||||||
|
return {
|
||||||
|
...parsed,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async joinRoom(roomId: Room['id'], user: MiUser): Promise<Room | null> {
|
||||||
|
const room = await this.getRoom(roomId);
|
||||||
|
if (!room) return null;
|
||||||
|
if (room.user1Id === user.id) return room;
|
||||||
|
if (room.user2Id === user.id) return room;
|
||||||
|
if (room.user3Id === user.id) return room;
|
||||||
|
if (room.user4Id === user.id) return room;
|
||||||
|
if (room.user2Id === null) {
|
||||||
|
room.user2Id = user.id;
|
||||||
|
room.user2 = await this.userEntityService.pack(user);
|
||||||
|
await this.saveRoom(room);
|
||||||
|
this.globalEventService.publishMahjongRoomStream(room.id, 'joined', { index: 2, user: room.user2 });
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
if (room.user3Id === null) {
|
||||||
|
room.user3Id = user.id;
|
||||||
|
room.user3 = await this.userEntityService.pack(user);
|
||||||
|
await this.saveRoom(room);
|
||||||
|
this.globalEventService.publishMahjongRoomStream(room.id, 'joined', { index: 3, user: room.user3 });
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
if (room.user4Id === null) {
|
||||||
|
room.user4Id = user.id;
|
||||||
|
room.user4 = await this.userEntityService.pack(user);
|
||||||
|
await this.saveRoom(room);
|
||||||
|
this.globalEventService.publishMahjongRoomStream(room.id, 'joined', { index: 4, user: room.user4 });
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async addAi(roomId: Room['id'], user: MiUser): Promise<Room | null> {
|
||||||
|
const room = await this.getRoom(roomId);
|
||||||
|
if (!room) return null;
|
||||||
|
if (room.user1Id !== user.id) throw new Error('access denied');
|
||||||
|
|
||||||
|
if (room.user2Id == null) {
|
||||||
|
room.user2Ai = true;
|
||||||
|
room.user2Ready = true;
|
||||||
|
await this.saveRoom(room);
|
||||||
|
this.globalEventService.publishMahjongRoomStream(room.id, 'joined', { index: 2, user: null });
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
if (room.user3Id == null) {
|
||||||
|
room.user3Ai = true;
|
||||||
|
room.user3Ready = true;
|
||||||
|
await this.saveRoom(room);
|
||||||
|
this.globalEventService.publishMahjongRoomStream(room.id, 'joined', { index: 3, user: null });
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
if (room.user4Id == null) {
|
||||||
|
room.user4Ai = true;
|
||||||
|
room.user4Ready = true;
|
||||||
|
await this.saveRoom(room);
|
||||||
|
this.globalEventService.publishMahjongRoomStream(room.id, 'joined', { index: 4, user: null });
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async leaveRoom(roomId: Room['id'], user: MiUser): Promise<Room | null> {
|
||||||
|
const room = await this.getRoom(roomId);
|
||||||
|
if (!room) return null;
|
||||||
|
if (room.user1Id === user.id) {
|
||||||
|
room.user1Id = null;
|
||||||
|
room.user1 = null;
|
||||||
|
await this.saveRoom(room);
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
if (room.user2Id === user.id) {
|
||||||
|
room.user2Id = null;
|
||||||
|
room.user2 = null;
|
||||||
|
await this.saveRoom(room);
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
if (room.user3Id === user.id) {
|
||||||
|
room.user3Id = null;
|
||||||
|
room.user3 = null;
|
||||||
|
await this.saveRoom(room);
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
if (room.user4Id === user.id) {
|
||||||
|
room.user4Id = null;
|
||||||
|
room.user4 = null;
|
||||||
|
await this.saveRoom(room);
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async changeReadyState(roomId: Room['id'], user: MiUser, ready: boolean): Promise<void> {
|
||||||
|
const room = await this.getRoom(roomId);
|
||||||
|
if (!room) return;
|
||||||
|
|
||||||
|
if (room.user1Id === user.id) {
|
||||||
|
room.user1Ready = ready;
|
||||||
|
await this.saveRoom(room);
|
||||||
|
}
|
||||||
|
if (room.user2Id === user.id) {
|
||||||
|
room.user2Ready = ready;
|
||||||
|
await this.saveRoom(room);
|
||||||
|
}
|
||||||
|
if (room.user3Id === user.id) {
|
||||||
|
room.user3Ready = ready;
|
||||||
|
await this.saveRoom(room);
|
||||||
|
}
|
||||||
|
if (room.user4Id === user.id) {
|
||||||
|
room.user4Ready = ready;
|
||||||
|
await this.saveRoom(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.globalEventService.publishMahjongRoomStream(room.id, 'changeReadyStates', {
|
||||||
|
user1: room.user1Ready,
|
||||||
|
user2: room.user2Ready,
|
||||||
|
user3: room.user3Ready,
|
||||||
|
user4: room.user4Ready,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (room.user1Ready && room.user2Ready && room.user3Ready && room.user4Ready) {
|
||||||
|
await this.startGame(room);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async startGame(room: Room) {
|
||||||
|
if (!room.user1Ready || !room.user2Ready || !room.user3Ready || !room.user4Ready) {
|
||||||
|
throw new Error('Not ready');
|
||||||
|
}
|
||||||
|
|
||||||
|
room.gameState = Mahjong.Engine.MasterGameEngine.createInitialState();
|
||||||
|
room.isStarted = true;
|
||||||
|
|
||||||
|
await this.saveRoom(room);
|
||||||
|
|
||||||
|
const packed = await this.packRoom(room);
|
||||||
|
this.globalEventService.publishMahjongRoomStream(room.id, 'started', { room: packed });
|
||||||
|
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async packRoom(room: Room, me: MiUser) {
|
||||||
|
return {
|
||||||
|
...room,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async dahai(room: Room, engine: Mahjong.Engine.MasterGameEngine, user: MiUser, tile: Mahjong.Engine.Tile) {
|
||||||
|
const myHouse = user.id === room.user1Id ? engine.state.user1House : user.id === room.user2Id ? engine.state.user2House : user.id === room.user3Id ? engine.state.user3House : engine.state.user4House;
|
||||||
|
const res = engine.op_dahai(myHouse, tile);
|
||||||
|
if (res.canPonHouse) {
|
||||||
|
// TODO: 家がCPUだった場合の処理
|
||||||
|
this.redisClient.set(`mahjong:gamePonAsking:${room.id}`, '');
|
||||||
|
const waitingStartedAt = Date.now();
|
||||||
|
const interval = setInterval(async () => {
|
||||||
|
const waiting = await this.redisClient.get(`mahjong:gamePonAsking:${room.id}`);
|
||||||
|
if (waiting == null) {
|
||||||
|
clearInterval(interval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Date.now() - waitingStartedAt > PON_TIMEOUT_MS) {
|
||||||
|
await this.redisClient.del(`mahjong:gamePonAsking:${room.id}`);
|
||||||
|
clearInterval(interval);
|
||||||
|
const res = engine.op_noOnePon();
|
||||||
|
this.globalEventService.publishMahjongRoomStream(room.id, 'tsumo', { house: res.house, tile: res.tile });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
this.globalEventService.publishMahjongRoomStream(room.id, 'dahai', { house: myHouse, tile });
|
||||||
|
} else {
|
||||||
|
this.globalEventService.publishMahjongRoomStream(room.id, 'dahaiAndTsumo', { house: myHouse, dahaiTile: tile, tsumoTile: res.tsumoTile });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async op_dahai(roomId: MiMahjongGame['id'], user: MiUser, tile: string) {
|
||||||
|
const room = await this.getRoom(roomId);
|
||||||
|
if (room == null) return;
|
||||||
|
if (room.gameState == null) return;
|
||||||
|
|
||||||
|
await this.redisClient.del(`mahjong:gameDahaiWaiting:${room.id}`);
|
||||||
|
|
||||||
|
const engine = new Mahjong.Engine.MasterGameEngine(room.gameState);
|
||||||
|
await this.dahai(room, engine, user, tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async op_pon(roomId: MiMahjongGame['id'], user: MiUser) {
|
||||||
|
const room = await this.getRoom(roomId);
|
||||||
|
if (room == null) return;
|
||||||
|
if (room.gameState == null) return;
|
||||||
|
|
||||||
|
const engine = new Mahjong.Engine.MasterGameEngine(room.gameState);
|
||||||
|
const myHouse = user.id === room.user1Id ? engine.state.user1House : user.id === room.user2Id ? engine.state.user2House : user.id === room.user3Id ? engine.state.user3House : engine.state.user4House;
|
||||||
|
const res = engine.op_pon(myHouse);
|
||||||
|
this.waitForDahai(room, user, engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async waitForDahai(game: Room, user: MiUser, engine: Mahjong.Engine.MasterGameEngine) {
|
||||||
|
this.redisClient.set(`mahjong:gameDahaiWaiting:${game.id}`, '');
|
||||||
|
const waitingStartedAt = Date.now();
|
||||||
|
const interval = setInterval(async () => {
|
||||||
|
const waiting = await this.redisClient.get(`mahjong:gameDahaiWaiting:${game.id}`);
|
||||||
|
if (waiting == null) {
|
||||||
|
clearInterval(interval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Date.now() - waitingStartedAt > DAHAI_TIMEOUT_MS) {
|
||||||
|
await this.redisClient.del(`mahjong:gameDahaiWaiting:${game.id}`);
|
||||||
|
clearInterval(interval);
|
||||||
|
const house = game.user1Id === user.id ? engine.state.user1House : game.user2Id === user.id ? engine.state.user2House : game.user3Id === user.id ? engine.state.user3House : engine.state.user4House;
|
||||||
|
const handTiles = house === 'e' ? engine.state.eHandTiles : house === 's' ? engine.state.sHandTiles : house === 'w' ? engine.state.wHandTiles : engine.state.nHandTiles;
|
||||||
|
await this.dahai(game, engine, user, handTiles.at(-1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public dispose(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public onApplicationShutdown(signal?: string | undefined): void {
|
||||||
|
this.dispose();
|
||||||
|
}
|
||||||
|
}
|
|
@ -80,5 +80,6 @@ export const DI = {
|
||||||
userMemosRepository: Symbol('userMemosRepository'),
|
userMemosRepository: Symbol('userMemosRepository'),
|
||||||
bubbleGameRecordsRepository: Symbol('bubbleGameRecordsRepository'),
|
bubbleGameRecordsRepository: Symbol('bubbleGameRecordsRepository'),
|
||||||
reversiGamesRepository: Symbol('reversiGamesRepository'),
|
reversiGamesRepository: Symbol('reversiGamesRepository'),
|
||||||
|
mahjongGamesRepository: Symbol('mahjongGamesRepository'),
|
||||||
//#endregion
|
//#endregion
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,6 +40,7 @@ import { packedSigninSchema } from '@/models/json-schema/signin.js';
|
||||||
import { packedRoleLiteSchema, packedRoleSchema } from '@/models/json-schema/role.js';
|
import { packedRoleLiteSchema, packedRoleSchema } from '@/models/json-schema/role.js';
|
||||||
import { packedAdSchema } from '@/models/json-schema/ad.js';
|
import { packedAdSchema } from '@/models/json-schema/ad.js';
|
||||||
import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js';
|
import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js';
|
||||||
|
import { packedMahjongRoomDetailedSchema } from '@/models/json-schema/mahjong-room.js';
|
||||||
|
|
||||||
export const refs = {
|
export const refs = {
|
||||||
UserLite: packedUserLiteSchema,
|
UserLite: packedUserLiteSchema,
|
||||||
|
@ -81,6 +82,7 @@ export const refs = {
|
||||||
Role: packedRoleSchema,
|
Role: packedRoleSchema,
|
||||||
ReversiGameLite: packedReversiGameLiteSchema,
|
ReversiGameLite: packedReversiGameLiteSchema,
|
||||||
ReversiGameDetailed: packedReversiGameDetailedSchema,
|
ReversiGameDetailed: packedReversiGameDetailedSchema,
|
||||||
|
MahjongRoomDetailed: packedMahjongRoomDetailedSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>;
|
export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>;
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
|
||||||
|
import { id } from './util/id.js';
|
||||||
|
import { MiUser } from './User.js';
|
||||||
|
|
||||||
|
@Entity('mahjong_game')
|
||||||
|
export class MiMahjongGame {
|
||||||
|
@PrimaryColumn(id())
|
||||||
|
public id: string;
|
||||||
|
|
||||||
|
@Column('timestamp with time zone', {
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public startedAt: Date | null;
|
||||||
|
|
||||||
|
@Column('timestamp with time zone', {
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public endedAt: Date | null;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public user1Id: MiUser['id'] | null;
|
||||||
|
|
||||||
|
@ManyToOne(type => MiUser, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public user1: MiUser | null;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public user2Id: MiUser['id'] | null;
|
||||||
|
|
||||||
|
@ManyToOne(type => MiUser, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public user2: MiUser | null;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public user3Id: MiUser['id'] | null;
|
||||||
|
|
||||||
|
@ManyToOne(type => MiUser, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public user3: MiUser | null;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public user4Id: MiUser['id'] | null;
|
||||||
|
|
||||||
|
@ManyToOne(type => MiUser, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public user4: MiUser | null;
|
||||||
|
|
||||||
|
@Column('boolean', {
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
|
public isEnded: boolean;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public winnerId: MiUser['id'] | null;
|
||||||
|
|
||||||
|
// in sec
|
||||||
|
@Column('smallint', {
|
||||||
|
default: 90,
|
||||||
|
})
|
||||||
|
public timeLimitForEachTurn: number;
|
||||||
|
|
||||||
|
@Column('jsonb', {
|
||||||
|
default: [],
|
||||||
|
})
|
||||||
|
public logs: number[][];
|
||||||
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook, MiBubbleGameRecord, MiReversiGame } from './_.js';
|
import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook, MiBubbleGameRecord, MiReversiGame, MiMahjongGame } from './_.js';
|
||||||
import type { DataSource } from 'typeorm';
|
import type { DataSource } from 'typeorm';
|
||||||
import type { Provider } from '@nestjs/common';
|
import type { Provider } from '@nestjs/common';
|
||||||
|
|
||||||
|
@ -411,6 +411,12 @@ const $reversiGamesRepository: Provider = {
|
||||||
inject: [DI.db],
|
inject: [DI.db],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const $mahjongGamesRepository: Provider = {
|
||||||
|
provide: DI.mahjongGamesRepository,
|
||||||
|
useFactory: (db: DataSource) => db.getRepository(MiMahjongGame),
|
||||||
|
inject: [DI.db],
|
||||||
|
};
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
],
|
],
|
||||||
|
@ -482,6 +488,7 @@ const $reversiGamesRepository: Provider = {
|
||||||
$userMemosRepository,
|
$userMemosRepository,
|
||||||
$bubbleGameRecordsRepository,
|
$bubbleGameRecordsRepository,
|
||||||
$reversiGamesRepository,
|
$reversiGamesRepository,
|
||||||
|
$mahjongGamesRepository,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
$usersRepository,
|
$usersRepository,
|
||||||
|
@ -551,6 +558,7 @@ const $reversiGamesRepository: Provider = {
|
||||||
$userMemosRepository,
|
$userMemosRepository,
|
||||||
$bubbleGameRecordsRepository,
|
$bubbleGameRecordsRepository,
|
||||||
$reversiGamesRepository,
|
$reversiGamesRepository,
|
||||||
|
$mahjongGamesRepository,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class RepositoryModule {}
|
export class RepositoryModule {}
|
||||||
|
|
|
@ -70,6 +70,7 @@ import { MiFlashLike } from '@/models/FlashLike.js';
|
||||||
import { MiUserListFavorite } from '@/models/UserListFavorite.js';
|
import { MiUserListFavorite } from '@/models/UserListFavorite.js';
|
||||||
import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js';
|
import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js';
|
||||||
import { MiReversiGame } from '@/models/ReversiGame.js';
|
import { MiReversiGame } from '@/models/ReversiGame.js';
|
||||||
|
import { MiMahjongGame } from '@/models/MahjongGame.js';
|
||||||
|
|
||||||
import type { Repository } from 'typeorm';
|
import type { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
@ -141,6 +142,7 @@ export {
|
||||||
MiUserMemo,
|
MiUserMemo,
|
||||||
MiBubbleGameRecord,
|
MiBubbleGameRecord,
|
||||||
MiReversiGame,
|
MiReversiGame,
|
||||||
|
MiMahjongGame,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AbuseUserReportsRepository = Repository<MiAbuseUserReport>;
|
export type AbuseUserReportsRepository = Repository<MiAbuseUserReport>;
|
||||||
|
@ -210,3 +212,4 @@ export type FlashLikesRepository = Repository<MiFlashLike>;
|
||||||
export type UserMemoRepository = Repository<MiUserMemo>;
|
export type UserMemoRepository = Repository<MiUserMemo>;
|
||||||
export type BubbleGameRecordsRepository = Repository<MiBubbleGameRecord>;
|
export type BubbleGameRecordsRepository = Repository<MiBubbleGameRecord>;
|
||||||
export type ReversiGamesRepository = Repository<MiReversiGame>;
|
export type ReversiGamesRepository = Repository<MiReversiGame>;
|
||||||
|
export type MahjongGamesRepository = Repository<MiMahjongGame>;
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const packedMahjongRoomDetailedSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
startedAt: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
endedAt: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
isStarted: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
isEnded: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
user1Id: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: null,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
user2Id: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: null,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
user3Id: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: null,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
user4Id: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: null,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
user1: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: null,
|
||||||
|
ref: 'User',
|
||||||
|
},
|
||||||
|
user2: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: null,
|
||||||
|
ref: 'User',
|
||||||
|
},
|
||||||
|
user3: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: null,
|
||||||
|
ref: 'User',
|
||||||
|
},
|
||||||
|
user4: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: null,
|
||||||
|
ref: 'User',
|
||||||
|
},
|
||||||
|
user1Ai: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
user2Ai: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
user3Ai: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
user4Ai: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
user1Ready: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
user2Ready: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
user3Ready: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
user4Ready: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
|
@ -78,6 +78,7 @@ import { MiFlashLike } from '@/models/FlashLike.js';
|
||||||
import { MiUserMemo } from '@/models/UserMemo.js';
|
import { MiUserMemo } from '@/models/UserMemo.js';
|
||||||
import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js';
|
import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js';
|
||||||
import { MiReversiGame } from '@/models/ReversiGame.js';
|
import { MiReversiGame } from '@/models/ReversiGame.js';
|
||||||
|
import { MiMahjongGame } from '@/models/MahjongGame.js';
|
||||||
|
|
||||||
import { Config } from '@/config.js';
|
import { Config } from '@/config.js';
|
||||||
import MisskeyLogger from '@/logger.js';
|
import MisskeyLogger from '@/logger.js';
|
||||||
|
@ -194,6 +195,7 @@ export const entities = [
|
||||||
MiUserMemo,
|
MiUserMemo,
|
||||||
MiBubbleGameRecord,
|
MiBubbleGameRecord,
|
||||||
MiReversiGame,
|
MiReversiGame,
|
||||||
|
MiMahjongGame,
|
||||||
...charts,
|
...charts,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ import { UserListChannelService } from './api/stream/channels/user-list.js';
|
||||||
import { RoleTimelineChannelService } from './api/stream/channels/role-timeline.js';
|
import { RoleTimelineChannelService } from './api/stream/channels/role-timeline.js';
|
||||||
import { ReversiChannelService } from './api/stream/channels/reversi.js';
|
import { ReversiChannelService } from './api/stream/channels/reversi.js';
|
||||||
import { ReversiGameChannelService } from './api/stream/channels/reversi-game.js';
|
import { ReversiGameChannelService } from './api/stream/channels/reversi-game.js';
|
||||||
|
import { MahjongRoomChannelService } from './api/stream/channels/mahjong-room.js';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -82,6 +83,7 @@ import { ReversiGameChannelService } from './api/stream/channels/reversi-game.js
|
||||||
RoleTimelineChannelService,
|
RoleTimelineChannelService,
|
||||||
ReversiChannelService,
|
ReversiChannelService,
|
||||||
ReversiGameChannelService,
|
ReversiGameChannelService,
|
||||||
|
MahjongRoomChannelService,
|
||||||
HomeTimelineChannelService,
|
HomeTimelineChannelService,
|
||||||
HybridTimelineChannelService,
|
HybridTimelineChannelService,
|
||||||
LocalTimelineChannelService,
|
LocalTimelineChannelService,
|
||||||
|
|
|
@ -373,6 +373,9 @@ import * as ep___reversi_invitations from './endpoints/reversi/invitations.js';
|
||||||
import * as ep___reversi_showGame from './endpoints/reversi/show-game.js';
|
import * as ep___reversi_showGame from './endpoints/reversi/show-game.js';
|
||||||
import * as ep___reversi_surrender from './endpoints/reversi/surrender.js';
|
import * as ep___reversi_surrender from './endpoints/reversi/surrender.js';
|
||||||
import * as ep___reversi_verify from './endpoints/reversi/verify.js';
|
import * as ep___reversi_verify from './endpoints/reversi/verify.js';
|
||||||
|
import * as ep___mahjong_createRoom from './endpoints/mahjong/create-room.js';
|
||||||
|
import * as ep___mahjong_joinRoom from './endpoints/mahjong/join-room.js';
|
||||||
|
import * as ep___mahjong_showRoom from './endpoints/mahjong/show-room.js';
|
||||||
import { GetterService } from './GetterService.js';
|
import { GetterService } from './GetterService.js';
|
||||||
import { ApiLoggerService } from './ApiLoggerService.js';
|
import { ApiLoggerService } from './ApiLoggerService.js';
|
||||||
import type { Provider } from '@nestjs/common';
|
import type { Provider } from '@nestjs/common';
|
||||||
|
@ -744,6 +747,9 @@ const $reversi_invitations: Provider = { provide: 'ep:reversi/invitations', useC
|
||||||
const $reversi_showGame: Provider = { provide: 'ep:reversi/show-game', useClass: ep___reversi_showGame.default };
|
const $reversi_showGame: Provider = { provide: 'ep:reversi/show-game', useClass: ep___reversi_showGame.default };
|
||||||
const $reversi_surrender: Provider = { provide: 'ep:reversi/surrender', useClass: ep___reversi_surrender.default };
|
const $reversi_surrender: Provider = { provide: 'ep:reversi/surrender', useClass: ep___reversi_surrender.default };
|
||||||
const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep___reversi_verify.default };
|
const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep___reversi_verify.default };
|
||||||
|
const $mahjong_createRoom: Provider = { provide: 'ep:mahjong/create-room', useClass: ep___mahjong_createRoom.default };
|
||||||
|
const $mahjong_joinRoom: Provider = { provide: 'ep:mahjong/join-room', useClass: ep___mahjong_joinRoom.default };
|
||||||
|
const $mahjong_showRoom: Provider = { provide: 'ep:mahjong/show-room', useClass: ep___mahjong_showRoom.default };
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -1119,6 +1125,9 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
|
||||||
$reversi_showGame,
|
$reversi_showGame,
|
||||||
$reversi_surrender,
|
$reversi_surrender,
|
||||||
$reversi_verify,
|
$reversi_verify,
|
||||||
|
$mahjong_createRoom,
|
||||||
|
$mahjong_joinRoom,
|
||||||
|
$mahjong_showRoom,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
$admin_meta,
|
$admin_meta,
|
||||||
|
@ -1485,6 +1494,9 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
|
||||||
$reversi_showGame,
|
$reversi_showGame,
|
||||||
$reversi_surrender,
|
$reversi_surrender,
|
||||||
$reversi_verify,
|
$reversi_verify,
|
||||||
|
$mahjong_createRoom,
|
||||||
|
$mahjong_joinRoom,
|
||||||
|
$mahjong_showRoom,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class EndpointsModule {}
|
export class EndpointsModule {}
|
||||||
|
|
|
@ -374,6 +374,9 @@ import * as ep___reversi_invitations from './endpoints/reversi/invitations.js';
|
||||||
import * as ep___reversi_showGame from './endpoints/reversi/show-game.js';
|
import * as ep___reversi_showGame from './endpoints/reversi/show-game.js';
|
||||||
import * as ep___reversi_surrender from './endpoints/reversi/surrender.js';
|
import * as ep___reversi_surrender from './endpoints/reversi/surrender.js';
|
||||||
import * as ep___reversi_verify from './endpoints/reversi/verify.js';
|
import * as ep___reversi_verify from './endpoints/reversi/verify.js';
|
||||||
|
import * as ep___mahjong_createRoom from './endpoints/mahjong/create-room.js';
|
||||||
|
import * as ep___mahjong_joinRoom from './endpoints/mahjong/join-room.js';
|
||||||
|
import * as ep___mahjong_showRoom from './endpoints/mahjong/show-room.js';
|
||||||
|
|
||||||
const eps = [
|
const eps = [
|
||||||
['admin/meta', ep___admin_meta],
|
['admin/meta', ep___admin_meta],
|
||||||
|
@ -743,6 +746,9 @@ const eps = [
|
||||||
['reversi/show-game', ep___reversi_showGame],
|
['reversi/show-game', ep___reversi_showGame],
|
||||||
['reversi/surrender', ep___reversi_surrender],
|
['reversi/surrender', ep___reversi_surrender],
|
||||||
['reversi/verify', ep___reversi_verify],
|
['reversi/verify', ep___reversi_verify],
|
||||||
|
['mahjong/create-room', ep___mahjong_createRoom],
|
||||||
|
['mahjong/join-room', ep___mahjong_joinRoom],
|
||||||
|
['mahjong/show-room', ep___mahjong_showRoom],
|
||||||
];
|
];
|
||||||
|
|
||||||
interface IEndpointMetaBase {
|
interface IEndpointMetaBase {
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { ReversiService } from '@/core/ReversiService.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'write:account',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
userId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
|
},
|
||||||
|
required: [],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
private reversiService: ReversiService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
if (ps.userId) {
|
||||||
|
await this.reversiService.matchSpecificUserCancel(me, ps.userId);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
await this.reversiService.matchAnyUserCancel(me);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { MahjongService } from '@/core/MahjongService.js';
|
||||||
|
import { ApiError } from '../../error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'write:account',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'MahjongRoomDetailed',
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
},
|
||||||
|
required: [],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
private mahjongService: MahjongService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const room = await this.mahjongService.createRoom(me);
|
||||||
|
return await this.mahjongService.packRoom(room, me);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Brackets } from 'typeorm';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import type { ReversiGamesRepository } from '@/models/_.js';
|
||||||
|
import { QueryService } from '@/core/QueryService.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
requireCredential: false,
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: { ref: 'ReversiGameLite' },
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
|
my: { type: 'boolean', default: false },
|
||||||
|
},
|
||||||
|
required: [],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.reversiGamesRepository)
|
||||||
|
private reversiGamesRepository: ReversiGamesRepository,
|
||||||
|
|
||||||
|
private reversiGameEntityService: ReversiGameEntityService,
|
||||||
|
private queryService: QueryService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const query = this.queryService.makePaginationQuery(this.reversiGamesRepository.createQueryBuilder('game'), ps.sinceId, ps.untilId)
|
||||||
|
.innerJoinAndSelect('game.user1', 'user1')
|
||||||
|
.innerJoinAndSelect('game.user2', 'user2');
|
||||||
|
|
||||||
|
if (ps.my && me) {
|
||||||
|
query.andWhere(new Brackets(qb => {
|
||||||
|
qb
|
||||||
|
.where('game.user1Id = :userId', { userId: me.id })
|
||||||
|
.orWhere('game.user2Id = :userId', { userId: me.id });
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
query.andWhere('game.isStarted = TRUE');
|
||||||
|
}
|
||||||
|
|
||||||
|
const games = await query.take(ps.limit).getMany();
|
||||||
|
|
||||||
|
return await this.reversiGameEntityService.packLiteMany(games);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { MahjongService } from '@/core/MahjongService.js';
|
||||||
|
import { ApiError } from '../../error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'write:account',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchRoom: {
|
||||||
|
message: 'No such room.',
|
||||||
|
code: 'NO_SUCH_ROOM',
|
||||||
|
id: '370e42b0-2a67-4306-9328-51c5f568f110',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'MahjongRoomDetailed',
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
roomId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['roomId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
private mahjongService: MahjongService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const room = await this.mahjongService.getRoom(ps.roomId);
|
||||||
|
|
||||||
|
if (room == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchRoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.mahjongService.joinRoom(room.id, me);
|
||||||
|
|
||||||
|
return await this.mahjongService.packRoom(room, me);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { MahjongService } from '@/core/MahjongService.js';
|
||||||
|
import { ApiError } from '../../error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'read:account',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchRoom: {
|
||||||
|
message: 'No such room.',
|
||||||
|
code: 'NO_SUCH_ROOM',
|
||||||
|
id: 'd77df68f-06f3-492b-9078-e6f72f4acf23',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'MahjongRoomDetailed',
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
roomId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['roomId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
private mahjongService: MahjongService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const room = await this.mahjongService.getRoom(ps.roomId);
|
||||||
|
|
||||||
|
if (room == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchRoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.mahjongService.packRoom(room, me);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { ReversiService } from '@/core/ReversiService.js';
|
||||||
|
import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js';
|
||||||
|
import { ApiError } from '../../error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
errors: {
|
||||||
|
noSuchGame: {
|
||||||
|
message: 'No such game.',
|
||||||
|
code: 'NO_SUCH_GAME',
|
||||||
|
id: '8fb05624-b525-43dd-90f7-511852bdfeee',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
properties: {
|
||||||
|
desynced: { type: 'boolean' },
|
||||||
|
game: {
|
||||||
|
type: 'object',
|
||||||
|
optional: true, nullable: true,
|
||||||
|
ref: 'ReversiGameDetailed',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
gameId: { type: 'string', format: 'misskey:id' },
|
||||||
|
crc32: { type: 'string' },
|
||||||
|
},
|
||||||
|
required: ['gameId', 'crc32'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
private reversiService: ReversiService,
|
||||||
|
private reversiGameEntityService: ReversiGameEntityService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const game = await this.reversiService.checkCrc(ps.gameId, ps.crc32);
|
||||||
|
if (game) {
|
||||||
|
return {
|
||||||
|
desynced: true,
|
||||||
|
game: await this.reversiGameEntityService.packDetail(game),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
desynced: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import { HashtagChannelService } from './channels/hashtag.js';
|
||||||
import { RoleTimelineChannelService } from './channels/role-timeline.js';
|
import { RoleTimelineChannelService } from './channels/role-timeline.js';
|
||||||
import { ReversiChannelService } from './channels/reversi.js';
|
import { ReversiChannelService } from './channels/reversi.js';
|
||||||
import { ReversiGameChannelService } from './channels/reversi-game.js';
|
import { ReversiGameChannelService } from './channels/reversi-game.js';
|
||||||
|
import { MahjongRoomChannelService } from './channels/mahjong-room.js';
|
||||||
import { type MiChannelService } from './channel.js';
|
import { type MiChannelService } from './channel.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -42,6 +43,7 @@ export class ChannelsService {
|
||||||
private adminChannelService: AdminChannelService,
|
private adminChannelService: AdminChannelService,
|
||||||
private reversiChannelService: ReversiChannelService,
|
private reversiChannelService: ReversiChannelService,
|
||||||
private reversiGameChannelService: ReversiGameChannelService,
|
private reversiGameChannelService: ReversiGameChannelService,
|
||||||
|
private mahjongRoomChannelService: MahjongRoomChannelService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +66,7 @@ export class ChannelsService {
|
||||||
case 'admin': return this.adminChannelService;
|
case 'admin': return this.adminChannelService;
|
||||||
case 'reversi': return this.reversiChannelService;
|
case 'reversi': return this.reversiChannelService;
|
||||||
case 'reversiGame': return this.reversiGameChannelService;
|
case 'reversiGame': return this.reversiGameChannelService;
|
||||||
|
case 'mahjongRoom': return this.mahjongRoomChannelService;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`no such channel: ${name}`);
|
throw new Error(`no such channel: ${name}`);
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { MahjongService } from '@/core/MahjongService.js';
|
||||||
|
import Channel, { type MiChannelService } from '../channel.js';
|
||||||
|
|
||||||
|
class MahjongRoomChannel extends Channel {
|
||||||
|
public readonly chName = 'mahjongRoom';
|
||||||
|
public static shouldShare = false;
|
||||||
|
public static requireCredential = true as const;
|
||||||
|
public static kind = 'read:account';
|
||||||
|
private roomId: string | null = null;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private mahjongService: MahjongService,
|
||||||
|
|
||||||
|
id: string,
|
||||||
|
connection: Channel['connection'],
|
||||||
|
) {
|
||||||
|
super(id, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async init(params: any) {
|
||||||
|
this.roomId = params.roomId as string;
|
||||||
|
|
||||||
|
this.subscriber.on(`mahjongRoomStream:${this.roomId}`, this.send);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public onMessage(type: string, body: any) {
|
||||||
|
switch (type) {
|
||||||
|
case 'ready': this.ready(body); break;
|
||||||
|
case 'updateSettings': this.updateSettings(body.key, body.value); break;
|
||||||
|
case 'addAi': this.addAi(); break;
|
||||||
|
case 'putStone': this.putStone(body.pos, body.id); break;
|
||||||
|
case 'claimTimeIsUp': this.claimTimeIsUp(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async updateSettings(key: string, value: any) {
|
||||||
|
if (this.user == null) return;
|
||||||
|
|
||||||
|
this.mahjongService.updateSettings(this.roomId!, this.user, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async ready(ready: boolean) {
|
||||||
|
if (this.user == null) return;
|
||||||
|
|
||||||
|
this.mahjongService.changeReadyState(this.roomId!, this.user, ready);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async addAi() {
|
||||||
|
if (this.user == null) return;
|
||||||
|
|
||||||
|
this.mahjongService.addAi(this.roomId!, this.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async putStone(pos: number, id: string) {
|
||||||
|
if (this.user == null) return;
|
||||||
|
|
||||||
|
this.mahjongService.putStoneToRoom(this.roomId!, this.user, pos, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async claimTimeIsUp() {
|
||||||
|
if (this.user == null) return;
|
||||||
|
|
||||||
|
this.mahjongService.checkTimeout(this.roomId!);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public dispose() {
|
||||||
|
// Unsubscribe events
|
||||||
|
this.subscriber.off(`mahjongRoomStream:${this.roomId}`, this.send);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MahjongRoomChannelService implements MiChannelService<true> {
|
||||||
|
public readonly shouldShare = MahjongRoomChannel.shouldShare;
|
||||||
|
public readonly requireCredential = MahjongRoomChannel.requireCredential;
|
||||||
|
public readonly kind = MahjongRoomChannel.kind;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private mahjongService: MahjongService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public create(id: string, connection: Channel['connection']): MahjongRoomChannel {
|
||||||
|
return new MahjongRoomChannel(
|
||||||
|
this.mahjongService,
|
||||||
|
id,
|
||||||
|
connection,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 161 KiB |
|
@ -53,6 +53,7 @@
|
||||||
"matter-js": "0.19.0",
|
"matter-js": "0.19.0",
|
||||||
"mfm-js": "0.24.0",
|
"mfm-js": "0.24.0",
|
||||||
"misskey-bubble-game": "workspace:*",
|
"misskey-bubble-game": "workspace:*",
|
||||||
|
"misskey-mahjong": "workspace:*",
|
||||||
"misskey-js": "workspace:*",
|
"misskey-js": "workspace:*",
|
||||||
"misskey-reversi": "workspace:*",
|
"misskey-reversi": "workspace:*",
|
||||||
"photoswipe": "5.4.3",
|
"photoswipe": "5.4.3",
|
||||||
|
|
|
@ -549,6 +549,14 @@ const routes = [{
|
||||||
path: '/reversi/g/:gameId',
|
path: '/reversi/g/:gameId',
|
||||||
component: page(() => import('@/pages/reversi/game.vue')),
|
component: page(() => import('@/pages/reversi/game.vue')),
|
||||||
loginRequired: false,
|
loginRequired: false,
|
||||||
|
}, {
|
||||||
|
path: '/mahjong',
|
||||||
|
component: page(() => import('@/pages/mahjong/index.vue')),
|
||||||
|
loginRequired: false,
|
||||||
|
}, {
|
||||||
|
path: '/mahjong/g/:roomId',
|
||||||
|
component: page(() => import('@/pages/mahjong/room.vue')),
|
||||||
|
loginRequired: true,
|
||||||
}, {
|
}, {
|
||||||
path: '/timeline',
|
path: '/timeline',
|
||||||
component: page(() => import('@/pages/timeline.vue')),
|
component: page(() => import('@/pages/timeline.vue')),
|
||||||
|
|
|
@ -18,6 +18,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/>
|
<img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/>
|
||||||
</MkA>
|
</MkA>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="_panel">
|
||||||
|
<MkA to="/mahjong">
|
||||||
|
<img src="/client-assets/mahjong/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/>
|
||||||
|
</MkA>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</MkStickyContainer>
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<MkSpacer :contentMax="600">
|
||||||
|
<div class="_gaps">
|
||||||
|
<div>
|
||||||
|
<img src="/client-assets/mahjong/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="_panel _gaps" style="padding: 16px;">
|
||||||
|
<div class="_buttonsCenter">
|
||||||
|
<MkButton primary gradate rounded @click="joinRoom">{{ i18n.ts._mahjong.joinRoom }}</MkButton>
|
||||||
|
<MkButton primary gradate rounded @click="createRoom">{{ i18n.ts._mahjong.createRoom }}</MkButton>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 90%; opacity: 0.7; text-align: center;"><i class="ti ti-music"></i> {{ i18n.ts.soundWillBePlayed }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MkSpacer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, onDeactivated, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
import { useStream } from '@/stream.js';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { $i } from '@/account.js';
|
||||||
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
|
import { useRouter } from '@/global/router/supplier.js';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
import { useInterval } from '@/scripts/use-interval.js';
|
||||||
|
import * as sound from '@/scripts/sound.js';
|
||||||
|
|
||||||
|
const myGamesPagination = {
|
||||||
|
endpoint: 'mahjong/games' as const,
|
||||||
|
limit: 10,
|
||||||
|
params: {
|
||||||
|
my: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const gamesPagination = {
|
||||||
|
endpoint: 'mahjong/games' as const,
|
||||||
|
limit: 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const invitations = ref<Misskey.entities.UserLite[]>([]);
|
||||||
|
const matchingUser = ref<Misskey.entities.UserLite | null>(null);
|
||||||
|
const matchingAny = ref<boolean>(false);
|
||||||
|
const noIrregularRules = ref<boolean>(false);
|
||||||
|
|
||||||
|
async function joinRoom() {
|
||||||
|
const { canceled, result } = await os.inputText({
|
||||||
|
title: 'roomId',
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
const room = await misskeyApi('mahjong/join-room', {
|
||||||
|
roomId: result,
|
||||||
|
});
|
||||||
|
router.push(`/mahjong/g/${room.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createRoom(ev: MouseEvent) {
|
||||||
|
const room = await misskeyApi('mahjong/create-room', {
|
||||||
|
});
|
||||||
|
router.push(`/mahjong/g/${room.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
definePageMetadata(computed(() => ({
|
||||||
|
title: i18n.ts._mahjong.mahjong,
|
||||||
|
icon: 'ti ti-device-gamepad',
|
||||||
|
})));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
@keyframes blink {
|
||||||
|
0% { opacity: 1; }
|
||||||
|
50% { opacity: 0.2; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.invitation {
|
||||||
|
display: flex;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
line-height: 32px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamePreviews {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||||
|
grid-gap: var(--margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamePreview {
|
||||||
|
font-size: 90%;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamePreviewActive {
|
||||||
|
box-shadow: inset 0 0 8px 0px var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamePreviewWaiting {
|
||||||
|
box-shadow: inset 0 0 8px 0px var(--warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamePreviewPlayers {
|
||||||
|
text-align: center;
|
||||||
|
padding: 16px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamePreviewPlayersAvatar {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamePreviewFooter {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
border-top: solid 0.5px var(--divider);
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamePreviewStatusActive {
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: bold;
|
||||||
|
animation: blink 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamePreviewStatusWaiting {
|
||||||
|
color: var(--warn);
|
||||||
|
font-weight: bold;
|
||||||
|
animation: blink 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.waitingScreen {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.waitingScreenTitle {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,307 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<MkSpacer :contentMax="500">
|
||||||
|
</MkSpacer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, onActivated, onDeactivated, onMounted, onUnmounted, ref, shallowRef, triggerRef, watch } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
|
import * as Mahjong from 'misskey-mahjong';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
|
import { deepClone } from '@/scripts/clone.js';
|
||||||
|
import { useInterval } from '@/scripts/use-interval.js';
|
||||||
|
import { signinRequired } from '@/account.js';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
|
import { userPage } from '@/filters/user.js';
|
||||||
|
import * as sound from '@/scripts/sound.js';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
import { confetti } from '@/scripts/confetti.js';
|
||||||
|
|
||||||
|
const $i = signinRequired();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
room: Misskey.entities.ReversiRoomDetailed;
|
||||||
|
connection?: Misskey.ChannelConnection | null;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const room = ref<Misskey.entities.ReversiRoomDetailed>(deepClone(props.room));
|
||||||
|
const myUserNumber = computed(() => room.value.user1Id === $i.id ? 1 : room.value.user2Id === $i.id ? 2 : room.value.user3Id === $i.id ? 3 : 4);
|
||||||
|
const engine = shallowRef(new Mahjong.Engine.PlayerGameEngine(myUserNumber, room.value.gameState));
|
||||||
|
|
||||||
|
const isMyTurn = computed(() => {
|
||||||
|
return engine.value.state.turn === engine.value.myHouse;
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (room.value.isStarted && !room.value.isEnded) {
|
||||||
|
useInterval(() => {
|
||||||
|
if (room.value.isEnded) return;
|
||||||
|
const crc32 = engine.value.calcCrc32();
|
||||||
|
if (_DEV_) console.log('crc32', crc32);
|
||||||
|
misskeyApi('reversi/verify', {
|
||||||
|
roomId: room.value.id,
|
||||||
|
crc32: crc32.toString(),
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.desynced) {
|
||||||
|
console.log('resynced');
|
||||||
|
restoreRoom(res.room!);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 10000, { immediate: false, afterMounted: true });
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const appliedOps: string[] = [];
|
||||||
|
|
||||||
|
const myTurnTimerRmain = ref<number>(room.value.timeLimitForEachTurn);
|
||||||
|
const opTurnTimerRmain = ref<number>(room.value.timeLimitForEachTurn);
|
||||||
|
|
||||||
|
/*
|
||||||
|
const TIMER_INTERVAL_SEC = 3;
|
||||||
|
if (!props.room.isEnded) {
|
||||||
|
useInterval(() => {
|
||||||
|
if (myTurnTimerRmain.value > 0) {
|
||||||
|
myTurnTimerRmain.value = Math.max(0, myTurnTimerRmain.value - TIMER_INTERVAL_SEC);
|
||||||
|
}
|
||||||
|
if (opTurnTimerRmain.value > 0) {
|
||||||
|
opTurnTimerRmain.value = Math.max(0, opTurnTimerRmain.value - TIMER_INTERVAL_SEC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iAmPlayer.value) {
|
||||||
|
if ((isMyTurn.value && myTurnTimerRmain.value === 0) || (!isMyTurn.value && opTurnTimerRmain.value === 0)) {
|
||||||
|
props.connection!.send('claimTimeIsUp', {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, TIMER_INTERVAL_SEC * 1000, { immediate: false, afterMounted: true });
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
async function onStreamLog(log) {
|
||||||
|
if (log.id == null || !appliedOps.includes(log.id)) {
|
||||||
|
switch (log.operation) {
|
||||||
|
case 'put': {
|
||||||
|
sound.playUrl('/client-assets/mahjong/dahai.mp3', {
|
||||||
|
volume: 1,
|
||||||
|
playbackRate: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (log.house !== engine.value.turn) { // = desyncが発生している
|
||||||
|
const _room = await misskeyApi('reversi/show-room', {
|
||||||
|
roomId: props.room.id,
|
||||||
|
});
|
||||||
|
restoreRoom(_room);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
engine.value.op_dahai(log.house, log.tile);
|
||||||
|
triggerRef(engine);
|
||||||
|
|
||||||
|
myTurnTimerRmain.value = room.value.timeLimitForEachTurn;
|
||||||
|
opTurnTimerRmain.value = room.value.timeLimitForEachTurn;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreRoom(_room) {
|
||||||
|
room.value = deepClone(_room);
|
||||||
|
|
||||||
|
engine.value = new Mahjong.Engine.PlayerGameEngine(myUserNumber, room.value.gameState);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.connection != null) {
|
||||||
|
props.connection.on('log', onStreamLog);
|
||||||
|
props.connection.on('ended', onStreamEnded);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onActivated(() => {
|
||||||
|
if (props.connection != null) {
|
||||||
|
props.connection.on('log', onStreamLog);
|
||||||
|
props.connection.on('ended', onStreamEnded);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onDeactivated(() => {
|
||||||
|
if (props.connection != null) {
|
||||||
|
props.connection.off('log', onStreamLog);
|
||||||
|
props.connection.off('ended', onStreamEnded);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (props.connection != null) {
|
||||||
|
props.connection.off('log', onStreamLog);
|
||||||
|
props.connection.off('ended', onStreamEnded);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
@use "sass:math";
|
||||||
|
|
||||||
|
.transition_flip_enterActive,
|
||||||
|
.transition_flip_leaveActive {
|
||||||
|
backface-visibility: hidden;
|
||||||
|
transition: opacity 0.5s ease, transform 0.5s ease;
|
||||||
|
}
|
||||||
|
.transition_flip_enterFrom {
|
||||||
|
transform: rotateY(-180deg);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.transition_flip_leaveTo {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$label-size: 16px;
|
||||||
|
$gap: 4px;
|
||||||
|
|
||||||
|
.root {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
padding: 7px;
|
||||||
|
background: #8C4F26;
|
||||||
|
box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boardInner {
|
||||||
|
padding: 32px;
|
||||||
|
|
||||||
|
background: var(--panel);
|
||||||
|
box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (max-width: 400px) {
|
||||||
|
.boardInner {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.labelsX {
|
||||||
|
height: $label-size;
|
||||||
|
padding: 0 $label-size;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labelsXLabel {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0.8em;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: -(math.div($gap, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-right: -(math.div($gap, 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.labelsY {
|
||||||
|
width: $label-size;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labelsYLabel {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: -(math.div($gap, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: -(math.div($gap, 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.boardCells {
|
||||||
|
flex: 1;
|
||||||
|
display: grid;
|
||||||
|
grid-gap: $gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boardCell {
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 100%;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
perspective: 150px;
|
||||||
|
transition: border 0.25s ease, opacity 0.25s ease;
|
||||||
|
|
||||||
|
&.boardCell_empty {
|
||||||
|
border: solid 2px var(--divider);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.boardCell_empty.boardCell_can {
|
||||||
|
border-color: var(--accent);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.boardCell_empty.boardCell_myTurn {
|
||||||
|
border-color: var(--divider);
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
&.boardCell_can {
|
||||||
|
border-color: var(--accent);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.boardCell_prev {
|
||||||
|
box-shadow: 0 0 0 4px var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.boardCell_isEnded {
|
||||||
|
border-color: var(--divider);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.boardCell_none {
|
||||||
|
border-color: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.boardCellStone {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,128 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<MkStickyContainer>
|
||||||
|
<MkSpacer :contentMax="600">
|
||||||
|
<div class="_gaps">
|
||||||
|
<div class="_panel">
|
||||||
|
<MkAvatar v-if="room.user1" :user="room.user1" :class="$style.userAvatar"/>
|
||||||
|
<div v-else-if="room.user1Ai">AI</div>
|
||||||
|
<div v-if="room.user1Ready">OK</div>
|
||||||
|
</div>
|
||||||
|
<div class="_panel">
|
||||||
|
<MkAvatar v-if="room.user2" :user="room.user2" :class="$style.userAvatar"/>
|
||||||
|
<div v-else-if="room.user2Ai">AI</div>
|
||||||
|
<div v-if="room.user2Ready">OK</div>
|
||||||
|
</div>
|
||||||
|
<div class="_panel">
|
||||||
|
<MkAvatar v-if="room.user3" :user="room.user3" :class="$style.userAvatar"/>
|
||||||
|
<div v-else-if="room.user3Ai">AI</div>
|
||||||
|
<div v-if="room.user3Ready">OK</div>
|
||||||
|
</div>
|
||||||
|
<div class="_panel">
|
||||||
|
<MkAvatar v-if="room.user4" :user="room.user4" :class="$style.userAvatar"/>
|
||||||
|
<div v-else-if="room.user4Ai">AI</div>
|
||||||
|
<div v-if="room.user4Ready">OK</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MkSpacer>
|
||||||
|
<template #footer>
|
||||||
|
<div :class="$style.footer">
|
||||||
|
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="16">
|
||||||
|
<div style="text-align: center;" class="_gaps_s">
|
||||||
|
<div class="_buttonsCenter">
|
||||||
|
<MkButton rounded danger @click="leave">{{ i18n.ts._mahjong.leave }}</MkButton>
|
||||||
|
<MkButton v-if="!isReady" rounded primary @click="ready">{{ i18n.ts._mahjong.ready }}</MkButton>
|
||||||
|
<MkButton v-if="isReady" rounded @click="unready">{{ i18n.ts._mahjong.cancelReady }}</MkButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MkSpacer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MkStickyContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, watch, ref, onMounted, shallowRef, onUnmounted } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
|
import * as Mahjong from 'misskey-mahjong';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { signinRequired } from '@/account.js';
|
||||||
|
import { deepClone } from '@/scripts/clone.js';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import MkRadios from '@/components/MkRadios.vue';
|
||||||
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
import { MenuItem } from '@/types/menu.js';
|
||||||
|
import { useRouter } from '@/global/router/supplier.js';
|
||||||
|
|
||||||
|
const $i = signinRequired();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
room: Misskey.entities.MahjongRoomDetailed;
|
||||||
|
connection: Misskey.ChannelConnection;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const room = ref<Misskey.entities.MahjongRoomDetailed>(deepClone(props.room));
|
||||||
|
|
||||||
|
const isReady = computed(() => {
|
||||||
|
if (room.value.user1Id === $i.id && room.value.user1Ready) return true;
|
||||||
|
if (room.value.user2Id === $i.id && room.value.user2Ready) return true;
|
||||||
|
if (room.value.user3Id === $i.id && room.value.user3Ready) return true;
|
||||||
|
if (room.value.user4Id === $i.id && room.value.user4Ready) return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
async function leave() {
|
||||||
|
const { canceled } = await os.confirm({
|
||||||
|
type: 'warning',
|
||||||
|
text: i18n.ts.areYouSure,
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
|
props.connection.send('leave', {});
|
||||||
|
|
||||||
|
router.push('/mahjong');
|
||||||
|
}
|
||||||
|
|
||||||
|
function ready() {
|
||||||
|
props.connection.send('ready', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unready() {
|
||||||
|
props.connection.send('ready', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChangeReadyStates(states) {
|
||||||
|
room.value.user1Ready = states.user1;
|
||||||
|
room.value.user2Ready = states.user2;
|
||||||
|
room.value.user3Ready = states.user3;
|
||||||
|
room.value.user4Ready = states.user4;
|
||||||
|
}
|
||||||
|
|
||||||
|
props.connection.on('changeReadyStates', onChangeReadyStates);
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
props.connection.off('changeReadyStates', onChangeReadyStates);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.userAvatar {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
-webkit-backdrop-filter: var(--blur, blur(15px));
|
||||||
|
backdrop-filter: var(--blur, blur(15px));
|
||||||
|
background: var(--acrylicBg);
|
||||||
|
border-top: solid 0.5px var(--divider);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,113 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="room == null || (!room.isEnded && connection == null)"><MkLoading/></div>
|
||||||
|
<RoomSetting v-else-if="!room.isStarted" :room="room" :connection="connection!"/>
|
||||||
|
<RoomGame v-else :room="room" :connection="connection"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, watch, ref, onMounted, shallowRef, onUnmounted } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
|
import RoomSetting from './room.setting.vue';
|
||||||
|
import RoomGame from './room.game.vue';
|
||||||
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
import { useStream } from '@/stream.js';
|
||||||
|
import { signinRequired } from '@/account.js';
|
||||||
|
import { useRouter } from '@/global/router/supplier.js';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { useInterval } from '@/scripts/use-interval.js';
|
||||||
|
|
||||||
|
const $i = signinRequired();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
roomId: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const room = shallowRef<Misskey.entities.MahjongRoomDetailed | null>(null);
|
||||||
|
const connection = shallowRef<Misskey.ChannelConnection | null>(null);
|
||||||
|
const shareWhenStart = ref(false);
|
||||||
|
|
||||||
|
watch(() => props.roomId, () => {
|
||||||
|
fetchGame();
|
||||||
|
});
|
||||||
|
|
||||||
|
function start(_room: Misskey.entities.MahjongRoomDetailed) {
|
||||||
|
if (room.value?.isStarted) return;
|
||||||
|
|
||||||
|
room.value = _room;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchGame() {
|
||||||
|
const _room = await misskeyApi('mahjong/show-room', {
|
||||||
|
roomId: props.roomId,
|
||||||
|
});
|
||||||
|
|
||||||
|
room.value = _room;
|
||||||
|
shareWhenStart.value = false;
|
||||||
|
|
||||||
|
if (connection.value) {
|
||||||
|
connection.value.dispose();
|
||||||
|
}
|
||||||
|
if (!room.value.isEnded) {
|
||||||
|
connection.value = useStream().useChannel('mahjongRoom', {
|
||||||
|
roomId: room.value.id,
|
||||||
|
});
|
||||||
|
connection.value.on('started', x => {
|
||||||
|
start(x.room);
|
||||||
|
});
|
||||||
|
connection.value.on('canceled', x => {
|
||||||
|
connection.value?.dispose();
|
||||||
|
|
||||||
|
if (x.userId !== $i.id) {
|
||||||
|
os.alert({
|
||||||
|
type: 'warning',
|
||||||
|
text: i18n.ts._mahjong.roomCanceled,
|
||||||
|
});
|
||||||
|
router.push('/mahjong');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通信を取りこぼした場合の救済
|
||||||
|
useInterval(async () => {
|
||||||
|
if (room.value == null) return;
|
||||||
|
if (room.value.isStarted) return;
|
||||||
|
|
||||||
|
const _room = await misskeyApi('mahjong/show-room', {
|
||||||
|
roomId: props.roomId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_room.isStarted) {
|
||||||
|
start(_room);
|
||||||
|
} else {
|
||||||
|
room.value = _room;
|
||||||
|
}
|
||||||
|
}, 1000 * 10, {
|
||||||
|
immediate: false,
|
||||||
|
afterMounted: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchGame();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (connection.value) {
|
||||||
|
connection.value.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
definePageMetadata(computed(() => ({
|
||||||
|
title: i18n.ts._mahjong.mahjong,
|
||||||
|
icon: 'ti ti-device-roompad',
|
||||||
|
})));
|
||||||
|
</script>
|
|
@ -130,7 +130,7 @@ export function getConfig(): UserConfig {
|
||||||
|
|
||||||
// https://vitejs.dev/guide/dep-pre-bundling.html#monorepos-and-linked-dependencies
|
// https://vitejs.dev/guide/dep-pre-bundling.html#monorepos-and-linked-dependencies
|
||||||
commonjsOptions: {
|
commonjsOptions: {
|
||||||
include: [/misskey-js/, /misskey-reversi/, /misskey-bubble-game/, /node_modules/],
|
include: [/misskey-js/, /misskey-reversi/, /misskey-bubble-game/, /misskey-mahjong/, /node_modules/],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1635,6 +1635,11 @@ declare namespace entities {
|
||||||
ReversiSurrenderRequest,
|
ReversiSurrenderRequest,
|
||||||
ReversiVerifyRequest,
|
ReversiVerifyRequest,
|
||||||
ReversiVerifyResponse,
|
ReversiVerifyResponse,
|
||||||
|
MahjongCreateRoomResponse,
|
||||||
|
MahjongJoinRoomRequest,
|
||||||
|
MahjongJoinRoomResponse,
|
||||||
|
MahjongShowRoomRequest,
|
||||||
|
MahjongShowRoomResponse,
|
||||||
Error_2 as Error,
|
Error_2 as Error,
|
||||||
UserLite,
|
UserLite,
|
||||||
UserDetailedNotMeOnly,
|
UserDetailedNotMeOnly,
|
||||||
|
@ -1673,7 +1678,8 @@ declare namespace entities {
|
||||||
RoleLite,
|
RoleLite,
|
||||||
Role,
|
Role,
|
||||||
ReversiGameLite,
|
ReversiGameLite,
|
||||||
ReversiGameDetailed
|
ReversiGameDetailed,
|
||||||
|
MahjongRoomDetailed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export { entities }
|
export { entities }
|
||||||
|
@ -2169,6 +2175,24 @@ type IWebhooksShowResponse = operations['i/webhooks/show']['responses']['200']['
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type IWebhooksUpdateRequest = operations['i/webhooks/update']['requestBody']['content']['application/json'];
|
type IWebhooksUpdateRequest = operations['i/webhooks/update']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type MahjongCreateRoomResponse = operations['mahjong/create-room']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type MahjongJoinRoomRequest = operations['mahjong/join-room']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type MahjongJoinRoomResponse = operations['mahjong/join-room']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type MahjongRoomDetailed = components['schemas']['MahjongRoomDetailed'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type MahjongShowRoomRequest = operations['mahjong/show-room']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type MahjongShowRoomResponse = operations['mahjong/show-room']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type MeDetailed = components['schemas']['MeDetailed'];
|
type MeDetailed = components['schemas']['MeDetailed'];
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* version: 2024.2.0-beta.6
|
* version: 2024.2.0-beta.7
|
||||||
* generatedAt: 2024-01-24T07:32:10.455Z
|
* generatedAt: 2024-01-26T05:23:04.911Z
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { SwitchCaseResponseType } from '../api.js';
|
import type { SwitchCaseResponseType } from '../api.js';
|
||||||
|
@ -4084,5 +4084,38 @@ declare module '../api.js' {
|
||||||
params: P,
|
params: P,
|
||||||
credential?: string | null,
|
credential?: string | null,
|
||||||
): Promise<SwitchCaseResponseType<E, P>>;
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:account*
|
||||||
|
*/
|
||||||
|
request<E extends 'mahjong/create-room', P extends Endpoints[E]['req']>(
|
||||||
|
endpoint: E,
|
||||||
|
params: P,
|
||||||
|
credential?: string | null,
|
||||||
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:account*
|
||||||
|
*/
|
||||||
|
request<E extends 'mahjong/join-room', P extends Endpoints[E]['req']>(
|
||||||
|
endpoint: E,
|
||||||
|
params: P,
|
||||||
|
credential?: string | null,
|
||||||
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *read:account*
|
||||||
|
*/
|
||||||
|
request<E extends 'mahjong/show-room', P extends Endpoints[E]['req']>(
|
||||||
|
endpoint: E,
|
||||||
|
params: P,
|
||||||
|
credential?: string | null,
|
||||||
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* version: 2024.2.0-beta.6
|
* version: 2024.2.0-beta.7
|
||||||
* generatedAt: 2024-01-24T07:32:10.453Z
|
* generatedAt: 2024-01-26T05:23:04.909Z
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
@ -556,6 +556,11 @@ import type {
|
||||||
ReversiSurrenderRequest,
|
ReversiSurrenderRequest,
|
||||||
ReversiVerifyRequest,
|
ReversiVerifyRequest,
|
||||||
ReversiVerifyResponse,
|
ReversiVerifyResponse,
|
||||||
|
MahjongCreateRoomResponse,
|
||||||
|
MahjongJoinRoomRequest,
|
||||||
|
MahjongJoinRoomResponse,
|
||||||
|
MahjongShowRoomRequest,
|
||||||
|
MahjongShowRoomResponse,
|
||||||
} from './entities.js';
|
} from './entities.js';
|
||||||
|
|
||||||
export type Endpoints = {
|
export type Endpoints = {
|
||||||
|
@ -926,4 +931,7 @@ export type Endpoints = {
|
||||||
'reversi/show-game': { req: ReversiShowGameRequest; res: ReversiShowGameResponse };
|
'reversi/show-game': { req: ReversiShowGameRequest; res: ReversiShowGameResponse };
|
||||||
'reversi/surrender': { req: ReversiSurrenderRequest; res: EmptyResponse };
|
'reversi/surrender': { req: ReversiSurrenderRequest; res: EmptyResponse };
|
||||||
'reversi/verify': { req: ReversiVerifyRequest; res: ReversiVerifyResponse };
|
'reversi/verify': { req: ReversiVerifyRequest; res: ReversiVerifyResponse };
|
||||||
|
'mahjong/create-room': { req: EmptyRequest; res: MahjongCreateRoomResponse };
|
||||||
|
'mahjong/join-room': { req: MahjongJoinRoomRequest; res: MahjongJoinRoomResponse };
|
||||||
|
'mahjong/show-room': { req: MahjongShowRoomRequest; res: MahjongShowRoomResponse };
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* version: 2024.2.0-beta.6
|
* version: 2024.2.0-beta.7
|
||||||
* generatedAt: 2024-01-24T07:32:10.452Z
|
* generatedAt: 2024-01-26T05:23:04.908Z
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { operations } from './types.js';
|
import { operations } from './types.js';
|
||||||
|
@ -558,3 +558,8 @@ export type ReversiShowGameResponse = operations['reversi/show-game']['responses
|
||||||
export type ReversiSurrenderRequest = operations['reversi/surrender']['requestBody']['content']['application/json'];
|
export type ReversiSurrenderRequest = operations['reversi/surrender']['requestBody']['content']['application/json'];
|
||||||
export type ReversiVerifyRequest = operations['reversi/verify']['requestBody']['content']['application/json'];
|
export type ReversiVerifyRequest = operations['reversi/verify']['requestBody']['content']['application/json'];
|
||||||
export type ReversiVerifyResponse = operations['reversi/verify']['responses']['200']['content']['application/json'];
|
export type ReversiVerifyResponse = operations['reversi/verify']['responses']['200']['content']['application/json'];
|
||||||
|
export type MahjongCreateRoomResponse = operations['mahjong/create-room']['responses']['200']['content']['application/json'];
|
||||||
|
export type MahjongJoinRoomRequest = operations['mahjong/join-room']['requestBody']['content']['application/json'];
|
||||||
|
export type MahjongJoinRoomResponse = operations['mahjong/join-room']['responses']['200']['content']['application/json'];
|
||||||
|
export type MahjongShowRoomRequest = operations['mahjong/show-room']['requestBody']['content']['application/json'];
|
||||||
|
export type MahjongShowRoomResponse = operations['mahjong/show-room']['responses']['200']['content']['application/json'];
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* version: 2024.2.0-beta.6
|
* version: 2024.2.0-beta.7
|
||||||
* generatedAt: 2024-01-24T07:32:10.450Z
|
* generatedAt: 2024-01-26T05:23:04.907Z
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { components } from './types.js';
|
import { components } from './types.js';
|
||||||
|
@ -43,3 +43,4 @@ export type RoleLite = components['schemas']['RoleLite'];
|
||||||
export type Role = components['schemas']['Role'];
|
export type Role = components['schemas']['Role'];
|
||||||
export type ReversiGameLite = components['schemas']['ReversiGameLite'];
|
export type ReversiGameLite = components['schemas']['ReversiGameLite'];
|
||||||
export type ReversiGameDetailed = components['schemas']['ReversiGameDetailed'];
|
export type ReversiGameDetailed = components['schemas']['ReversiGameDetailed'];
|
||||||
|
export type MahjongRoomDetailed = components['schemas']['MahjongRoomDetailed'];
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
/* eslint @typescript-eslint/no-explicit-any: 0 */
|
/* eslint @typescript-eslint/no-explicit-any: 0 */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* version: 2024.2.0-beta.6
|
* version: 2024.2.0-beta.7
|
||||||
* generatedAt: 2024-01-24T07:32:10.370Z
|
* generatedAt: 2024-01-26T05:23:04.825Z
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3535,6 +3535,33 @@ export type paths = {
|
||||||
*/
|
*/
|
||||||
post: operations['reversi/verify'];
|
post: operations['reversi/verify'];
|
||||||
};
|
};
|
||||||
|
'/mahjong/create-room': {
|
||||||
|
/**
|
||||||
|
* mahjong/create-room
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:account*
|
||||||
|
*/
|
||||||
|
post: operations['mahjong/create-room'];
|
||||||
|
};
|
||||||
|
'/mahjong/join-room': {
|
||||||
|
/**
|
||||||
|
* mahjong/join-room
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:account*
|
||||||
|
*/
|
||||||
|
post: operations['mahjong/join-room'];
|
||||||
|
};
|
||||||
|
'/mahjong/show-room': {
|
||||||
|
/**
|
||||||
|
* mahjong/show-room
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *read:account*
|
||||||
|
*/
|
||||||
|
post: operations['mahjong/show-room'];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type webhooks = Record<string, never>;
|
export type webhooks = Record<string, never>;
|
||||||
|
@ -4537,6 +4564,38 @@ export type components = {
|
||||||
logs: unknown[][];
|
logs: unknown[][];
|
||||||
map: string[];
|
map: string[];
|
||||||
};
|
};
|
||||||
|
MahjongRoomDetailed: {
|
||||||
|
/** Format: id */
|
||||||
|
id: string;
|
||||||
|
/** Format: date-time */
|
||||||
|
createdAt: string;
|
||||||
|
/** Format: date-time */
|
||||||
|
startedAt: string | null;
|
||||||
|
/** Format: date-time */
|
||||||
|
endedAt: string | null;
|
||||||
|
isStarted: boolean;
|
||||||
|
isEnded: boolean;
|
||||||
|
/** Format: id */
|
||||||
|
user1Id: string;
|
||||||
|
/** Format: id */
|
||||||
|
user2Id: string;
|
||||||
|
/** Format: id */
|
||||||
|
user3Id: string;
|
||||||
|
/** Format: id */
|
||||||
|
user4Id: string;
|
||||||
|
user1: components['schemas']['User'];
|
||||||
|
user2: components['schemas']['User'];
|
||||||
|
user3: components['schemas']['User'];
|
||||||
|
user4: components['schemas']['User'];
|
||||||
|
user1Ai: boolean;
|
||||||
|
user2Ai: boolean;
|
||||||
|
user3Ai: boolean;
|
||||||
|
user4Ai: boolean;
|
||||||
|
user1Ready: boolean;
|
||||||
|
user2Ready: boolean;
|
||||||
|
user3Ready: boolean;
|
||||||
|
user4Ready: boolean;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
responses: never;
|
responses: never;
|
||||||
parameters: never;
|
parameters: never;
|
||||||
|
@ -26057,5 +26116,159 @@ export type operations = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* mahjong/create-room
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:account*
|
||||||
|
*/
|
||||||
|
'mahjong/create-room': {
|
||||||
|
responses: {
|
||||||
|
/** @description OK (with results) */
|
||||||
|
200: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['MahjongRoomDetailed'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Client error */
|
||||||
|
400: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Authentication error */
|
||||||
|
401: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Forbidden error */
|
||||||
|
403: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description I'm Ai */
|
||||||
|
418: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Internal server error */
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* mahjong/join-room
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:account*
|
||||||
|
*/
|
||||||
|
'mahjong/join-room': {
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
/** Format: misskey:id */
|
||||||
|
roomId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description OK (with results) */
|
||||||
|
200: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['MahjongRoomDetailed'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Client error */
|
||||||
|
400: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Authentication error */
|
||||||
|
401: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Forbidden error */
|
||||||
|
403: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description I'm Ai */
|
||||||
|
418: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Internal server error */
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* mahjong/show-room
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *read:account*
|
||||||
|
*/
|
||||||
|
'mahjong/show-room': {
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
/** Format: misskey:id */
|
||||||
|
roomId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description OK (with results) */
|
||||||
|
200: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['MahjongRoomDetailed'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Client error */
|
||||||
|
400: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Authentication error */
|
||||||
|
401: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Forbidden error */
|
||||||
|
403: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description I'm Ai */
|
||||||
|
418: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Internal server error */
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
node_modules
|
||||||
|
/built
|
||||||
|
/coverage
|
||||||
|
/.eslintrc.js
|
||||||
|
/jest.config.ts
|
||||||
|
/test
|
||||||
|
/test-d
|
|
@ -0,0 +1,10 @@
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: ['./tsconfig.json'],
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'../shared/.eslintrc.js',
|
||||||
|
],
|
||||||
|
};
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { build } from "esbuild";
|
||||||
|
import { globSync } from "glob";
|
||||||
|
|
||||||
|
const entryPoints = globSync("./src/**/**.{ts,tsx}");
|
||||||
|
|
||||||
|
/** @type {import('esbuild').BuildOptions} */
|
||||||
|
const options = {
|
||||||
|
entryPoints,
|
||||||
|
minify: true,
|
||||||
|
outdir: "./built/esm",
|
||||||
|
target: "es2022",
|
||||||
|
platform: "browser",
|
||||||
|
format: "esm",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.env.WATCH === "true") {
|
||||||
|
options.watch = {
|
||||||
|
onRebuild(error, result) {
|
||||||
|
if (error) {
|
||||||
|
console.error("watch build failed:", error);
|
||||||
|
} else {
|
||||||
|
console.log("watch build succeeded:", result);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
build(options).catch((err) => {
|
||||||
|
process.stderr.write(err.stderr);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"type": "module",
|
||||||
|
"name": "misskey-mahjong",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./built/esm/index.js",
|
||||||
|
"types": "./built/dts/index.d.ts"
|
||||||
|
},
|
||||||
|
"./*": {
|
||||||
|
"import": "./built/esm/*",
|
||||||
|
"types": "./built/dts/*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "node ./build.js",
|
||||||
|
"build:tsc": "npm run tsc",
|
||||||
|
"tsc": "npm run tsc-esm && npm run tsc-dts",
|
||||||
|
"tsc-esm": "tsc --outDir built/esm",
|
||||||
|
"tsc-dts": "tsc --outDir built/dts --declaration true --emitDeclarationOnly true --declarationMap true",
|
||||||
|
"watch": "nodemon -w src -e ts,js,cjs,mjs,json --exec \"pnpm run build:tsc\"",
|
||||||
|
"eslint": "eslint . --ext .js,.jsx,.ts,.tsx",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"lint": "pnpm typecheck && pnpm eslint"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
|
"@types/node": "20.11.5",
|
||||||
|
"@typescript-eslint/eslint-plugin": "6.18.1",
|
||||||
|
"@typescript-eslint/parser": "6.18.1",
|
||||||
|
"eslint": "8.56.0",
|
||||||
|
"nodemon": "3.0.2",
|
||||||
|
"typescript": "5.3.3"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"built"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"crc-32": "1.2.2",
|
||||||
|
"esbuild": "0.19.11",
|
||||||
|
"glob": "10.3.10"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,523 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import CRC32 from 'crc-32';
|
||||||
|
|
||||||
|
export const TILE_TYPES = [
|
||||||
|
'bamboo1',
|
||||||
|
'bamboo2',
|
||||||
|
'bamboo3',
|
||||||
|
'bamboo4',
|
||||||
|
'bamboo5',
|
||||||
|
'bamboo6',
|
||||||
|
'bamboo7',
|
||||||
|
'bamboo8',
|
||||||
|
'bamboo9',
|
||||||
|
'character1',
|
||||||
|
'character2',
|
||||||
|
'character3',
|
||||||
|
'character4',
|
||||||
|
'character5',
|
||||||
|
'character6',
|
||||||
|
'character7',
|
||||||
|
'character8',
|
||||||
|
'character9',
|
||||||
|
'circle1',
|
||||||
|
'circle2',
|
||||||
|
'circle3',
|
||||||
|
'circle4',
|
||||||
|
'circle5',
|
||||||
|
'circle6',
|
||||||
|
'circle7',
|
||||||
|
'circle8',
|
||||||
|
'circle9',
|
||||||
|
'wind-east',
|
||||||
|
'wind-south',
|
||||||
|
'wind-west',
|
||||||
|
'wind-north',
|
||||||
|
'dragon-red',
|
||||||
|
'dragon-green',
|
||||||
|
'dragon-white',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type Tile = typeof TILE_TYPES[number];
|
||||||
|
|
||||||
|
export function isTile(tile: string): tile is Tile {
|
||||||
|
return TILE_TYPES.includes(tile as Tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type House = 'e' | 's' | 'w' | 'n';
|
||||||
|
|
||||||
|
export type MasterState = {
|
||||||
|
user1House: House;
|
||||||
|
user2House: House;
|
||||||
|
user3House: House;
|
||||||
|
user4House: House;
|
||||||
|
tiles: Tile[];
|
||||||
|
eHandTiles: Tile[];
|
||||||
|
sHandTiles: Tile[];
|
||||||
|
wHandTiles: Tile[];
|
||||||
|
nHandTiles: Tile[];
|
||||||
|
eHoTiles: Tile[];
|
||||||
|
sHoTiles: Tile[];
|
||||||
|
wHoTiles: Tile[];
|
||||||
|
nHoTiles: Tile[];
|
||||||
|
ePonnedTiles: { tile: Tile; from: House; }[];
|
||||||
|
sPonnedTiles: { tile: Tile; from: House; }[];
|
||||||
|
wPonnedTiles: { tile: Tile; from: House; }[];
|
||||||
|
nPonnedTiles: { tile: Tile; from: House; }[];
|
||||||
|
eCiiedTiles: { tiles: [Tile, Tile, Tile]; from: House; }[];
|
||||||
|
sCiiedTiles: { tiles: [Tile, Tile, Tile]; from: House; }[];
|
||||||
|
wCiiedTiles: { tiles: [Tile, Tile, Tile]; from: House; }[];
|
||||||
|
nCiiedTiles: { tiles: [Tile, Tile, Tile]; from: House; }[];
|
||||||
|
eRiichi: boolean;
|
||||||
|
sRiichi: boolean;
|
||||||
|
wRiichi: boolean;
|
||||||
|
nRiichi: boolean;
|
||||||
|
ePoints: number;
|
||||||
|
sPoints: number;
|
||||||
|
wPoints: number;
|
||||||
|
nPoints: number;
|
||||||
|
turn: House | null;
|
||||||
|
ponAsking: {
|
||||||
|
source: House;
|
||||||
|
target: House;
|
||||||
|
} | null;
|
||||||
|
ciiAsking: {
|
||||||
|
source: House;
|
||||||
|
} | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Common {
|
||||||
|
public static nextHouse(house: House): House {
|
||||||
|
switch (house) {
|
||||||
|
case 'e':
|
||||||
|
return 's';
|
||||||
|
case 's':
|
||||||
|
return 'w';
|
||||||
|
case 'w':
|
||||||
|
return 'n';
|
||||||
|
case 'n':
|
||||||
|
return 'e';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static checkYaku(tiles: Tile[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MasterGameEngine {
|
||||||
|
public state: MasterState;
|
||||||
|
|
||||||
|
constructor(state: MasterState) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static createInitialState(): MasterState {
|
||||||
|
const tiles = TILE_TYPES.slice();
|
||||||
|
tiles.sort(() => Math.random() - 0.5);
|
||||||
|
|
||||||
|
const eHandTiles = tiles.splice(0, 14);
|
||||||
|
const sHandTiles = tiles.splice(0, 13);
|
||||||
|
const wHandTiles = tiles.splice(0, 13);
|
||||||
|
const nHandTiles = tiles.splice(0, 13);
|
||||||
|
|
||||||
|
return {
|
||||||
|
user1House: 'e',
|
||||||
|
user2House: 's',
|
||||||
|
user3House: 'w',
|
||||||
|
user4House: 'n',
|
||||||
|
tiles,
|
||||||
|
eHandTiles,
|
||||||
|
sHandTiles,
|
||||||
|
wHandTiles,
|
||||||
|
nHandTiles,
|
||||||
|
eHoTiles: [],
|
||||||
|
sHoTiles: [],
|
||||||
|
wHoTiles: [],
|
||||||
|
nHoTiles: [],
|
||||||
|
ePonnedTiles: [],
|
||||||
|
sPonnedTiles: [],
|
||||||
|
wPonnedTiles: [],
|
||||||
|
nPonnedTiles: [],
|
||||||
|
eCiiedTiles: [],
|
||||||
|
sCiiedTiles: [],
|
||||||
|
wCiiedTiles: [],
|
||||||
|
nCiiedTiles: [],
|
||||||
|
eRiichi: false,
|
||||||
|
sRiichi: false,
|
||||||
|
wRiichi: false,
|
||||||
|
nRiichi: false,
|
||||||
|
ePoints: 25000,
|
||||||
|
sPoints: 25000,
|
||||||
|
wPoints: 25000,
|
||||||
|
nPoints: 25000,
|
||||||
|
turn: 'e',
|
||||||
|
ponAsking: null,
|
||||||
|
ciiAsking: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private ツモ(): Tile {
|
||||||
|
const tile = this.state.tiles.pop();
|
||||||
|
switch (this.state.turn) {
|
||||||
|
case 'e':
|
||||||
|
this.state.eHandTiles.push(tile);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
this.state.sHandTiles.push(tile);
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
this.state.wHandTiles.push(tile);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
this.state.nHandTiles.push(tile);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public op_dahai(house: House, tile: Tile) {
|
||||||
|
if (this.state.turn !== house) throw new Error('Not your turn');
|
||||||
|
|
||||||
|
switch (house) {
|
||||||
|
case 'e':
|
||||||
|
if (!this.state.eHandTiles.includes(tile)) throw new Error('Invalid tile');
|
||||||
|
this.state.eHandTiles.splice(this.state.eHandTiles.indexOf(tile), 1);
|
||||||
|
this.state.eHoTiles.push(tile);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
if (!this.state.sHandTiles.includes(tile)) throw new Error('Invalid tile');
|
||||||
|
this.state.sHandTiles.splice(this.state.sHandTiles.indexOf(tile), 1);
|
||||||
|
this.state.sHoTiles.push(tile);
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
if (!this.state.wHandTiles.includes(tile)) throw new Error('Invalid tile');
|
||||||
|
this.state.wHandTiles.splice(this.state.wHandTiles.indexOf(tile), 1);
|
||||||
|
this.state.wHoTiles.push(tile);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
if (!this.state.nHandTiles.includes(tile)) throw new Error('Invalid tile');
|
||||||
|
this.state.nHandTiles.splice(this.state.nHandTiles.indexOf(tile), 1);
|
||||||
|
this.state.nHoTiles.push(tile);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let canPonHouse: House | null = null;
|
||||||
|
if (house === 'e') {
|
||||||
|
canPonHouse = this.canPon('s', tile) ? 's' : this.canPon('w', tile) ? 'w' : this.canPon('n', tile) ? 'n' : null;
|
||||||
|
} else if (house === 's') {
|
||||||
|
canPonHouse = this.canPon('e', tile) ? 'e' : this.canPon('w', tile) ? 'w' : this.canPon('n', tile) ? 'n' : null;
|
||||||
|
} else if (house === 'w') {
|
||||||
|
canPonHouse = this.canPon('e', tile) ? 'e' : this.canPon('s', tile) ? 's' : this.canPon('n', tile) ? 'n' : null;
|
||||||
|
} else if (house === 'n') {
|
||||||
|
canPonHouse = this.canPon('e', tile) ? 'e' : this.canPon('s', tile) ? 's' : this.canPon('w', tile) ? 'w' : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
//let canCii: boolean = false;
|
||||||
|
//if (house === 'e') {
|
||||||
|
// canCii = this.state.sHandTiles...
|
||||||
|
//} else if (house === 's') {
|
||||||
|
// canCii = this.state.wHandTiles...
|
||||||
|
//} else if (house === 'w') {
|
||||||
|
// canCii = this.state.nHandTiles...
|
||||||
|
//} else if (house === 'n') {
|
||||||
|
// canCii = this.state.eHandTiles...
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (canPonHouse) {
|
||||||
|
this.state.ponAsking = {
|
||||||
|
source: house,
|
||||||
|
target: canPonHouse,
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
canPonHouse: canPonHouse,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state.turn = Common.nextHouse(house);
|
||||||
|
|
||||||
|
const tsumoTile = this.ツモ();
|
||||||
|
|
||||||
|
return {
|
||||||
|
tsumo: tsumoTile,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public op_pon(house: House) {
|
||||||
|
if (this.state.ponAsking == null) throw new Error('No one is asking for pon');
|
||||||
|
if (this.state.ponAsking.target !== house) throw new Error('Not you');
|
||||||
|
|
||||||
|
const source = this.state.ponAsking.source;
|
||||||
|
const target = this.state.ponAsking.target;
|
||||||
|
this.state.ponAsking = null;
|
||||||
|
|
||||||
|
let tile: Tile;
|
||||||
|
|
||||||
|
switch (source) {
|
||||||
|
case 'e':
|
||||||
|
tile = this.state.eHoTiles.pop();
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
tile = this.state.sHoTiles.pop();
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
tile = this.state.wHoTiles.pop();
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
tile = this.state.nHoTiles.pop();
|
||||||
|
break;
|
||||||
|
default: throw new Error('Invalid source');
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (target) {
|
||||||
|
case 'e':
|
||||||
|
this.state.ePonnedTiles.push({ tile, from: source });
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
this.state.sPonnedTiles.push({ tile, from: source });
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
this.state.wPonnedTiles.push({ tile, from: source });
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
this.state.nPonnedTiles.push({ tile, from: source });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state.turn = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public op_noOnePon() {
|
||||||
|
if (this.state.ponAsking == null) throw new Error('No one is asking for pon');
|
||||||
|
|
||||||
|
this.state.ponAsking = null;
|
||||||
|
this.state.turn = Common.nextHouse(this.state.turn);
|
||||||
|
|
||||||
|
const tile = this.ツモ();
|
||||||
|
|
||||||
|
return {
|
||||||
|
house: this.state.turn,
|
||||||
|
tile,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private canPon(house: House, tile: Tile): boolean {
|
||||||
|
switch (house) {
|
||||||
|
case 'e':
|
||||||
|
return this.state.eHandTiles.filter(t => t === tile).length === 2;
|
||||||
|
case 's':
|
||||||
|
return this.state.sHandTiles.filter(t => t === tile).length === 2;
|
||||||
|
case 'w':
|
||||||
|
return this.state.wHandTiles.filter(t => t === tile).length === 2;
|
||||||
|
case 'n':
|
||||||
|
return this.state.nHandTiles.filter(t => t === tile).length === 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public calcCrc32ForUser1(): number {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
public calcCrc32ForUser2(): number {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
public calcCrc32ForUser3(): number {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
public calcCrc32ForUser4(): number {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PlayerState = {
|
||||||
|
user1House: House;
|
||||||
|
user2House: House;
|
||||||
|
user3House: House;
|
||||||
|
user4House: House;
|
||||||
|
tilesCount: number;
|
||||||
|
eHandTiles: Tile[] | null[];
|
||||||
|
sHandTiles: Tile[] | null[];
|
||||||
|
wHandTiles: Tile[] | null[];
|
||||||
|
nHandTiles: Tile[] | null[];
|
||||||
|
eHoTiles: Tile[];
|
||||||
|
sHoTiles: Tile[];
|
||||||
|
wHoTiles: Tile[];
|
||||||
|
nHoTiles: Tile[];
|
||||||
|
ePonnedTiles: { tile: Tile; from: House; }[];
|
||||||
|
sPonnedTiles: { tile: Tile; from: House; }[];
|
||||||
|
wPonnedTiles: { tile: Tile; from: House; }[];
|
||||||
|
nPonnedTiles: { tile: Tile; from: House; }[];
|
||||||
|
eCiiedTiles: { tiles: [Tile, Tile, Tile]; from: House; }[];
|
||||||
|
sCiiedTiles: { tiles: [Tile, Tile, Tile]; from: House; }[];
|
||||||
|
wCiiedTiles: { tiles: [Tile, Tile, Tile]; from: House; }[];
|
||||||
|
nCiiedTiles: { tiles: [Tile, Tile, Tile]; from: House; }[];
|
||||||
|
eRiichi: boolean;
|
||||||
|
sRiichi: boolean;
|
||||||
|
wRiichi: boolean;
|
||||||
|
nRiichi: boolean;
|
||||||
|
ePoints: number;
|
||||||
|
sPoints: number;
|
||||||
|
wPoints: number;
|
||||||
|
nPoints: number;
|
||||||
|
latestDahaiedTile: Tile | null;
|
||||||
|
turn: House | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class PlayerGameEngine {
|
||||||
|
/**
|
||||||
|
* このエラーが発生したときはdesyncが疑われる
|
||||||
|
*/
|
||||||
|
public static InvalidOperationError = class extends Error {};
|
||||||
|
|
||||||
|
private myUserNumber: 1 | 2 | 3 | 4;
|
||||||
|
public state: PlayerState;
|
||||||
|
|
||||||
|
constructor(myUserNumber: PlayerGameEngine['myUserNumber'], state: PlayerState) {
|
||||||
|
this.myUserNumber = myUserNumber;
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get myHouse(): House {
|
||||||
|
switch (this.myUserNumber) {
|
||||||
|
case 1: return this.state.user1House;
|
||||||
|
case 2: return this.state.user2House;
|
||||||
|
case 3: return this.state.user3House;
|
||||||
|
case 4: return this.state.user4House;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get myHandTiles(): Tile[] {
|
||||||
|
switch (this.myHouse) {
|
||||||
|
case 'e': return this.state.eHandTiles as Tile[];
|
||||||
|
case 's': return this.state.sHandTiles as Tile[];
|
||||||
|
case 'w': return this.state.wHandTiles as Tile[];
|
||||||
|
case 'n': return this.state.nHandTiles as Tile[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get myHoTiles(): Tile[] {
|
||||||
|
switch (this.myHouse) {
|
||||||
|
case 'e': return this.state.eHoTiles;
|
||||||
|
case 's': return this.state.sHoTiles;
|
||||||
|
case 'w': return this.state.wHoTiles;
|
||||||
|
case 'n': return this.state.nHoTiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public op_tsumo(house: House, tile: Tile) {
|
||||||
|
if (house === this.myHouse) {
|
||||||
|
this.myHandTiles.push(tile);
|
||||||
|
} else {
|
||||||
|
switch (house) {
|
||||||
|
case 'e':
|
||||||
|
this.state.eHandTiles.push(null);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
this.state.sHandTiles.push(null);
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
this.state.wHandTiles.push(null);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
this.state.nHandTiles.push(null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public op_dahai(house: House, tile: Tile) {
|
||||||
|
if (this.state.turn !== house) throw new PlayerGameEngine.InvalidOperationError();
|
||||||
|
|
||||||
|
if (house === this.myHouse) {
|
||||||
|
this.myHandTiles.splice(this.myHandTiles.indexOf(tile), 1);
|
||||||
|
this.myHoTiles.push(tile);
|
||||||
|
} else {
|
||||||
|
switch (house) {
|
||||||
|
case 'e':
|
||||||
|
this.state.eHandTiles.pop();
|
||||||
|
this.state.eHoTiles.push(tile);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
this.state.sHandTiles.pop();
|
||||||
|
this.state.sHoTiles.push(tile);
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
this.state.wHandTiles.pop();
|
||||||
|
this.state.wHoTiles.push(tile);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
this.state.nHandTiles.pop();
|
||||||
|
this.state.nHoTiles.push(tile);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (house === this.myHouse) {
|
||||||
|
this.state.turn = null;
|
||||||
|
} else {
|
||||||
|
const canPon = this.myHandTiles.filter(t => t === tile).length === 2;
|
||||||
|
|
||||||
|
// TODO: canCii
|
||||||
|
|
||||||
|
return {
|
||||||
|
canPon,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public op_pon(source: House, target: House) {
|
||||||
|
let tile: Tile;
|
||||||
|
|
||||||
|
switch (source) {
|
||||||
|
case 'e': {
|
||||||
|
const lastTile = this.state.eHoTiles.pop();
|
||||||
|
if (lastTile == null) throw new PlayerGameEngine.InvalidOperationError();
|
||||||
|
tile = lastTile;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 's': {
|
||||||
|
const lastTile = this.state.sHoTiles.pop();
|
||||||
|
if (lastTile == null) throw new PlayerGameEngine.InvalidOperationError();
|
||||||
|
tile = lastTile;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'w': {
|
||||||
|
const lastTile = this.state.wHoTiles.pop();
|
||||||
|
if (lastTile == null) throw new PlayerGameEngine.InvalidOperationError();
|
||||||
|
tile = lastTile;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'n': {
|
||||||
|
const lastTile = this.state.nHoTiles.pop();
|
||||||
|
if (lastTile == null) throw new PlayerGameEngine.InvalidOperationError();
|
||||||
|
tile = lastTile;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: throw new Error('Invalid source');
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (target) {
|
||||||
|
case 'e':
|
||||||
|
this.state.ePonnedTiles.push({ tile, from: source });
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
this.state.sPonnedTiles.push({ tile, from: source });
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
this.state.wPonnedTiles.push({ tile, from: source });
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
this.state.nPonnedTiles.push({ tile, from: source });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state.turn = target;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * as Engine from './engine.js';
|
||||||
|
export * as Serializer from './serializer.js';
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Tile } from './engine.js';
|
||||||
|
|
||||||
|
export type Log = {
|
||||||
|
time: number;
|
||||||
|
player: 1 | 2 | 3 | 4;
|
||||||
|
operation: 'dahai';
|
||||||
|
tile: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SerializedLog = number[];
|
||||||
|
|
||||||
|
export const TILE_MAP: Record<Tile, number> = {
|
||||||
|
'bamboo1': 1,
|
||||||
|
'bamboo2': 2,
|
||||||
|
'bamboo3': 3,
|
||||||
|
'bamboo4': 4,
|
||||||
|
'bamboo5': 5,
|
||||||
|
'bamboo6': 6,
|
||||||
|
'bamboo7': 7,
|
||||||
|
'bamboo8': 8,
|
||||||
|
'bamboo9': 9,
|
||||||
|
'character1': 10,
|
||||||
|
'character2': 11,
|
||||||
|
'character3': 12,
|
||||||
|
'character4': 13,
|
||||||
|
'character5': 14,
|
||||||
|
'character6': 15,
|
||||||
|
'character7': 16,
|
||||||
|
'character8': 17,
|
||||||
|
'character9': 18,
|
||||||
|
'circle1': 19,
|
||||||
|
'circle2': 20,
|
||||||
|
'circle3': 21,
|
||||||
|
'circle4': 22,
|
||||||
|
'circle5': 23,
|
||||||
|
'circle6': 24,
|
||||||
|
'circle7': 25,
|
||||||
|
'circle8': 26,
|
||||||
|
'circle9': 27,
|
||||||
|
'wind-east': 28,
|
||||||
|
'wind-south': 29,
|
||||||
|
'wind-west': 30,
|
||||||
|
'wind-north': 31,
|
||||||
|
'dragon-red': 32,
|
||||||
|
'dragon-green': 33,
|
||||||
|
'dragon-white': 34,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function serializeTile(tile: Tile): number {
|
||||||
|
return TILE_MAP[tile];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deserializeTile(tile: number): Tile {
|
||||||
|
return Object.keys(TILE_MAP).find(key => TILE_MAP[key as Tile] === tile) as Tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serializeLogs(logs: Log[]) {
|
||||||
|
const _logs: number[][] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < logs.length; i++) {
|
||||||
|
const log = logs[i];
|
||||||
|
const timeDelta = i === 0 ? log.time : log.time - logs[i - 1].time;
|
||||||
|
|
||||||
|
switch (log.operation) {
|
||||||
|
case 'dahai':
|
||||||
|
_logs.push([timeDelta, log.player, 1, serializeTile(log.tile)]);
|
||||||
|
break;
|
||||||
|
//case 'surrender':
|
||||||
|
// _logs.push([timeDelta, log.player, 1]);
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _logs;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deserializeLogs(logs: SerializedLog[]) {
|
||||||
|
const _logs: Log[] = [];
|
||||||
|
|
||||||
|
let time = 0;
|
||||||
|
|
||||||
|
for (const log of logs) {
|
||||||
|
const timeDelta = log[0];
|
||||||
|
time += timeDelta;
|
||||||
|
|
||||||
|
const player = log[1];
|
||||||
|
const operation = log[2];
|
||||||
|
|
||||||
|
switch (operation) {
|
||||||
|
case 1:
|
||||||
|
_logs.push({
|
||||||
|
time,
|
||||||
|
player: player,
|
||||||
|
operation: 'dahai',
|
||||||
|
tile: log[3],
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
//case 1:
|
||||||
|
// _logs.push({
|
||||||
|
// time,
|
||||||
|
// player: player === 1,
|
||||||
|
// operation: 'surrender',
|
||||||
|
// });
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _logs;
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "nodenext",
|
||||||
|
"moduleResolution": "nodenext",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./built/",
|
||||||
|
"removeComments": true,
|
||||||
|
"strict": true,
|
||||||
|
"strictFunctionTypes": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"typeRoots": [
|
||||||
|
"./node_modules/@types"
|
||||||
|
],
|
||||||
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"dom"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"test/**/*"
|
||||||
|
]
|
||||||
|
}
|
179
pnpm-lock.yaml
179
pnpm-lock.yaml
|
@ -263,6 +263,9 @@ importers:
|
||||||
misskey-js:
|
misskey-js:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../misskey-js
|
version: link:../misskey-js
|
||||||
|
misskey-mahjong:
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../misskey-mahjong
|
||||||
misskey-reversi:
|
misskey-reversi:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../misskey-reversi
|
version: link:../misskey-reversi
|
||||||
|
@ -778,6 +781,9 @@ importers:
|
||||||
misskey-js:
|
misskey-js:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../misskey-js
|
version: link:../misskey-js
|
||||||
|
misskey-mahjong:
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../misskey-mahjong
|
||||||
misskey-reversi:
|
misskey-reversi:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../misskey-reversi
|
version: link:../misskey-reversi
|
||||||
|
@ -831,7 +837,7 @@ importers:
|
||||||
version: 1.7.2(vue@3.4.15)
|
version: 1.7.2(vue@3.4.15)
|
||||||
vite:
|
vite:
|
||||||
specifier: 5.0.12
|
specifier: 5.0.12
|
||||||
version: 5.0.12(@types/node@20.11.5)(sass@1.70.0)
|
version: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0)
|
||||||
vue:
|
vue:
|
||||||
specifier: 3.4.15
|
specifier: 3.4.15
|
||||||
version: 3.4.15(typescript@5.3.3)
|
version: 3.4.15(typescript@5.3.3)
|
||||||
|
@ -1009,7 +1015,7 @@ importers:
|
||||||
version: 1.0.3
|
version: 1.0.3
|
||||||
vitest:
|
vitest:
|
||||||
specifier: 0.34.6
|
specifier: 0.34.6
|
||||||
version: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)
|
version: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0)
|
||||||
vitest-fetch-mock:
|
vitest-fetch-mock:
|
||||||
specifier: 0.2.2
|
specifier: 0.2.2
|
||||||
version: 0.2.2(vitest@0.34.6)
|
version: 0.2.2(vitest@0.34.6)
|
||||||
|
@ -1166,6 +1172,40 @@ importers:
|
||||||
specifier: 5.3.3
|
specifier: 5.3.3
|
||||||
version: 5.3.3
|
version: 5.3.3
|
||||||
|
|
||||||
|
packages/misskey-mahjong:
|
||||||
|
dependencies:
|
||||||
|
crc-32:
|
||||||
|
specifier: 1.2.2
|
||||||
|
version: 1.2.2
|
||||||
|
esbuild:
|
||||||
|
specifier: 0.19.11
|
||||||
|
version: 0.19.11
|
||||||
|
glob:
|
||||||
|
specifier: 10.3.10
|
||||||
|
version: 10.3.10
|
||||||
|
devDependencies:
|
||||||
|
'@misskey-dev/eslint-plugin':
|
||||||
|
specifier: 1.0.0
|
||||||
|
version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0)
|
||||||
|
'@types/node':
|
||||||
|
specifier: 20.11.5
|
||||||
|
version: 20.11.5
|
||||||
|
'@typescript-eslint/eslint-plugin':
|
||||||
|
specifier: 6.18.1
|
||||||
|
version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3)
|
||||||
|
'@typescript-eslint/parser':
|
||||||
|
specifier: 6.18.1
|
||||||
|
version: 6.18.1(eslint@8.56.0)(typescript@5.3.3)
|
||||||
|
eslint:
|
||||||
|
specifier: 8.56.0
|
||||||
|
version: 8.56.0
|
||||||
|
nodemon:
|
||||||
|
specifier: 3.0.2
|
||||||
|
version: 3.0.2
|
||||||
|
typescript:
|
||||||
|
specifier: 5.3.3
|
||||||
|
version: 5.3.3
|
||||||
|
|
||||||
packages/misskey-reversi:
|
packages/misskey-reversi:
|
||||||
dependencies:
|
dependencies:
|
||||||
crc-32:
|
crc-32:
|
||||||
|
@ -1906,7 +1946,7 @@ packages:
|
||||||
'@babel/traverse': 7.22.11
|
'@babel/traverse': 7.22.11
|
||||||
'@babel/types': 7.22.17
|
'@babel/types': 7.22.17
|
||||||
convert-source-map: 1.9.0
|
convert-source-map: 1.9.0
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
gensync: 1.0.0-beta.2
|
gensync: 1.0.0-beta.2
|
||||||
json5: 2.2.3
|
json5: 2.2.3
|
||||||
semver: 6.3.1
|
semver: 6.3.1
|
||||||
|
@ -1929,7 +1969,7 @@ packages:
|
||||||
'@babel/traverse': 7.23.5
|
'@babel/traverse': 7.23.5
|
||||||
'@babel/types': 7.23.5
|
'@babel/types': 7.23.5
|
||||||
convert-source-map: 2.0.0
|
convert-source-map: 2.0.0
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
gensync: 1.0.0-beta.2
|
gensync: 1.0.0-beta.2
|
||||||
json5: 2.2.3
|
json5: 2.2.3
|
||||||
semver: 6.3.1
|
semver: 6.3.1
|
||||||
|
@ -2031,7 +2071,7 @@ packages:
|
||||||
'@babel/core': 7.23.5
|
'@babel/core': 7.23.5
|
||||||
'@babel/helper-compilation-targets': 7.22.15
|
'@babel/helper-compilation-targets': 7.22.15
|
||||||
'@babel/helper-plugin-utils': 7.22.5
|
'@babel/helper-plugin-utils': 7.22.5
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
lodash.debounce: 4.0.8
|
lodash.debounce: 4.0.8
|
||||||
resolve: 1.22.8
|
resolve: 1.22.8
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
@ -3430,7 +3470,7 @@ packages:
|
||||||
'@babel/helper-split-export-declaration': 7.22.6
|
'@babel/helper-split-export-declaration': 7.22.6
|
||||||
'@babel/parser': 7.23.5
|
'@babel/parser': 7.23.5
|
||||||
'@babel/types': 7.22.17
|
'@babel/types': 7.22.17
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
globals: 11.12.0
|
globals: 11.12.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
@ -3448,7 +3488,7 @@ packages:
|
||||||
'@babel/helper-split-export-declaration': 7.22.6
|
'@babel/helper-split-export-declaration': 7.22.6
|
||||||
'@babel/parser': 7.23.6
|
'@babel/parser': 7.23.6
|
||||||
'@babel/types': 7.23.5
|
'@babel/types': 7.23.5
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
globals: 11.12.0
|
globals: 11.12.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
@ -4155,7 +4195,7 @@ packages:
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv: 6.12.6
|
ajv: 6.12.6
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
espree: 9.6.1
|
espree: 9.6.1
|
||||||
globals: 13.19.0
|
globals: 13.19.0
|
||||||
ignore: 5.2.4
|
ignore: 5.2.4
|
||||||
|
@ -4172,7 +4212,7 @@ packages:
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv: 6.12.6
|
ajv: 6.12.6
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
espree: 9.6.1
|
espree: 9.6.1
|
||||||
globals: 13.19.0
|
globals: 13.19.0
|
||||||
ignore: 5.2.4
|
ignore: 5.2.4
|
||||||
|
@ -4407,7 +4447,7 @@ packages:
|
||||||
engines: {node: '>=10.10.0'}
|
engines: {node: '>=10.10.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@humanwhocodes/object-schema': 2.0.1
|
'@humanwhocodes/object-schema': 2.0.1
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
minimatch: 3.1.2
|
minimatch: 3.1.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
@ -4711,7 +4751,7 @@ packages:
|
||||||
magic-string: 0.27.0
|
magic-string: 0.27.0
|
||||||
react-docgen-typescript: 2.2.2(typescript@5.3.3)
|
react-docgen-typescript: 2.2.2(typescript@5.3.3)
|
||||||
typescript: 5.3.3
|
typescript: 5.3.3
|
||||||
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)
|
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0)
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@jridgewell/gen-mapping@0.3.2:
|
/@jridgewell/gen-mapping@0.3.2:
|
||||||
|
@ -4735,7 +4775,6 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/gen-mapping': 0.3.2
|
'@jridgewell/gen-mapping': 0.3.2
|
||||||
'@jridgewell/trace-mapping': 0.3.18
|
'@jridgewell/trace-mapping': 0.3.18
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@jridgewell/sourcemap-codec@1.4.14:
|
/@jridgewell/sourcemap-codec@1.4.14:
|
||||||
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
|
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
|
||||||
|
@ -6772,7 +6811,7 @@ packages:
|
||||||
magic-string: 0.30.5
|
magic-string: 0.30.5
|
||||||
rollup: 3.29.4
|
rollup: 3.29.4
|
||||||
typescript: 5.3.3
|
typescript: 5.3.3
|
||||||
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)
|
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- encoding
|
- encoding
|
||||||
- supports-color
|
- supports-color
|
||||||
|
@ -6977,7 +7016,7 @@ packages:
|
||||||
util: 0.12.5
|
util: 0.12.5
|
||||||
util-deprecate: 1.0.2
|
util-deprecate: 1.0.2
|
||||||
watchpack: 2.4.0
|
watchpack: 2.4.0
|
||||||
ws: 8.16.0
|
ws: 8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
- encoding
|
- encoding
|
||||||
|
@ -7146,7 +7185,7 @@ packages:
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-docgen: 7.0.1
|
react-docgen: 7.0.1
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)
|
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@preact/preset-vite'
|
- '@preact/preset-vite'
|
||||||
- encoding
|
- encoding
|
||||||
|
@ -7272,7 +7311,7 @@ packages:
|
||||||
'@storybook/vue3': 7.6.10(vue@3.4.15)
|
'@storybook/vue3': 7.6.10(vue@3.4.15)
|
||||||
'@vitejs/plugin-vue': 4.5.2(vite@5.0.12)(vue@3.4.15)
|
'@vitejs/plugin-vue': 4.5.2(vite@5.0.12)(vue@3.4.15)
|
||||||
magic-string: 0.30.5
|
magic-string: 0.30.5
|
||||||
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)
|
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0)
|
||||||
vue-docgen-api: 4.64.1(vue@3.4.15)
|
vue-docgen-api: 4.64.1(vue@3.4.15)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@preact/preset-vite'
|
- '@preact/preset-vite'
|
||||||
|
@ -7772,7 +7811,7 @@ packages:
|
||||||
dom-accessibility-api: 0.5.16
|
dom-accessibility-api: 0.5.16
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
redent: 3.0.0
|
redent: 3.0.0
|
||||||
vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)
|
vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0)
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@testing-library/user-event@14.4.3(@testing-library/dom@9.2.0):
|
/@testing-library/user-event@14.4.3(@testing-library/dom@9.2.0):
|
||||||
|
@ -8446,7 +8485,7 @@ packages:
|
||||||
'@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
|
'@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
|
||||||
'@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
|
'@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
|
||||||
'@typescript-eslint/visitor-keys': 6.11.0
|
'@typescript-eslint/visitor-keys': 6.11.0
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
eslint: 8.53.0
|
eslint: 8.53.0
|
||||||
graphemer: 1.4.0
|
graphemer: 1.4.0
|
||||||
ignore: 5.2.4
|
ignore: 5.2.4
|
||||||
|
@ -8475,7 +8514,7 @@ packages:
|
||||||
'@typescript-eslint/type-utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3)
|
'@typescript-eslint/type-utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3)
|
||||||
'@typescript-eslint/utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3)
|
'@typescript-eslint/utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3)
|
||||||
'@typescript-eslint/visitor-keys': 6.18.1
|
'@typescript-eslint/visitor-keys': 6.18.1
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
eslint: 8.56.0
|
eslint: 8.56.0
|
||||||
graphemer: 1.4.0
|
graphemer: 1.4.0
|
||||||
ignore: 5.2.4
|
ignore: 5.2.4
|
||||||
|
@ -8501,7 +8540,7 @@ packages:
|
||||||
'@typescript-eslint/types': 6.11.0
|
'@typescript-eslint/types': 6.11.0
|
||||||
'@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
|
'@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
|
||||||
'@typescript-eslint/visitor-keys': 6.11.0
|
'@typescript-eslint/visitor-keys': 6.11.0
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
eslint: 8.53.0
|
eslint: 8.53.0
|
||||||
typescript: 5.3.3
|
typescript: 5.3.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
@ -8522,7 +8561,7 @@ packages:
|
||||||
'@typescript-eslint/types': 6.18.1
|
'@typescript-eslint/types': 6.18.1
|
||||||
'@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3)
|
'@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3)
|
||||||
'@typescript-eslint/visitor-keys': 6.18.1
|
'@typescript-eslint/visitor-keys': 6.18.1
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
eslint: 8.56.0
|
eslint: 8.56.0
|
||||||
typescript: 5.3.3
|
typescript: 5.3.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
@ -8557,7 +8596,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
|
'@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
|
||||||
'@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
|
'@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
eslint: 8.53.0
|
eslint: 8.53.0
|
||||||
ts-api-utils: 1.0.1(typescript@5.3.3)
|
ts-api-utils: 1.0.1(typescript@5.3.3)
|
||||||
typescript: 5.3.3
|
typescript: 5.3.3
|
||||||
|
@ -8577,7 +8616,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3)
|
'@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3)
|
||||||
'@typescript-eslint/utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3)
|
'@typescript-eslint/utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3)
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
eslint: 8.56.0
|
eslint: 8.56.0
|
||||||
ts-api-utils: 1.0.1(typescript@5.3.3)
|
ts-api-utils: 1.0.1(typescript@5.3.3)
|
||||||
typescript: 5.3.3
|
typescript: 5.3.3
|
||||||
|
@ -8606,7 +8645,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 6.11.0
|
'@typescript-eslint/types': 6.11.0
|
||||||
'@typescript-eslint/visitor-keys': 6.11.0
|
'@typescript-eslint/visitor-keys': 6.11.0
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
globby: 11.1.0
|
globby: 11.1.0
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
semver: 7.5.4
|
semver: 7.5.4
|
||||||
|
@ -8627,7 +8666,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 6.18.1
|
'@typescript-eslint/types': 6.18.1
|
||||||
'@typescript-eslint/visitor-keys': 6.18.1
|
'@typescript-eslint/visitor-keys': 6.18.1
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
globby: 11.1.0
|
globby: 11.1.0
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
minimatch: 9.0.3
|
minimatch: 9.0.3
|
||||||
|
@ -8707,7 +8746,7 @@ packages:
|
||||||
'@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.23.5)
|
'@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.23.5)
|
||||||
magic-string: 0.27.0
|
magic-string: 0.27.0
|
||||||
react-refresh: 0.14.0
|
react-refresh: 0.14.0
|
||||||
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)
|
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -8719,7 +8758,7 @@ packages:
|
||||||
vite: ^4.0.0 || ^5.0.0
|
vite: ^4.0.0 || ^5.0.0
|
||||||
vue: ^3.2.25
|
vue: ^3.2.25
|
||||||
dependencies:
|
dependencies:
|
||||||
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)
|
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0)
|
||||||
vue: 3.4.15(typescript@5.3.3)
|
vue: 3.4.15(typescript@5.3.3)
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -8730,7 +8769,7 @@ packages:
|
||||||
vite: ^5.0.0
|
vite: ^5.0.0
|
||||||
vue: ^3.2.25
|
vue: ^3.2.25
|
||||||
dependencies:
|
dependencies:
|
||||||
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)
|
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0)
|
||||||
vue: 3.4.15(typescript@5.3.3)
|
vue: 3.4.15(typescript@5.3.3)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
@ -8750,7 +8789,7 @@ packages:
|
||||||
std-env: 3.7.0
|
std-env: 3.7.0
|
||||||
test-exclude: 6.0.0
|
test-exclude: 6.0.0
|
||||||
v8-to-istanbul: 9.2.0
|
v8-to-istanbul: 9.2.0
|
||||||
vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)
|
vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -9091,7 +9130,7 @@ packages:
|
||||||
engines: {node: '>= 6.0.0'}
|
engines: {node: '>= 6.0.0'}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
@ -9099,7 +9138,7 @@ packages:
|
||||||
resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
|
resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -9485,7 +9524,7 @@ packages:
|
||||||
resolution: {integrity: sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==}
|
resolution: {integrity: sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
archy: 1.0.0
|
archy: 1.0.0
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
fastq: 1.15.0
|
fastq: 1.15.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
@ -9889,7 +9928,6 @@ packages:
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dependencies:
|
dependencies:
|
||||||
node-gyp-build: 4.6.0
|
node-gyp-build: 4.6.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/bullmq@5.1.4:
|
/bullmq@5.1.4:
|
||||||
resolution: {integrity: sha512-j/AjaPc8BhyrH7b2MyZpi4cUtGH8TJTxonZUmXEefmKU8z5DcldzmlXPief0P4+qvN0A7qwWZH3n0F+GsWgQkg==}
|
resolution: {integrity: sha512-j/AjaPc8BhyrH7b2MyZpi4cUtGH8TJTxonZUmXEefmKU8z5DcldzmlXPief0P4+qvN0A7qwWZH3n0F+GsWgQkg==}
|
||||||
|
@ -10434,7 +10472,6 @@ packages:
|
||||||
|
|
||||||
/commander@2.20.3:
|
/commander@2.20.3:
|
||||||
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
|
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/commander@6.2.1:
|
/commander@6.2.1:
|
||||||
resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==}
|
resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==}
|
||||||
|
@ -10931,6 +10968,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.2
|
ms: 2.1.2
|
||||||
supports-color: 5.5.0
|
supports-color: 5.5.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/debug@4.3.4(supports-color@8.1.1):
|
/debug@4.3.4(supports-color@8.1.1):
|
||||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||||
|
@ -10943,7 +10981,6 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.2
|
ms: 2.1.2
|
||||||
supports-color: 8.1.1
|
supports-color: 8.1.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/decamelize-keys@1.1.1:
|
/decamelize-keys@1.1.1:
|
||||||
resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==}
|
resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==}
|
||||||
|
@ -11160,7 +11197,7 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
address: 1.2.2
|
address: 1.2.2
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -11484,7 +11521,7 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
esbuild: '>=0.12 <1'
|
esbuild: '>=0.12 <1'
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
esbuild: 0.18.20
|
esbuild: 0.18.20
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
@ -11793,7 +11830,7 @@ packages:
|
||||||
ajv: 6.12.6
|
ajv: 6.12.6
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
cross-spawn: 7.0.3
|
cross-spawn: 7.0.3
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
doctrine: 3.0.0
|
doctrine: 3.0.0
|
||||||
escape-string-regexp: 4.0.0
|
escape-string-regexp: 4.0.0
|
||||||
eslint-scope: 7.2.2
|
eslint-scope: 7.2.2
|
||||||
|
@ -11840,7 +11877,7 @@ packages:
|
||||||
ajv: 6.12.6
|
ajv: 6.12.6
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
cross-spawn: 7.0.3
|
cross-spawn: 7.0.3
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
doctrine: 3.0.0
|
doctrine: 3.0.0
|
||||||
escape-string-regexp: 4.0.0
|
escape-string-regexp: 4.0.0
|
||||||
eslint-scope: 7.2.2
|
eslint-scope: 7.2.2
|
||||||
|
@ -12471,7 +12508,7 @@ packages:
|
||||||
debug:
|
debug:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
|
|
||||||
/for-each@0.3.3:
|
/for-each@0.3.3:
|
||||||
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
||||||
|
@ -13027,6 +13064,7 @@ packages:
|
||||||
/has-flag@3.0.0:
|
/has-flag@3.0.0:
|
||||||
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/has-flag@4.0.0:
|
/has-flag@4.0.0:
|
||||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||||
|
@ -13164,7 +13202,7 @@ packages:
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
dependencies:
|
dependencies:
|
||||||
agent-base: 7.1.0
|
agent-base: 7.1.0
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -13224,7 +13262,7 @@ packages:
|
||||||
engines: {node: '>= 6.0.0'}
|
engines: {node: '>= 6.0.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
agent-base: 5.1.1
|
agent-base: 5.1.1
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -13234,7 +13272,7 @@ packages:
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
dependencies:
|
dependencies:
|
||||||
agent-base: 6.0.2
|
agent-base: 6.0.2
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
@ -13243,7 +13281,7 @@ packages:
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
dependencies:
|
dependencies:
|
||||||
agent-base: 7.1.0
|
agent-base: 7.1.0
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -13403,7 +13441,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ioredis/commands': 1.2.0
|
'@ioredis/commands': 1.2.0
|
||||||
cluster-key-slot: 1.1.2
|
cluster-key-slot: 1.1.2
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
denque: 2.1.0
|
denque: 2.1.0
|
||||||
lodash.defaults: 4.2.0
|
lodash.defaults: 4.2.0
|
||||||
lodash.isarguments: 3.1.0
|
lodash.isarguments: 3.1.0
|
||||||
|
@ -13849,7 +13887,7 @@ packages:
|
||||||
resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
|
resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
istanbul-lib-coverage: 3.2.2
|
istanbul-lib-coverage: 3.2.2
|
||||||
source-map: 0.6.1
|
source-map: 0.6.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
@ -15624,7 +15662,6 @@ packages:
|
||||||
resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
|
resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
|
||||||
|
|
||||||
/node-gyp@10.0.1:
|
/node-gyp@10.0.1:
|
||||||
resolution: {integrity: sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==}
|
resolution: {integrity: sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==}
|
||||||
|
@ -17159,7 +17196,7 @@ packages:
|
||||||
engines: {node: '>=8.16.0'}
|
engines: {node: '>=8.16.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mime-types': 2.1.4
|
'@types/mime-types': 2.1.4
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
extract-zip: 1.7.0
|
extract-zip: 1.7.0
|
||||||
https-proxy-agent: 4.0.0
|
https-proxy-agent: 4.0.0
|
||||||
mime: 2.6.0
|
mime: 2.6.0
|
||||||
|
@ -18159,7 +18196,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@hapi/hoek': 10.0.1
|
'@hapi/hoek': 10.0.1
|
||||||
'@hapi/wreck': 18.0.1
|
'@hapi/wreck': 18.0.1
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
joi: 17.7.0
|
joi: 17.7.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
@ -18359,7 +18396,7 @@ packages:
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
dependencies:
|
dependencies:
|
||||||
agent-base: 7.1.0
|
agent-base: 7.1.0
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
socks: 2.7.1
|
socks: 2.7.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
@ -18512,7 +18549,7 @@ packages:
|
||||||
arg: 5.0.2
|
arg: 5.0.2
|
||||||
bluebird: 3.7.2
|
bluebird: 3.7.2
|
||||||
check-more-types: 2.24.0
|
check-more-types: 2.24.0
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
execa: 5.1.1
|
execa: 5.1.1
|
||||||
lazy-ass: 1.6.0
|
lazy-ass: 1.6.0
|
||||||
ps-tree: 1.2.0
|
ps-tree: 1.2.0
|
||||||
|
@ -18770,6 +18807,7 @@ packages:
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
dependencies:
|
dependencies:
|
||||||
has-flag: 3.0.0
|
has-flag: 3.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/supports-color@7.2.0:
|
/supports-color@7.2.0:
|
||||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||||
|
@ -18932,7 +18970,6 @@ packages:
|
||||||
acorn: 8.11.3
|
acorn: 8.11.3
|
||||||
commander: 2.20.3
|
commander: 2.20.3
|
||||||
source-map-support: 0.5.21
|
source-map-support: 0.5.21
|
||||||
dev: false
|
|
||||||
|
|
||||||
/test-exclude@6.0.0:
|
/test-exclude@6.0.0:
|
||||||
resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
|
resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
|
||||||
|
@ -19391,7 +19428,7 @@ packages:
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
cli-highlight: 2.1.11
|
cli-highlight: 2.1.11
|
||||||
dayjs: 1.11.10
|
dayjs: 1.11.10
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
dotenv: 16.0.3
|
dotenv: 16.0.3
|
||||||
glob: 10.3.10
|
glob: 10.3.10
|
||||||
ioredis: 5.3.2
|
ioredis: 5.3.2
|
||||||
|
@ -19654,7 +19691,6 @@ packages:
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dependencies:
|
dependencies:
|
||||||
node-gyp-build: 4.6.0
|
node-gyp-build: 4.6.0
|
||||||
dev: false
|
|
||||||
|
|
||||||
/util-deprecate@1.0.2:
|
/util-deprecate@1.0.2:
|
||||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||||
|
@ -19746,17 +19782,17 @@ packages:
|
||||||
core-util-is: 1.0.2
|
core-util-is: 1.0.2
|
||||||
extsprintf: 1.3.0
|
extsprintf: 1.3.0
|
||||||
|
|
||||||
/vite-node@0.34.6(@types/node@20.11.5)(sass@1.70.0):
|
/vite-node@0.34.6(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0):
|
||||||
resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
|
resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
|
||||||
engines: {node: '>=v14.18.0'}
|
engines: {node: '>=v14.18.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
cac: 6.7.14
|
cac: 6.7.14
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
mlly: 1.5.0
|
mlly: 1.5.0
|
||||||
pathe: 1.1.2
|
pathe: 1.1.2
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)
|
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
- less
|
- less
|
||||||
|
@ -19772,7 +19808,7 @@ packages:
|
||||||
resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
|
resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vite@5.0.12(@types/node@20.11.5)(sass@1.70.0):
|
/vite@5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0):
|
||||||
resolution: {integrity: sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==}
|
resolution: {integrity: sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==}
|
||||||
engines: {node: ^18.0.0 || >=20.0.0}
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
@ -19805,6 +19841,7 @@ packages:
|
||||||
postcss: 8.4.33
|
postcss: 8.4.33
|
||||||
rollup: 4.9.6
|
rollup: 4.9.6
|
||||||
sass: 1.70.0
|
sass: 1.70.0
|
||||||
|
terser: 5.27.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
|
|
||||||
|
@ -19815,12 +19852,12 @@ packages:
|
||||||
vitest: '>=0.16.0'
|
vitest: '>=0.16.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
cross-fetch: 3.1.5
|
cross-fetch: 3.1.5
|
||||||
vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)
|
vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- encoding
|
- encoding
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vitest@0.34.6(happy-dom@10.0.3)(sass@1.70.0):
|
/vitest@0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0):
|
||||||
resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==}
|
resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==}
|
||||||
engines: {node: '>=v14.18.0'}
|
engines: {node: '>=v14.18.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
@ -19863,7 +19900,7 @@ packages:
|
||||||
acorn-walk: 8.3.2
|
acorn-walk: 8.3.2
|
||||||
cac: 6.7.14
|
cac: 6.7.14
|
||||||
chai: 4.3.10
|
chai: 4.3.10
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
happy-dom: 10.0.3
|
happy-dom: 10.0.3
|
||||||
local-pkg: 0.4.3
|
local-pkg: 0.4.3
|
||||||
magic-string: 0.30.5
|
magic-string: 0.30.5
|
||||||
|
@ -19873,8 +19910,8 @@ packages:
|
||||||
strip-literal: 1.3.0
|
strip-literal: 1.3.0
|
||||||
tinybench: 2.6.0
|
tinybench: 2.6.0
|
||||||
tinypool: 0.7.0
|
tinypool: 0.7.0
|
||||||
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)
|
vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0)
|
||||||
vite-node: 0.34.6(@types/node@20.11.5)(sass@1.70.0)
|
vite-node: 0.34.6(@types/node@20.11.5)(sass@1.70.0)(terser@5.27.0)
|
||||||
why-is-node-running: 2.2.2
|
why-is-node-running: 2.2.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- less
|
- less
|
||||||
|
@ -19945,7 +19982,7 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '>=6.0.0'
|
eslint: '>=6.0.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.3.4(supports-color@5.5.0)
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
eslint: 8.56.0
|
eslint: 8.56.0
|
||||||
eslint-scope: 7.2.2
|
eslint-scope: 7.2.2
|
||||||
eslint-visitor-keys: 3.4.3
|
eslint-visitor-keys: 3.4.3
|
||||||
|
@ -20275,19 +20312,6 @@ packages:
|
||||||
async-limiter: 1.0.1
|
async-limiter: 1.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/ws@8.16.0:
|
|
||||||
resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==}
|
|
||||||
engines: {node: '>=10.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
bufferutil: ^4.0.1
|
|
||||||
utf-8-validate: '>=5.0.2'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
bufferutil:
|
|
||||||
optional: true
|
|
||||||
utf-8-validate:
|
|
||||||
optional: true
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/ws@8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
|
/ws@8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
|
||||||
resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==}
|
resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
|
@ -20302,7 +20326,6 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
bufferutil: 4.0.7
|
bufferutil: 4.0.7
|
||||||
utf-8-validate: 6.0.3
|
utf-8-validate: 6.0.3
|
||||||
dev: false
|
|
||||||
|
|
||||||
/xev@3.0.2:
|
/xev@3.0.2:
|
||||||
resolution: {integrity: sha512-8kxuH95iMXzHZj+fwqfA4UrPcYOy6bGIgfWzo9Ji23JoEc30ge/Z++Ubkiuy8c0+M64nXmmxrmJ7C8wnuBhluw==}
|
resolution: {integrity: sha512-8kxuH95iMXzHZj+fwqfA4UrPcYOy6bGIgfWzo9Ji23JoEc30ge/Z++Ubkiuy8c0+M64nXmmxrmJ7C8wnuBhluw==}
|
||||||
|
|
|
@ -6,3 +6,4 @@ packages:
|
||||||
- 'packages/misskey-js/generator'
|
- 'packages/misskey-js/generator'
|
||||||
- 'packages/misskey-reversi'
|
- 'packages/misskey-reversi'
|
||||||
- 'packages/misskey-bubble-game'
|
- 'packages/misskey-bubble-game'
|
||||||
|
- 'packages/misskey-mahjong'
|
||||||
|
|
|
@ -25,6 +25,9 @@ const fs = require('fs');
|
||||||
fs.rmSync(__dirname + '/../packages/misskey-bubble-game/built', { recursive: true, force: true });
|
fs.rmSync(__dirname + '/../packages/misskey-bubble-game/built', { recursive: true, force: true });
|
||||||
fs.rmSync(__dirname + '/../packages/misskey-bubble-game/node_modules', { recursive: true, force: true });
|
fs.rmSync(__dirname + '/../packages/misskey-bubble-game/node_modules', { recursive: true, force: true });
|
||||||
|
|
||||||
|
fs.rmSync(__dirname + '/../packages/misskey-mahjong/built', { recursive: true, force: true });
|
||||||
|
fs.rmSync(__dirname + '/../packages/misskey-mahjong/node_modules', { recursive: true, force: true });
|
||||||
|
|
||||||
fs.rmSync(__dirname + '/../built', { recursive: true, force: true });
|
fs.rmSync(__dirname + '/../built', { recursive: true, force: true });
|
||||||
fs.rmSync(__dirname + '/../node_modules', { recursive: true, force: true });
|
fs.rmSync(__dirname + '/../node_modules', { recursive: true, force: true });
|
||||||
|
|
||||||
|
|
|
@ -12,5 +12,6 @@ const fs = require('fs');
|
||||||
fs.rmSync(__dirname + '/../packages/misskey-js/built', { recursive: true, force: true });
|
fs.rmSync(__dirname + '/../packages/misskey-js/built', { recursive: true, force: true });
|
||||||
fs.rmSync(__dirname + '/../packages/misskey-reversi/built', { recursive: true, force: true });
|
fs.rmSync(__dirname + '/../packages/misskey-reversi/built', { recursive: true, force: true });
|
||||||
fs.rmSync(__dirname + '/../packages/misskey-bubble-game/built', { recursive: true, force: true });
|
fs.rmSync(__dirname + '/../packages/misskey-bubble-game/built', { recursive: true, force: true });
|
||||||
|
fs.rmSync(__dirname + '/../packages/misskey-mahjong/built', { recursive: true, force: true });
|
||||||
fs.rmSync(__dirname + '/../built', { recursive: true, force: true });
|
fs.rmSync(__dirname + '/../built', { recursive: true, force: true });
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -46,6 +46,12 @@ await execa('pnpm', ['--filter', 'misskey-bubble-game', 'build'], {
|
||||||
stderr: process.stderr,
|
stderr: process.stderr,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await execa('pnpm', ['--filter', 'misskey-mahjong', 'build'], {
|
||||||
|
cwd: _dirname + '/../',
|
||||||
|
stdout: process.stdout,
|
||||||
|
stderr: process.stderr,
|
||||||
|
});
|
||||||
|
|
||||||
execa('pnpm', ['build-pre', '--watch'], {
|
execa('pnpm', ['build-pre', '--watch'], {
|
||||||
cwd: _dirname + '/../',
|
cwd: _dirname + '/../',
|
||||||
stdout: process.stdout,
|
stdout: process.stdout,
|
||||||
|
@ -87,3 +93,9 @@ execa('pnpm', ['--filter', 'misskey-bubble-game', 'watch'], {
|
||||||
stdout: process.stdout,
|
stdout: process.stdout,
|
||||||
stderr: process.stderr,
|
stderr: process.stderr,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
execa('pnpm', ['--filter', 'misskey-mahjong', 'watch'], {
|
||||||
|
cwd: _dirname + '/../',
|
||||||
|
stdout: process.stdout,
|
||||||
|
stderr: process.stderr,
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue