From 67e6184a75599c8b3a15dee48eb46363934445bd Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 26 Jan 2024 14:25:00 +0900 Subject: [PATCH] wip --- Dockerfile | 4 + locales/index.d.ts | 26 + locales/ja-JP.yml | 8 + package.json | 3 +- .../migration/1706234054207-mahjong.js | 24 + packages/backend/package.json | 1 + packages/backend/src/core/CoreModule.ts | 6 + .../backend/src/core/GlobalEventService.ts | 32 ++ packages/backend/src/core/MahjongService.ts | 350 ++++++++++++ packages/backend/src/di-symbols.ts | 1 + packages/backend/src/misc/json-schema.ts | 2 + packages/backend/src/models/MahjongGame.ts | 89 +++ .../backend/src/models/RepositoryModule.ts | 10 +- packages/backend/src/models/_.ts | 3 + .../src/models/json-schema/mahjong-room.ts | 110 ++++ packages/backend/src/postgres.ts | 2 + packages/backend/src/server/ServerModule.ts | 2 + .../backend/src/server/api/EndpointsModule.ts | 12 + packages/backend/src/server/api/endpoints.ts | 6 + .../api/endpoints/mahjong/cancel-match.ts | 44 ++ .../api/endpoints/mahjong/create-room.ts | 44 ++ .../src/server/api/endpoints/mahjong/games.ts | 64 +++ .../server/api/endpoints/mahjong/join-room.ts | 56 ++ .../server/api/endpoints/mahjong/show-room.ts | 54 ++ .../server/api/endpoints/mahjong/verify.ts | 64 +++ .../src/server/api/stream/ChannelsService.ts | 3 + .../api/stream/channels/mahjong-room.ts | 107 ++++ packages/frontend/assets/mahjong/dahai.mp3 | Bin 0 -> 5014 bytes packages/frontend/assets/mahjong/logo.png | Bin 0 -> 164835 bytes packages/frontend/package.json | 1 + .../frontend/src/global/router/definition.ts | 8 + packages/frontend/src/pages/games.vue | 5 + packages/frontend/src/pages/mahjong/index.vue | 166 ++++++ .../frontend/src/pages/mahjong/room.game.vue | 307 ++++++++++ .../src/pages/mahjong/room.setting.vue | 128 +++++ packages/frontend/src/pages/mahjong/room.vue | 113 ++++ packages/frontend/vite.config.ts | 2 +- packages/misskey-js/etc/misskey-js.api.md | 26 +- .../misskey-js/src/autogen/apiClientJSDoc.ts | 37 +- packages/misskey-js/src/autogen/endpoint.ts | 12 +- packages/misskey-js/src/autogen/entities.ts | 9 +- packages/misskey-js/src/autogen/models.ts | 5 +- packages/misskey-js/src/autogen/types.ts | 217 +++++++- packages/misskey-mahjong/.eslintignore | 7 + packages/misskey-mahjong/.eslintrc.cjs | 10 + packages/misskey-mahjong/build.js | 31 ++ packages/misskey-mahjong/package.json | 43 ++ packages/misskey-mahjong/src/engine.ts | 523 ++++++++++++++++++ packages/misskey-mahjong/src/index.ts | 7 + packages/misskey-mahjong/src/serializer.ts | 114 ++++ packages/misskey-mahjong/tsconfig.json | 33 ++ pnpm-lock.yaml | 179 +++--- pnpm-workspace.yaml | 1 + scripts/clean-all.js | 3 + scripts/clean.js | 1 + scripts/dev.mjs | 12 + 56 files changed, 3035 insertions(+), 92 deletions(-) create mode 100644 packages/backend/migration/1706234054207-mahjong.js create mode 100644 packages/backend/src/core/MahjongService.ts create mode 100644 packages/backend/src/models/MahjongGame.ts create mode 100644 packages/backend/src/models/json-schema/mahjong-room.ts create mode 100644 packages/backend/src/server/api/endpoints/mahjong/cancel-match.ts create mode 100644 packages/backend/src/server/api/endpoints/mahjong/create-room.ts create mode 100644 packages/backend/src/server/api/endpoints/mahjong/games.ts create mode 100644 packages/backend/src/server/api/endpoints/mahjong/join-room.ts create mode 100644 packages/backend/src/server/api/endpoints/mahjong/show-room.ts create mode 100644 packages/backend/src/server/api/endpoints/mahjong/verify.ts create mode 100644 packages/backend/src/server/api/stream/channels/mahjong-room.ts create mode 100644 packages/frontend/assets/mahjong/dahai.mp3 create mode 100644 packages/frontend/assets/mahjong/logo.png create mode 100644 packages/frontend/src/pages/mahjong/index.vue create mode 100644 packages/frontend/src/pages/mahjong/room.game.vue create mode 100644 packages/frontend/src/pages/mahjong/room.setting.vue create mode 100644 packages/frontend/src/pages/mahjong/room.vue create mode 100644 packages/misskey-mahjong/.eslintignore create mode 100644 packages/misskey-mahjong/.eslintrc.cjs create mode 100644 packages/misskey-mahjong/build.js create mode 100644 packages/misskey-mahjong/package.json create mode 100644 packages/misskey-mahjong/src/engine.ts create mode 100644 packages/misskey-mahjong/src/index.ts create mode 100644 packages/misskey-mahjong/src/serializer.ts create mode 100644 packages/misskey-mahjong/tsconfig.json diff --git a/Dockerfile b/Dockerfile index a8d3dbcd89..3e54a493d9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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-reversi/package.json", "./packages/misskey-reversi/"] 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 \ 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-reversi/package.json", "./packages/misskey-reversi/"] 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 \ 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-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-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/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-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/fluent-emojis /misskey/fluent-emojis COPY --chown=misskey:misskey . ./ diff --git a/locales/index.d.ts b/locales/index.d.ts index 966c2224fb..be020be9b1 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -9604,6 +9604,32 @@ export interface Locale extends ILocale { */ "disallowIrregularRules": string; }; + "_mahjong": { + /** + * 麻雀 + */ + "mahjong": string; + /** + * ルームに参加 + */ + "joinRoom": string; + /** + * ルームを作成 + */ + "createRoom": string; + /** + * 準備完了 + */ + "ready": string; + /** + * 準備を再開 + */ + "cancelReady": string; + /** + * 退室 + */ + "leave": string; + }; "_offlineScreen": { /** * オフライン - サーバーに接続できません diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index b9e7ea4922..fa0dbdbb10 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2559,6 +2559,14 @@ _reversi: allowIrregularRules: "変則許可 (完全フリー)" disallowIrregularRules: "変則なし" +_mahjong: + mahjong: "麻雀" + joinRoom: "ルームに参加" + createRoom: "ルームを作成" + ready: "準備完了" + cancelReady: "準備を再開" + leave: "退室" + _offlineScreen: title: "オフライン - サーバーに接続できません" header: "サーバーに接続できません" diff --git a/package.json b/package.json index 77c7b92270..58140d96c3 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "packages/sw", "packages/misskey-js", "packages/misskey-reversi", - "packages/misskey-bubble-game" + "packages/misskey-bubble-game", + "packages/misskey-mahjong" ], "private": true, "scripts": { diff --git a/packages/backend/migration/1706234054207-mahjong.js b/packages/backend/migration/1706234054207-mahjong.js new file mode 100644 index 0000000000..d4178cf822 --- /dev/null +++ b/packages/backend/migration/1706234054207-mahjong.js @@ -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"`); + } +} diff --git a/packages/backend/package.json b/packages/backend/package.json index 9551991b34..65b5cbcb36 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -134,6 +134,7 @@ "mime-types": "2.1.35", "misskey-js": "workspace:*", "misskey-reversi": "workspace:*", + "misskey-mahjong": "workspace:*", "ms": "3.0.0-canary.1", "nanoid": "5.0.4", "nested-property": "4.0.0", diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index c9e285346e..81c7559b66 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -67,6 +67,7 @@ import { FanoutTimelineService } from './FanoutTimelineService.js'; import { ChannelFollowingService } from './ChannelFollowingService.js'; import { RegistryApiService } from './RegistryApiService.js'; import { ReversiService } from './ReversiService.js'; +import { MahjongService } from './MahjongService.js'; import { ChartLoggerService } from './chart/ChartLoggerService.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 $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService }; const $ReversiService: Provider = { provide: 'ReversiService', useExisting: ReversiService }; +const $MahjongService: Provider = { provide: 'MahjongService', useExisting: MahjongService }; const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService }; const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart }; @@ -344,6 +346,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting ChannelFollowingService, RegistryApiService, ReversiService, + MahjongService, ChartLoggerService, FederationChart, @@ -479,6 +482,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $ChannelFollowingService, $RegistryApiService, $ReversiService, + $MahjongService, $ChartLoggerService, $FederationChart, @@ -615,6 +619,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting ChannelFollowingService, RegistryApiService, ReversiService, + MahjongService, FederationChart, NotesChart, @@ -749,6 +754,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $ChannelFollowingService, $RegistryApiService, $ReversiService, + $MahjongService, $FederationChart, $NotesChart, diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 5b4c8cb44f..a2fc255e05 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -6,6 +6,7 @@ import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; import * as Reversi from 'misskey-reversi'; +import * as Mahjong from 'misskey-mahjong'; import type { MiChannel } from '@/models/Channel.js'; import type { MiUser } from '@/models/User.js'; import type { MiUserProfile } from '@/models/UserProfile.js'; @@ -192,6 +193,28 @@ export interface ReversiGameEventTypes { 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 // 辞書(interface or type)から{ type, body }ユニオンを定義 @@ -290,6 +313,10 @@ export type GlobalEvents = { name: `reversiGameStream:${MiReversiGame['id']}`; payload: EventUnionFromDictionary>; }; + mahjongRoom: { + name: `mahjongRoomStream:${string}`; + payload: EventUnionFromDictionary>; + }; }; // API event definitions @@ -389,4 +416,9 @@ export class GlobalEventService { public publishReversiGameStream(gameId: MiReversiGame['id'], type: K, value?: ReversiGameEventTypes[K]): void { this.publish(`reversiGameStream:${gameId}`, type, typeof value === 'undefined' ? null : value); } + + @bindThis + public publishMahjongRoomStream(roomId: string, type: K, value?: MahjongRoomEventTypes[K]): void { + this.publish(`mahjongRoomStream:${roomId}`, type, typeof value === 'undefined' ? null : value); + } } diff --git a/packages/backend/src/core/MahjongService.ts b/packages/backend/src/core/MahjongService.ts new file mode 100644 index 0000000000..4d385a0399 --- /dev/null +++ b/packages/backend/src/core/MahjongService.ts @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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(); + } +} diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index 73de01f33a..cca6b08132 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -80,5 +80,6 @@ export const DI = { userMemosRepository: Symbol('userMemosRepository'), bubbleGameRecordsRepository: Symbol('bubbleGameRecordsRepository'), reversiGamesRepository: Symbol('reversiGamesRepository'), + mahjongGamesRepository: Symbol('mahjongGamesRepository'), //#endregion }; diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index b4f0541712..62586ff827 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -40,6 +40,7 @@ import { packedSigninSchema } from '@/models/json-schema/signin.js'; import { packedRoleLiteSchema, packedRoleSchema } from '@/models/json-schema/role.js'; import { packedAdSchema } from '@/models/json-schema/ad.js'; import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js'; +import { packedMahjongRoomDetailedSchema } from '@/models/json-schema/mahjong-room.js'; export const refs = { UserLite: packedUserLiteSchema, @@ -81,6 +82,7 @@ export const refs = { Role: packedRoleSchema, ReversiGameLite: packedReversiGameLiteSchema, ReversiGameDetailed: packedReversiGameDetailedSchema, + MahjongRoomDetailed: packedMahjongRoomDetailedSchema, }; export type Packed = SchemaType; diff --git a/packages/backend/src/models/MahjongGame.ts b/packages/backend/src/models/MahjongGame.ts new file mode 100644 index 0000000000..eaef2be891 --- /dev/null +++ b/packages/backend/src/models/MahjongGame.ts @@ -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[][]; +} diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index 2b2aaeb91c..6b753b62cd 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -5,7 +5,7 @@ import { Module } from '@nestjs/common'; 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 { Provider } from '@nestjs/common'; @@ -411,6 +411,12 @@ const $reversiGamesRepository: Provider = { inject: [DI.db], }; +const $mahjongGamesRepository: Provider = { + provide: DI.mahjongGamesRepository, + useFactory: (db: DataSource) => db.getRepository(MiMahjongGame), + inject: [DI.db], +}; + @Module({ imports: [ ], @@ -482,6 +488,7 @@ const $reversiGamesRepository: Provider = { $userMemosRepository, $bubbleGameRecordsRepository, $reversiGamesRepository, + $mahjongGamesRepository, ], exports: [ $usersRepository, @@ -551,6 +558,7 @@ const $reversiGamesRepository: Provider = { $userMemosRepository, $bubbleGameRecordsRepository, $reversiGamesRepository, + $mahjongGamesRepository, ], }) export class RepositoryModule {} diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index a1a0d8823d..665f04f9ee 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -70,6 +70,7 @@ import { MiFlashLike } from '@/models/FlashLike.js'; import { MiUserListFavorite } from '@/models/UserListFavorite.js'; import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js'; import { MiReversiGame } from '@/models/ReversiGame.js'; +import { MiMahjongGame } from '@/models/MahjongGame.js'; import type { Repository } from 'typeorm'; @@ -141,6 +142,7 @@ export { MiUserMemo, MiBubbleGameRecord, MiReversiGame, + MiMahjongGame, }; export type AbuseUserReportsRepository = Repository; @@ -210,3 +212,4 @@ export type FlashLikesRepository = Repository; export type UserMemoRepository = Repository; export type BubbleGameRecordsRepository = Repository; export type ReversiGamesRepository = Repository; +export type MahjongGamesRepository = Repository; diff --git a/packages/backend/src/models/json-schema/mahjong-room.ts b/packages/backend/src/models/json-schema/mahjong-room.ts new file mode 100644 index 0000000000..71d5f268ff --- /dev/null +++ b/packages/backend/src/models/json-schema/mahjong-room.ts @@ -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; diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index 543cedc867..5f94348743 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -78,6 +78,7 @@ import { MiFlashLike } from '@/models/FlashLike.js'; import { MiUserMemo } from '@/models/UserMemo.js'; import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js'; import { MiReversiGame } from '@/models/ReversiGame.js'; +import { MiMahjongGame } from '@/models/MahjongGame.js'; import { Config } from '@/config.js'; import MisskeyLogger from '@/logger.js'; @@ -194,6 +195,7 @@ export const entities = [ MiUserMemo, MiBubbleGameRecord, MiReversiGame, + MiMahjongGame, ...charts, ]; diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index aed352d15e..b616c460f4 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -45,6 +45,7 @@ import { UserListChannelService } from './api/stream/channels/user-list.js'; import { RoleTimelineChannelService } from './api/stream/channels/role-timeline.js'; import { ReversiChannelService } from './api/stream/channels/reversi.js'; import { ReversiGameChannelService } from './api/stream/channels/reversi-game.js'; +import { MahjongRoomChannelService } from './api/stream/channels/mahjong-room.js'; @Module({ imports: [ @@ -82,6 +83,7 @@ import { ReversiGameChannelService } from './api/stream/channels/reversi-game.js RoleTimelineChannelService, ReversiChannelService, ReversiGameChannelService, + MahjongRoomChannelService, HomeTimelineChannelService, HybridTimelineChannelService, LocalTimelineChannelService, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index e74441834e..09b90b6b1b 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -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_surrender from './endpoints/reversi/surrender.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 { ApiLoggerService } from './ApiLoggerService.js'; 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_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 $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({ imports: [ @@ -1119,6 +1125,9 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $reversi_showGame, $reversi_surrender, $reversi_verify, + $mahjong_createRoom, + $mahjong_joinRoom, + $mahjong_showRoom, ], exports: [ $admin_meta, @@ -1485,6 +1494,9 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $reversi_showGame, $reversi_surrender, $reversi_verify, + $mahjong_createRoom, + $mahjong_joinRoom, + $mahjong_showRoom, ], }) export class EndpointsModule {} diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 4a88216d06..d64ead65e6 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -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_surrender from './endpoints/reversi/surrender.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 = [ ['admin/meta', ep___admin_meta], @@ -743,6 +746,9 @@ const eps = [ ['reversi/show-game', ep___reversi_showGame], ['reversi/surrender', ep___reversi_surrender], ['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 { diff --git a/packages/backend/src/server/api/endpoints/mahjong/cancel-match.ts b/packages/backend/src/server/api/endpoints/mahjong/cancel-match.ts new file mode 100644 index 0000000000..8edc049500 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/mahjong/cancel-match.ts @@ -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 { // 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); + } + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/mahjong/create-room.ts b/packages/backend/src/server/api/endpoints/mahjong/create-room.ts new file mode 100644 index 0000000000..4689a62af3 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/mahjong/create-room.ts @@ -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 { // 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); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/mahjong/games.ts b/packages/backend/src/server/api/endpoints/mahjong/games.ts new file mode 100644 index 0000000000..c1b2ff1702 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/mahjong/games.ts @@ -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 { // 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); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/mahjong/join-room.ts b/packages/backend/src/server/api/endpoints/mahjong/join-room.ts new file mode 100644 index 0000000000..47a7dd8472 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/mahjong/join-room.ts @@ -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 { // 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); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/mahjong/show-room.ts b/packages/backend/src/server/api/endpoints/mahjong/show-room.ts new file mode 100644 index 0000000000..a1b7074c49 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/mahjong/show-room.ts @@ -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 { // 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); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/mahjong/verify.ts b/packages/backend/src/server/api/endpoints/mahjong/verify.ts new file mode 100644 index 0000000000..5f5af6ce67 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/mahjong/verify.ts @@ -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 { // 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, + }; + } + }); + } +} diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index 998429dd0a..3cb732784b 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -21,6 +21,7 @@ import { HashtagChannelService } from './channels/hashtag.js'; import { RoleTimelineChannelService } from './channels/role-timeline.js'; import { ReversiChannelService } from './channels/reversi.js'; import { ReversiGameChannelService } from './channels/reversi-game.js'; +import { MahjongRoomChannelService } from './channels/mahjong-room.js'; import { type MiChannelService } from './channel.js'; @Injectable() @@ -42,6 +43,7 @@ export class ChannelsService { private adminChannelService: AdminChannelService, private reversiChannelService: ReversiChannelService, private reversiGameChannelService: ReversiGameChannelService, + private mahjongRoomChannelService: MahjongRoomChannelService, ) { } @@ -64,6 +66,7 @@ export class ChannelsService { case 'admin': return this.adminChannelService; case 'reversi': return this.reversiChannelService; case 'reversiGame': return this.reversiGameChannelService; + case 'mahjongRoom': return this.mahjongRoomChannelService; default: throw new Error(`no such channel: ${name}`); diff --git a/packages/backend/src/server/api/stream/channels/mahjong-room.ts b/packages/backend/src/server/api/stream/channels/mahjong-room.ts new file mode 100644 index 0000000000..ebdd6bd607 --- /dev/null +++ b/packages/backend/src/server/api/stream/channels/mahjong-room.ts @@ -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 { + 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, + ); + } +} diff --git a/packages/frontend/assets/mahjong/dahai.mp3 b/packages/frontend/assets/mahjong/dahai.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..baa1b83195434d3af77ecf04b6d81c22c8fd2109 GIT binary patch literal 5014 zcmeI0XHXN&x5g8QfE4My2%!g12t~S-&|7GsD=l;c1gRDXAiWAmmy#gGAYExn?*S=6 z0t8WCM5=-yP01hMJ9B6JbU)v@_cya&_U!CFbM~3DJBM`EU<~*lFu3CpaIu8`Vo(AA z%y|HzzcBa<|G&uoo3_7L`U}!uT%4t3tZAYndrd}K8rbnip`;W5AlD;A{@Mfp==q}~ zw1uDiPvHOg-;@E;zm6l%@<3ofJ!IY<$^&I(09HDGe?ojTSwX#jCtm{~jR6SI1d>q= z$z-68&X2+vOTpl4QzE9j+c8wbs)bP`uZ&%=pjxRhfI%Y$0MMjGfv6a?)CTm9LXBg{ zKwvOM$><$si~>WyWAKQ z6cpSG&|m|#|WW#oT3_uw95iM6RF5lIs}9}N@kw|{q`d&QAMkqXpfI|F)7d!7x6_nBkQ!_Z?eN1f=51Fbm8SR{G$FB5QrpWC$_5P^% zGES<7snS*s=0+Ah1~dFo7(3#SGOBFcn3?HG!yKc0KeW!)247GYHR?e+Yw}}^25oE1 z9I34WBK618*UJEwI>H7M4bx+q=#B2U3h~%#m5A;eO7^N8#&DV~=Cw%vW%lvDE|iV` zq*T%FIq3U*?&LRp%f*FGk?m7_`uR1NUbDd{igd1`FB^w!+$1?Nl=bboTp1lh02Pq6e+ET8^Ub<{k@n@u>t@dLQ3Hr#rQ$W%C*~+h?CRs$w zpxV3G!`~mxjz#ZmwtwNJEz=NNlVgScG$~^;R(q;$p_9IJosB@X{J3QW+Ocoz$|qh? zXN1rce=)6;cq%v}apx%_gUnpWx%KWLqTA-pS=-fHPRvdC02@}@@r^F)hsXK~4rWdk zKdcT~B;}O{brkpl<;S1fA}tseypafN{@H9h{7SM1F&pIx5d}Y`nM|H173N;4j-Doi zZoH+$TijkJMR4DK^im&A29)Z5RgVtFxnA-WD$DC5&ef5{@p3on&(^TI(uL>rT8@es z&BLtZcAaclh+k6}m72AqMl#-$d~{JJtnCX@ zVcHA;AX^S!u{dqR<2w8Fd55rMJ=|Jc~nhh7(F{C3-Js;NRL>d&xO}}WH7?R*c}C4WUQKXy?KZ|_H&e2 zdRSDIEh#*!5_NKq@pQtvFeRJ12D1C`;Dt;*WMprCWbevXXzW_Cf0_t1j2%7I1ML6!YyJf^#{f5v?yRO!*N!Y z5?YGSF>N!k4apsJ|bCwv)V$5MOVv zIJm+>Bzx%AYxVZ%1L`gAAI#mkvr<1w#|Q`WV`38h~# zsh~aAsklDQuIwH-YB0(4#mnb_mrU2Kq2RONLg1W^OotL9wxn3CDushK;l~KsV;8mj zs*H16$89yb>Cv9)5>pu(G>eU|^ocQqi@TZHOHu9=tICw65A_xCJ}{eh;vzh~@v~RU zP%l9-1S$uhAZutlU zLEEzXIsTj>YO!CK@DC&s&<@lREy96$>;mqIV>bdAtMx9w&a`3xeJcBhBK33quR3&7 zlhumL&W8W8lSAaMYg(C2O!4g)UL{!X-js^@#KUrWVM=f+VRv_s$>`o|-?TvCGoJ8= zlFH#|nQtmBI+Xj-dbwr4DW$zGYiHsXyGie&zcJ9fJR+@W*5p%cgkOaYn=T>JOYB^I z)A)qBit2-Y1;>X^nky8%U$Wb9SDI3eHC-T z9Wg%9-f0xeMnkM2b3}DCqDHH94^_$X`Zlgu%ji|Htfh`!OAAh(Enw$T4o}Knne|9+ zl;Mm@>t>MYaKgmY`XTg`)_mToasJ{Z6Ht3mvI16+B|kXfz>Fj`DP_jwWZr?yTR*>I zEe)Ait70}J&uyjCDoz6nTjkTted5FTJU*uj8A014CuUb)K)rqW4H?d8uJ={?$t^N* zOf}gFS!hwj)vXA__Oj;1DV*^##x>%?^Z9F>9XbXbq07N4GQ;2C%_E*Oh(q6*#a17k zkaB{bi06UdlDp}nCSIln0IkZi^#vogL=2w*xo8~%b^gV<+PVPEWg}G1s9E3kR!0e(zd8{&rtwTnBm z`yoW*)vrwd%5o7E%Ivq{8nzP)y~Us8f!zxmGjp-aB4qT~Mu_phPx;LF{lap_9I-;K z`XlAr4&Sqq+PT#P_bnN-coG^SwBWLp&I1|zcsH0BT>1T%G_S?|4&1Y!o0|;3d@b+q zf5xi^f^T?a9W2kQSTF@AYy{_Q|6Cw*8#hX_Y&!hOHbncd-93s#Q7Dh_b5eZ2bw41_?c}XIw=u-cas0unO+0rT2dyCilV!RW zSbrv0uuw8elrVCsh?AHEi61;^eKmHtFZ3EGv4L+YgsddF-T(PWu-(>uwM_1)DxQiV zNX`INk!{BI95OIA#=1;Z#5)?^Z;w&tU}{6ZEvtT?L-~^>&?4eKb|`J%JBJ_B9O2@~ zUH=d#X$&x|Vu;(?+WjEC$72anwGk9&o|TxTe$1ZfP+?xu7cP6Vd7iC>-(4?!;6X`B zd>z^B%*5@jm0OtLJ1)ovPQ!@quI&Is3O7@!I{8>$XQULQd$HZ;V{jfyHDc^~{Alz) z4md1YZgFX|vvL;yLU;C#49nCSRso2%Nja>%K`*)Z^5|gWAitj?T>sJ2-nsC6=Rj)r zL+&2Y??BqpBmzRodpyI*Zep<8GlF*{V!8%No2W{FgOS8D-McRe5TMTai@gH%&t1zd6m z0D8T0Xolb$28LC6^uxGpJuB_8!PG^?^!o>B5^G?H5>p|h6mmV6TBs^xz4&g&?4K&f zm6GG73+w&p%%3SPmdLsNn0XolqHKtwW~<~MP~cJ;)h$H4^>du_Px@r_=3YEfZ?}_E zJa}Fi3XQ)qRQPp)Z6 zSRSJV+-L8_a1)QlZ-vMW0MHr0^(wSvExSdWOOcbV4{T+SZx+C6n2NpQ5LWGeQ26BO zw-;X`>D@L&Ck-BqvxgUMnI&r`AHKgQkX_eh>o@$!PMK4>C$HiJdj*DJe}abrPfus( ziN*1C5n@=+;O(|oUFCk~*P5ztR-UxJ@}D)1Q?O z_)lT15>1zHt3PnO0qsd!C|Y8jz&1K~Re9X8kyWS+sXvvPT3P7Tq6haUU77O+GrMsR z70urB$S6bAW2&$X-E@LKU85r$K}qjwgwEF3UbM9(9*J|QM?lQ zqMsdM>f}UtRu|bUt%h&fj;dO`QR6(N^ue#P#8UNauT9BzT$N-&Kzyx5l4S|DV$zWW zn$O&ZJkfI>JsMnxvf$4#-ITCIaF_3|*%zRKaw2IEM^q$5lITaMH0&TYNH9 z0m}}FV$?PF1nZg#2S39HCkekTHgr!tx!Aoz(;#%02xP*X$;1P1zo!;@b6o>YHLvYh zkS}d*F;{JwitGP+8P3dENL+d-KmV$ga~vCN6N;%Rx0>n`A!J#2XFi7M5$rHoB8E>s zHBZacau`aOunmdXMcbZ=bYW?-qn#yxq=G`@Rq>mu`)CWKD`(BuwSv ztV6Mdi_PXRE6BKHhysFwiYG#{sWQ6(uF*?L_0tZijijYzkdB$LL#7+{YLbI^1PRET zv>a(^jXp{PWFDu)B@it`NZ7M;zw#Iq1>^Khk}wi8aTM=PU$WM>Fx#rkrFATySuyDG$_(7AYCHeZ0YXq?(UjT&pqefJNGxU z=D%5s&D!67>#6tYFHA{68V#8U83Y2My_Jzv27zF9Kp?0KM0ntrGCBk%5D2c*Tt&l4 zLtc*G(AJt+-^kX$nAy$R4j2c41V!BJ^bIYIohS^9P0ekDsE!+3sVK~igs9ZHWjq{=KX@pp7%tH-OT@u^M8T$d*pw%cQQBmf0+JzWNzaAf1J$A&&CN%=Ks&x zfH)cHJL&&_ng0)i_@xfL+x zs4Oi`@%D`bHya-}HxoNE+n>3=`Npqg?q+PIA!!aU-4T#UAu2Z3e_`_P;gA0@%=Mq6 ze~$kE+(zJxzxMpiT>iJ#`liO}=0;9t|FPY_N92rcOn(pkz15$Q-(0eC{(UV}ZOj3_ zvi||`&ydEy$AqYO*jfJLq>ukI^!uVw2pSpkJDEFK8UK0u>*9%7IsNtY*Q1sB@7u<2 zrEg;@MCHb0WNf1EY~@5HY;Er7Xld-uWUX&zVQXW`WMyk=E6DQurdpZ*Yqvj7e{Uzq z@?U2O{t;w;8FNP`TL<_5IR1mN{eK_-=fYD^{1Ib*eZ$|F3sKoQ*cv$-8XNsV9zf); zQAb-7Cs%z3V=+@ef`zEWOiauH8o5&dy2RYn#@K;^or0a2<6m6;XO*j&F|gzRO_2UW zT3P-Roxi~oWceQi{=)u0^cJx0uipTD2q;#Te>5xb@Q-dbwgIHr0nofNtxDek?e1?5 z$m9RWAqYhG+4*z-J8>}_aO+RHKGuRak|<%w0jLbIRA$*10T$Wt&D1Jl1L(m@PIG$oC#<;ePGmNK3Z$aU9a$Le+pqdRaK#NRixU)s}DqKHsnt{a2>+U1zjax(gU`cns%wtvH`)wgdZ**mH_&Ql2| zH-uKvUy`As=ptfRIEbwxe87mm8Qjna^Oc)|7G}|y3nHzgM^~-Hp8nEt=HL+*R-)Vz zbBnD>Q@xQ`E!{*+O3vYC$J&jfvw+IMTt!SvjoKScbX#WdL}lKqYNXbiT`3mHO9=mF zQ1&vkz{n z&TaUVC+wAVT~nc1ws+l}|BxwH0bIlv)dlRRj(fhJWuo__oaONzYQozGNYI*RszH|y3cTx{bq=R&91G02}QD#`RE7&p}hG0fdZwZ5r9Axptq7@DsE{9i>_KYrcVeb z!vk07?~bzqD8g|M7HLun*oX`b2HuSK-@dDV*Qzy_w>{>`JeXv5z?MMJSm}yI9PT%! z@6TkloV-kq*zu#05%JrZcdO3*+~RDqX-ie#kmE5ZIQQf=8@-R@GVhYzm1)Z>U&_{pd*3rc^HHx>vd<4IEsHnPy6$B^ zb1Yv5Igw2SD*Pche>$}%#)5TGQHUr-$Je)zEF~Gr#CNR5Yf29-CtM3o-uxC87aBD; z`6J-t)~dRkePA&<&Ir=)(`E91*e*YX(JGSc8`o-NDfO{hNRn3^bY^yDQ$o322G6?P zEWJfk>bIk^^u0N(joxm(JAfn#lv#+&n2L5!{DrW>ORnl^*Y2ys(CRt4&@l8)iED)b zrI`!UQ@@r)HU|wNmtz5RZFjz_gGDZjtpR}~(#9+6`Ulk);`}gsVzhr`COy2c1Z3Pm z1rn_-`-~a!MrS)J0tu2xE*SFl=zYZ}i<9jXT$aHM=YG?~Vl{D@ZE>jdKZiTfw_~wM zY_bztxzHXpJbJK39^Z0Vz%997LPZi!;v29o^>xJ-x8k*jWK$nqZt_t!N{#v*uGU0n-n5&w`Q^%s>qFKljnRBXc#lickqv!Pw~d7N!DslwvRuUglj6jJh0gDXDwGTXmEzgk zYrlW5^K=@UopXrs_*j|G-{2!&rt%NdPTOFvXQQojS!(9wlK72jo*oahnLaJ;I;>t- zH=ZuhM(9=Yf1aWx?d=t66uCa0t`Zu2Zf~@ed*1CXXLVT!>Q*4>C$Zq%(XakU`8k|Y z?_(0`E8a90#9;9gL5wg-9f#T3 zlnsrhA{B-5OGzezj~mP5xyo;^&(6l)#5XwLqzwl{Y{28YL0Xi7gBJdhi~(}YFc1(G zB}&G2yS*R*t6hVZ9ltv)Skmy}SK;vDeOr#Lc60+${hX5Mzr>;A9ZT!n_KCkU7+Enr zSd{Ts_pxw5Bvfyy#lFncN5SX5S~~jCY~LK_Sw+JsgU!`hLL4t@qA@D%KFx4F!Q=}=cN7dJq$eRz+#;~ z8an9=OuZIz!YU;uYiI--ScP8P$q4gX$IqS5Br&0OOjg_o8_GyD>{vy`mP&>-9h++v z4wuYUi}r}F13nw+_hb*<9M&HhD5sH;cmCXk+z)m4WqjURlbv98j%%vdeCWEqcQnv2 zUrTMui~2%+pYAP5h6G^}jI&kDDfEKr%IN}SXo9$vf-6Qqxh^r%;#vd-ITBcSMZsFH z{Kc7r7k6R%L8y?9Rl5LVLOVf>h|Z8NWAsTDj0J_2;uJUryM5L@{VWp3)ll?#M9|qF zmts;EFj_lhHX>RSD>?!1#2AnFO7AM9JF;ow+TvbU(?+-DCUF>@Y@ryp!q3lm#VLi& z!HmczgOxxnl@zIRHMXB7R<={`BRpvR(#y{6=AL0bUtSX9`_&3xm$azC2QH>J|Bu-n|HVT?9PZlA5Rjd{S6DBI6UJu zQfMTic&r*)f=6w~Xz;tUW^RPsMgGMxl?lHy&i7HRiEwGw^iY}$n_%tIA zJ%7Y{H-PdsCs}u#ZwxOTU@Zb(Sp){s3fm1kMG|p{>BhP~wF-G&-(GgT2FnXcYtu*!uIs*bf7vJd@V|eZ@AF2l*I4O zi`^*cZ*Hz$kN+|+KFNM~A2C9%Z4HTqY-d4VbmP=bwoFJx1UmNY^90YC~(V0ARoD1E-Nm-*KiZ2qvIu@ z&y%V(nKV%Oic8J>3YFt~^sldB5Q_MphcA~DGns-$jij1NF1sIIj1m5=UZ$JEe^B)p zu_FeF4`gt)_-S#m_t`IbyjyR6NN$H-o;UtOz_&GalKu%Xim$t8LnaUl zHcC3t5d9b6m5h%sFtNTj=Vl));UuaA7=H|smaK7~57K%^_cv-f*S|8ZZ8Tf)#6N=; z8=RtJ>2Q!Sb5++9!XQej*5XrPg0Kj1-zw^$eGmJfwcvA6Wu-pJ;=t!!i<(t@*WRm` zPoHsIp=ql^L`C(;(N+`h;R=A}|8r;Rw{R12jhuE9Q0oR{4)7*NH?>^+p=42sCtiSJ z(+I*MTf$g1t$$y`ei~~JWdT5pQO?fZwIOPJjZd_(s>Q`}K+E#`%Y7(whpWG(qZb4h z^iULhJ&R_}m(UViCWfj0gkF7j)`U@i`?9B(0lq){q`iCDs)3Q=K`o|D+a7{$%9q7hti%SZAlV>3v}zwn3oP&!ePTNR;Zr|59ulDQb4Y&kU-5@XEp1 z9?eLM@6>M_w9*vte<*A~2?lC@Uu0{_*{9r)mHwA?*Bbw!XiO<`jC#P0(C|yz$%!vr z_9_|rV8$DDPn0P<0vX-aNhnBb_aBm{Kr9+-b5}@Uq4Czs-yQEb@W)Fen<^PrQZjNj zd9<}r4gqdpECVT=(;p|$L7s!)V#SS2HV>6i0y58nA2AtysfjL5hhuPZGTO-_-_$ub z%7x{Sm-9EV%;AFTm5QNbzY?G&7n@8D z=jt!#lXlz+xaf1g|8fQ3?`sL9U9D&YWqfoU|MhypQz4Z6AL!L=hIXN5X=B8xw%CmH zh5=TLvl3WVxI7A1Kx5f7KbC#@4Sg}(KhS@Ayl3CtLbDtvabfVtb9pmNc1o(PoZogX zSaT>C9xe;BhTX85EuE3H_#2Dre^~w&@14Ar&XPGDjsq&`1d6zSR?fRU962sLuXBh+ z5|fKrb$bkug9*}-2VfhsPf}a@?Jli>2OtOvs{4JPXSlGb!Fm41yOtLYyvg2V?%VxY zD(0-GTf3y%=#=WX`qKEKe;h0sPmO8=N=7azBUBlGamsywAyFbcq@dyRy2l|;w%slB z%s(8IFF;6-y&BgMTAhIW7E4aUA#I+Rg2KWOxmWGKh~Y=^H;lhH3t})#9|ZtjZrHl? zxIhD$cp3LU%8XCDk;Zzl-k9l?0zr5i$hhi!pB`J{Y3F3a>2|MPC5$kFjm3gRFCBlqy8(Xpk}W$hdYdFCGgi`>IyeBR|e zrYLfQp`jKUwhq3*tq{4lLAxU75Bu?jeaDGEv=h5vQr4^*%55j3S7r}8I4KEJ=9fVQ zBTPtr!`()@0=`?zRXzy9sz>niHX5APL}fMOfjHNuH1O<<8#VDV7Z;$jgM0O6EguZ> z;%~qFg?qi}lh4)#QURxhlYHofWwPm(0;GL_L!-`#!zq4}m0*yLj!t@M7FzIm3_lbR zL(IyG_ZziDHkwMdVtE-sQU-LG#>9}Oqd6YqY!Fcu8gUJY->4DPHsG2nQ3-RCum}mE zuqWpZPF0)Ts_hg-?j#C7^n6d3o-ffB=YIiQSS^0r?8dzD`4mq#S6!B1PvF8#=a%Fk z99PVj5~M95_(RdRCVZBWB>0*U&|0lN;*VYBM^17*TpG3E+>oKq_W~_v=^H)wb*pGc61# zo0}e?Zwzqo6CfJaZRW+e_3m&o^1EX`#Oq)fu!3QCMvlhaysjPrJY_r4FUgQ^1r?|U z*&yy>G}SxV3TQ%lCw{=1kciko5=cL~?It&}TIt2S`{Wxn8*aN@>fh28rcd2PN+`6| z;B5WVJjjV3@Z`;fd_8+oJ&%e@ryMX&(DL0iX@lJXnAMvVg}v^lheh1c=<`SxUl=kM zG9?&e5t0S*4D=uzw3B4}JBb<{SPv(0> zQZd4m;0>#>9`9)lN7~4C=3WvNzCJ}nSC%OKB2gGy-5+-7a28(SFvx62LZME7M8xTK z^T4f(M4675uKhVS;oPO!zU$EWdi$tcyD!QbQnOGSQI-!Vj*X%`WwY-n8c-Y7Zf6{z z6<#vM=Y0keUN;i#FwFO(*#sQlMWj&WEypiGqQ>!9!_9XvQ`Y=Djx8swOd+VU!Agwq z3^<}iO4(Ybev*t`k%c+22`PRBp?ZL2bV1t2{3M9)0`WxX_Y)Zj(m#^@; zeSXiLdmj#F>=9*yNyOUyWfa07X#%#QR!(Nc^QBYMSz7tFI9aX-JstVFC6XWAai3he zdFkTW%llC@@N&SHVsDelxM?wR+F`Sm5YrW679Oyj4zZEqWtaBuyzGGh_8>L>#!?!O zh8=E8_;i7x>Zrxny0IiORv{>)?uEl{Q?QY4`R%8!nAf_nvCf8Km_J6 zdAoNWW3qp#%DPvKg`emb-;XCs6)(Oh)&8@~s&fo?-j3!R=a&InKmZJzg^@0BZrCs8 z(NY`N`Jo(*CNJXfaQc(IRugKjm*~V(q-`mio=U3veo@sBGgfLr#t~BiRLIKa_Hmc` z@?-QZ3ir1rErbk5y~pn`1tr|8*`Bm_-i7rE8V~c_R*OEc^r|3uG_t}>!Ofb)`QP*w z2d+;&Clk_&HQ^Z4^6G-7hHK{5Asb+#9Nu_bAF6Y9S4E@qIiKASSu0UYm3>0?c79oc z$H6GY!MlV+M#4$d`LevX~pZa zNAjgXifc~4&7K60aW3fKLtcgEq1)x8dlI)ZY|EV!u7^}7)k{HSOAK z`7(5E%j8MW&Drn_YDc2g?Wl#4K}Ga1N8T4gaxLbpWDTYcsp$72ZX2jp-1wY(6mn^T zQL2R#g$hGR)_Sbp3=G(A6Xv6arK$Df-`-8G_9N%9uWh;3)xM#-Y1K<;cp$Q6 z&PS2W!+{P7p0o&@vLgYGm8Gp&Ikl~U0-wV^0GSrP|U z?_e6`(yHBBAd8ybwSU}BI#J9vStitJ@O~6N?*7RLguLyOx%72`nTo(b1&4S+TATK| z)cQ@mUNV^eU#}Rf`T>)rvQZ-g4w_^TqC*jMdxVfs~P}2Aak6C3xGB zXvd#k=cD2)c>e3Kx_R;W8p%NsXZU2c!CCwKO@4+q~aJRdtG}k~n=ZfwV+y=o@Pv7l*3Okn&dDU}J_F@p2w2etHu2LIU(? zNYeX9R?n@=k%C8J;bD6cmqP*nOuRB_w*ow9HPFyrM@qcs=Orer=M-mw04@s+E0>ML z1WGn~Xw?3-$CnIxxP12G>4C9Jtm0oWmBB~g4 zI1|>LHuz&AKMI&$%syaSEvE;QHdZOA8cPC3nAsj{Cj%#N;>aEmY03{lk2U4J@;6An z?jwBMD3rAZPKM{^AYqPll+HvE8f9)=wjp4ilku*Tbn7bN;NaqNzI|(fTe*hP)7RH8 zS5sGa#Xv;7p?x53IbtX|2i2LVz3u)EgtPVE7#}vf{U#X_MXalmKaVkCbJ&ui<$ZH* zZXGOS(>z@@eP>U1&iPY}o>i~yl;meSk)$MSHC-AEsQ(86J1g#KrT$FaE#_3|a8OGY z@?)bDBfsYL3DSm z0A#CJJ0xIIBVVn!lNsZBcnKV1%#cc@@qTfq1_6%XjXyO1?!d$1=FxEFwy7TOpMLbYL>54oUw^?1V zs9Il)@#!SrzZQG_!f@lHh1Z20KTO*Y6Q4N5oF~TYJ#RS#6dH~wrDDYT4I=RwizGr5 zj3!~G=hsb6g`WSyBNZ1bOf1o>sZNEeYv%J~)+pWSDE>PvqM~3R!#HUXJZ|277O%;ec2?Wy$z~YqsD=UH=*-s zah};+0C89DcNRaEK?9;=f4w~z>`=36eX`PCo`t*Gq@bm>@Q$VB#(>7cwFpUY!Ly-K z*k*9@*!upr9_im(SzRnBE0J{5z?4Flx2(NvI-63QsoSK`LPwaecS^4ons`(!?2%@8z+3Oi7-|jaGr4F85c5%{17(MvB2_E0q;`SQ)(@P_1~3 zV#%co=kRQiC#T)yzGT(@O{?-N#&dZ=&WguxA=^tj)Ie-9x3GaUju8q=J3dEhAU-D*vm5UlC&X9IF%|Zos2T}x(((<>T zuD(r!FE%zd!ZOs9jhMQsq%$EyeL^cxjITA|{n{-HSqx1EtmU_+Evi2(dF+=CXL?`F zdU2AK4P~FQZI#Rmxn?JXT%#pk21iQ(ZqZnFBv+R$9Mx(wts?%~&Z!myoOj^H0v)`@Q$3 zLV_l$^zq%cP1iq`A!U7wQEHum-TSo5T@_jFq4C`l3Kp!8ozWbGMYr{+klr}j?%dp5 zNpNuR8%xdj)-@L!7R&j$W61p!iLn z)rLkTPfIxuMy(%k)FHWI#5;}BYPJRKQ|a9&=U zgTyNN0X@T-8Auw9kTiF`@FhJ z==qS~x|^~d0@ee96PMH)#h&UtStO;6xrx!|Rt?v7+rkM5vF&+n_-FD(V`cql2=(STx` zb^9{LV)Tuj4RbCJ{v6BeG?DWG4$6}g*97C9fL`;45dw^*(nw{?nuli1_FB=bBcMA%!(C3_JGpeED)QG6nZ9$+E zvskun)Z)!>h7S(rN0bz&aXp69vu?D(pq&1~u>B!iYySah`zd3gwAto}R{q>coJ)~b z>jh_>YpeSLQrBq<#-qY`?v#*K+(>F#+QU(MAcC#fIc?PW)l%DE5yhk&+`f;g)EPIWJ3KIlkFms`C;?)mC4{7 zx9`(Kk;&Zogyz!8Z!gbbw)ylZ|805e*+F)Ezt5p#Sr$o*&^aHOyB>R2^Z`O4dE(<) zmmVjO6L~=}!c#fBv}*ZACKlednUi=MrT@M1C%8gM*x4BzScJlMdlF6yxD5xSAZV!E zFLUX{PigO$tXTDSj|D2s@7a1({ShNVC9fwYY*`?$up9<~{N!AUNVoeva=bO+NOS45 zI_tI5@#|bqLCUoI!(U-9$jT9@^_z9EA6sr_LlbVDYsRJKyLEepBNJ>pL5WvH@Fxv%z3w0Iw_gAJ?VDNYxb2p<=z5(k9T~SiaaL%lhdulJzxy~zf==!H=LC>zyrLQS-{ThM8Rd=XxbP-g?%md8wRJskL=)GF(DBcGFA z2*4EX9g!_3T`zeaH{@q{&?ETuA9iuoT{}V7Zz&K+>jS0m9^=#Ad)u>qyg{%Wy_lC4 zNZ8ZXCSsmfT_Xfv=N@Vsg@M)Q03>+ZqY~OV;W|mg91Tf**7kGU?vIW8fV}jwtzvZj zW_s!VrjNhOFSZ!ujr{rD1T0Ehjw zwIQ2L;}*Z?Hm+r8o|dp;)VVO?|Q*0`oh1=<9!XI_kR|>3Kjtyjgqtit}{; zy4A~NZ|7rh;rBb6s&@3MNiQYRtR?UM^f+tk?kJBR&C`l9n&12sKJ}{~J1wqnLI23# z^pdxf*B(SO2h?P$j_>{B_42sq)j@K$5}m;ts`r?nE{kflbRo~HwyI&_#?5GfqaDC~ zs`WtLMhiDOk=omLJH2VOA4`f-aaQ!2+ot9e^jR31JsrvJuB`Mz;T2gzzwWO-EFZey zT@Fqm4moScaJ=qzo<9(tSQx?&`OF44wE2yWsRFinSQy^~2U@ z2b(;4JEIN_8VUxgA1K#F5+sBuwtRvDkM|xdHVeUuHo{(Hwx)=T7fe5U9ju6X-IOQl zvk|wSi--PVgf01zDNcRJqu;0T0tksVih%Z$n=!3e8ym#>R?0<9J6m(_rMYvwmc>lZ z=DfVTx9TMt_PSaPN{xksqX(T79St@&KCLDc=6%(r!*!Y&MTD8cBLz!_5RFRU`rG z^s6hnFE*D`ZaiIBHP$YENfDH{WsdYrnKI(Nf4Ds#|E`$%>;R?`-jYmY^y5r;`5`Hn`E+lVpMoi3mATI7i}%zd!*%G z`wz|i&ijUAVPhi~&!UDHXDju14QXbNuamR*L;;nD?+RogNbBr?gBhxjGPqxf8klNB z5PG^kR_U#XhsU&xJoK{5^?Z7@6a&v-D$+iQ+npE32mUgI3KD4Fq6F@KK7E)<=QvjT z`KEc<=OWO)_j>tl5q*(lUZ*J~Rs`lU7a7O49GIIIGh3fhj}T7}6#OY{oOWns<1_V{ z56ry_l-)Nb3yKYgEI%J-^P}S2yw%?2@>jkd&5vj;EeY0dwV4iuuH93)ty4UDVkyOx z3srvFF1Hks``ly`fySFXU3W`sm+rCwQeEb}DlcJ1(dqFZvwSxdU^#M5S}xO;=-nzB zK-Dt(&>x{z|563w7tn)Z}_HPJ!~boxp83`00mRVK)ag zw0XwAtk`Pv3;2l2R1i^zeAl1LT~#XH(AhqY1S+V2_nz&0EO{XCHOd|3DG1WLzE7@Z zcwPf>d-!a=v&2NCTsTvH<(45WSBGc3`O0ec=hK(v1LywfW6fHxTfa<^djiJ&fch@K zuNiL%_#H5*zENeNAqOeV@Qwk6M^-94_&})qn9PO=Y{dE!Af2b1g0kU!?E_v7dqFF^ z?0MYWjvP!S$B2iZ$(SIUGg{zzIjQc-q|-FBHdT|)pHf^s>*ur5=Kq$BjSWVXUez{R ziGUsx@h2*TlPL3Y)-*Gn?)B?gX6&%9Kyvy6@3j*WXH4RobvN{9!8;5q`o$TV*{!J! zgS!ku4XzEIpm&zT;;GjBQ6IVh4<%(_n6*IUeOgdFg#uZG+Z#sgjmpr(ZYmwJz@?Ol zhN@c6uV21=fz`uc){z~2ygNK<-nlH69*;c|e)!#&cDg%KMtc3QUGKZBshv&!P038t|z!K!ZlS zz6DwugUnnNEyxK6d51Ko8D3S}qA?=@D0O&N)effe(B|joi_jU%LNEluWmP$^O82Jhi((<07$Spv9tr~Qh$J{+|TKT8yc zbrvgqhbgTzRgnv|&JPv@wuDo^rm#96wPt87X%PcyVjA_{!?ga_!Y)+H>w?}MFZG57 z_D48gqAy~1wEu8=d$h%h8VHKQ=Fv&?&xZL>>2SzgvRH?)jZ6>p)1+!=xYp|xPEqq~ zp#aTIO$#Jevp1%R?+wdE_J{A(S+w!#$LVAfAAS;mxvgRQXDH&{VUgm8wL{oP@-zjF z9G1bHAU{z$QKe8k)8P$jCc_oyRx2GviG^O(oH%mtas9HN*Uj~0`)X*!2yF^UtYQsz zs~w3G7MFofcpr3ho*pL(KP=C+csEW;Ftu3NDsQi}2X3Bk54Y0t^Ly;&p?~NOUKtGR ze(GD)usia^(Lip4As`e)A2&3ilAF8uR8pSdhSZv&3(7EEC^L3Bt(VH}${1~=(1Y#a zPVYjSr-eb&v2|K>T!Q?BAD$F{X%kHl6aQk%%F2o~6fg5a8ro5M zL32~eeQ830Vok^B8^BGq)c#`_temr&z2nE^y`gbaygU6s_`?+sb0 zDR`mdY%<~0atIP*F~8)vBoHeE)$A{&9A)CeV*bKos3b?zS;1aMZBvsN4ToZ7e;KTvDZIY+@q(;Y&J< z|42wxz5)gND-!B1REZd!*Re7*O#HTOE!y{$R;$MZJez7w7ptl2bGEkPP2b2at~QUs z?eQnss|>ldb=@SOM!?l=AtWSRMuZIrO2EO!#%4Di|1Q6tz@qzf6G_Ms?N%_Tc^GxH zJ(xP}*%e9nj2qFZqd4ax1`*x0*1m+g&N<*=6M1gD3lsF?=o;71sUsl2d_)%FGLORh z*_a|?U$CU##s;y^HTMu>nBo>_Wr5E}Iv-+cC(qM7RogpH!VyNdn=i}C%DS*^xix76 z+^|*z`SR@}bF;=)pl52i%d^7Qgs-c^s>GG9(>^6y zCn5xvb!ffb2&?Nnt;~0=+hn0<6KYz@$kF68C?BdZ=W9&YhUUAqj|O*vZVAG>i@mV} zFZ2l9tv-6cZT-rbRR?=ICi;%gkN2tCS0h&txcvm?_=LnX7y{ZaA6~F+)`acM3gB8q z70qr6At56Lh!={A+1L~((B|fhXkz*JAyC~Vd+?yq(^VRsnv?fNN4#iaW}zz^4+quxn+3$^&WXI0G?-+ zrS;*gK#9%?V>_dRcm2@0QiG(*X?Ls}6x!gl`)#1F&qJepdf1ge{odtp&Uq!(wq^Ee zkZ0MdxTHkz+w}gnsj;!p*QBI^hnw{n&*3z_b5eiNk%Rn(Nn^3XYD`)*=}OI7xcPpb zwu7Qm6=E3ByW!^pdyc&jt@uyxDjIHTrOGc+c9yjWL4Jq$(EK35_zTJPt4E9M*YJW% z15X2n9NF2~5P)Ydq7$Bl&m zW$w(*;Ulf_PEQ^*3Mg(zT7{lKW?flvs8>=Qy`b?Sp0*;qJZfFaXtij6cs(n9Ed%q@ zAs6E)V*c5M!6t>BtevtQ6ERgMWQv?tFyo5r?vPKMioKd#->n`XDt+QE~tsm{3J$yuc*{3|t5 zDJi|oSJn$Y_lJ)y&rkQ_wI+{9`@glxa*Nw`Adk<(&5K{IVG>#oo#l@C<6#E}95TE1 zrw1#jkgQ|BC7FXIG=bW~uAx#-rP$Q4A8QW?gb|nRt{J|q)8n>+$z1zGVbAU-wvM_S z2_`wp`4Yx&aIcQ|LZF(lDR3g%N8YY7qhze>l^2?G4sJS#0euCp--{FDjO|nl&AVLm z5A)T!AFjc%re5q!2|*d!5JcAn@ygIVY4;5h1HfntTv6QBypHWRUTKFYSqJH;FJiQf zzOzV+?GJ+X*yqI8ctBc7#Gac^5m{Y0)<0GhdAtw;d_utpnaAEqybN!&;$dH)3xK!9 zMfKgeNZYR@Fk4kWie7S;=R3j>{Y(vyM&)BeTT`n8Mq1Mg@$Xl|?+S_v9-VzS48hzYln?})*)LH{5Yo~9)9T+!3YdA^d7of zJ_QifpRfU>%f8lp~KTQ}gweJ=KOUKR?(c2;LHZS0c#`J;E;=ub*93F4o}i&x6@yyBgNY8BdEL9RdF*Z0U-Hwrj$(v_emf|FcdO&%nYyiBTsZ8n zJ+2zu;66}7L^tf(^sBO07TnAG`*9r}mN{b9wszz6s*;pqNzE6@ z%G%paD;S~fpyZX*AS5{Q>z5S;RQ^Ow>z_QDpZR!7g5YR8zD!_JMal@0jv{5ZKO9N~ zT#*d-<6!)+x27?MBPUJ^*F9ERuIowMDBC36t2_R3u-z+TiUK$b!xxJuEl+qPB#a5YM)D07+D$Nk zVH+_OvqXLZr+y<3QpN$*Y2ytH-wCa?uS4R7*OfNWV}!PZyNhA$?9fY?KZVaC#ZYJq z;LxHD9tSVx&HMZK!|Y{Zpc94140|mcIH1Kyl#h#KvG|ZTA#x9eETnktq)Qcp!Xytz&>ZyXFzfs`~2HWrP+=;rH`ZGded5DV)w?CrJeScdGc z2ow~eji*uh8S_t<0=6$fBvLvesMOu4;8&t(-_fLbRQBjUXU0W%(k_LXeTG$qW}y1k zp;uL(rBZ(_jp?xY&2NdHi7?~H`lhbdy@_dj%ByregRF%q?P@PqcH+er@>DY(-dx-AQA0!sSc+E4Gw>lQZM#b}1d)j0$39 zH6z&C^wt(xoM+o?bP_l*tL@&)zf`#nkSz!p)xT&!hZp=Ipa0&3LArnjR&aRQ*Zy?H zUd0!6_Inkt>FYK=$JO9_=&$p|DN}6|IxNl6Y6rIwy;j`no=I}qsX)tV>P(%*d8w_) zxtFY0ts5#S9|2>(I0)X}fDoqygx&~ak&LSR**BY=kqXCkQRGEf?|p^-pj6s09|{?X z2BQ6j3|>PNGC1dZ$tb%^6(JWtDUO&}B2#W+yn0TZ3c?A9L9f!(_mIz)zBm$ig`;D% zM5|{58xa;-G6;d1QW&{-`cPcM4iOqAfYq(djyIi|6*XctP(n$&6b#w*o3-+nVU*k& zMgFR)vZ8WD=(ozsnuDK<4JAp1w}?%ZrQG{@f(v<7|C1Svrtd4KayPOB3!xJDz;0bM zX~i_KB#nP4?zfVPn~fjjzTz@6m|gND5lfFA;`H&{+B?{r+v(sz1L?#aIywy~8Y)Wc zhL;rkJ+G54GklIP9gO!%pqlftua%=;n}R4x8U@v8jJpE$8-6fS5x`^VB_YCxf?!@I zMSx{aqwb>T9ck+_n^4=XCS?Y70HvVl{L58WMm|1G}#4>P%DXPg?Vye7V+7TX#0) zuM(eX?YSmBxhCf9JzWZxYRp=hst=kk%a<4ohgT<67*>jx`c>BhIwMw{>{$2YcQW4# zv?(y)h%psBYU`D-t$Udh8fd@useO$X!R9rfZ z2Ko}iS*=!-(gb|TwvkbkvKjnU=q1F&&}3!bwc7*lzcH?@cd!EQGTmLvi_28ND~Azu zsAW)+7LZQa6c&YiEVo@Cw)3y-3g_}A3z^;Vdt=f0)LN4x{fXVC-kLo?B5>~KbQ5~F z{YskwHtvqVrjG6FTie9U~3(4g1VEkGkC!=njBKk|t;G+vWR!tmNy+8F}T{ZW51(X4SP2yJYqinn3@?oE7 z+PDlT&_>cw*2Jf=g9d^LbjM6=zWN%{le*)T9A16F$-O%+D%>)ROE2xq=Hx~SOg4FO z_r-Oo@qIPaiD$6Y1w#8xQK1lYZ~XP9>*0ha*Ceu2NE?cl_GOK@5 zM9rKv1*O-VF68d#A%B{I7R3TD%0Xf0lVH~KKgt-3QnVAuAB8(lC z1M3CM%n z2^eU@`7DE)Ck_>DxVi?4n1#;Miu!dUBN+q++FHLU%8CTRBD8nfEA`=ym`$*1GU_GI z#gY+VDm9L`ZF6&AB3t@4Fa(R!yqwZ`*W$U>Y1x-5_i6_XK8?>4FIhU~+mBB%LY@kS z*GnJl_(>5*dVp6z=xyrF5tXrXDAZqTau&61+O=t@O>N}bp)u*yBP^~#D(=hUR*V!R za}id%G+&9nvb@(h=f8E|j`igxucBQ-td~QE_Y1%c&?n$MK(|vM)~LKr$F^>UgqwG_ zZoVtJ?C6lo*%oxm5L@I+0^Wi-@xpeaVH4CPUZ|hLS9vtV2-x;r=ktN%eJXfeb<#IW z?vA$02-RHQimh8-Tad3sVKQ?~uKz8)`x75~_tEsIBC7oYsh z#*>i6Sx+sl^&5QS2+^^;BgzKa*5Nz{%!}@YfX{(t5?*rZnft}_3Z*pI*<-w4Kc0L4 zUqpRlbfn?Z?ZldJV%s)5n%EP2V%yGSl8J45V%xSovE6aUx_!>~-Fw&S{?oly|LCXc zsoJ~tuJ_^L?BbBuf<= z%HJTv>wtj3(5Lhc2o6r>^t!mN`#m}ifRr2!G9vic4t_KMnM1aFXquxQA0n47{`RNi zcRwC!TCiE0O~zAR*BJqk8uCGO?fcA5jLYTys6fNO|M~@u?W-v=id`(C8D&T>xuAVBQD&XS}oEQYciG5u7|%a z%s7ZMsXMV%QjQ=kX3w0Fxk}&B1H?2N#yDA1X&L!nYUC2OuAHXqc#Xe)f$1zXL}(m- zMo%z7^IN7;AN@6fQQgz$-D!4Zf=idljocM>o=ykMtQ64{*}&LZLe z)dHMAx+=j-+NN4kQVsunYXzCFH?J)As^vo+yLxAr6zRnZ^AHCaTz&uPNGfT_gE%0G zY_|fQOG*|Z^!7phee8Fz z{Zo@YY}jgXM(iSp8*??{;w${;*l?HcvZ!1M)Z-arC@I=y=av$98ClXp>L9egWnl7! zv_8~0O=PmO4>9fQ9|=EN3vIBfHShAZlo?(hm~1Kto1GW<(-edk17Rhew*)O-g(#x# z22)>;YGioCY-&bw+YYhSIqtb&J6YN$Z7K2Dt3^X6Ul`@9yOvzy1Gaio)O zeLSHP&FLDj9S5h$P2>W&eiiWgg|%$uJE37G79<(CgIW0UrB9447`ZzVoW;>Ykl{tA zF{T0GRK}F9(IbhUS&=;-YriH7M`~>^(6jhe!$Lo~v1LP!9Z#Hje=;(KsMCjNF+=$n zfpWoigoZY%e{R>9D1`h_6-y{>V#uMt!Di->ypP|rRAdmMzkZ>G4=K}P#)S<{!Cp-~ zGl7B1AUQfX3}<0s$?56{fbIeySc>U)VV`57V@bEm_p5Z3&8?MlriV3y$3GobuqNIJ zXc%Hj&S0{hE|rIphldA&?h#}Dn;0Zz(kx_ts`92??{F)@WBt;4M`H#5-3M7TXM&_o zRo8iWuCUEi7Tp}76_4Mkje`)hu?cZThRU01Rwok7NU2j^3s-3^O_Ibvx&w)7*Qb@5Qa^m_> zcp3!z^y6ZO)MTvb3Z>;i3vA1d-1`nymg;8Od--)XV5}hH72ALt=)|ILxgA{`?grkU!NbvFj8fl z1H){B_2AJdPlCe~!l139q@ewwGY0p9(0%V;I@)&2YjEiUp7&-TYfl&41aIaIwO)36 z_FXyrOUNq^bmBkq30|TRtJ`<(-e!0%DQx1gT2X{KyL^zL^5 ze%o%=y`il`7mJ>~I!CfQNATvPB)jX-p1EDP@SsZacnr=Bc5blo!%JoR<>h5j4?tk> zW@p_4+N=YL=uHR{*=c^J+JT-E&wHqCjJ9mMc`gaaoU2zz^0t>|RBMF(n5IB7UtsL| zsIH?RMx`lZv~z@)3Y?_r&c=Qmda!F1a}gTiT;&h%+za99Z79M1_$w8Whf>v9n3}Ei zG5alSH@N_^7_(t$Q%a*bBq&G_I*uWeT*s_vy|k2ZWdC9B#<|+hJ0UYs!CU7=rD_*o0RBl#>;qM;xS1%c1 zy27V^w_^@3U%piPU`A^uT8yvSC60fY1}wCR?@6Sk=zbd)Nj z2Z6o>QOth#o_h9clV2>5$U6%pjDyUeE$O z_l{eIZdRbISnCsPGR2l=Ea7B(+$I8?j;9_eQ}84PxxS|=ObfyN3QmC&B6fGL8mMS> zdhvdGUd}wZkvv-AByZG7$5xY&C?3Y!=JNIb@B%$&0nax@K}`8$?Rig#<_EYwNWNy1 z`fSibt4)_OxO3X;9}tH4ehgI2=x>hwk59YPxyZB=jOlId2N>P#XV~VZN{Y0f7N~E5 z_EK>5GSF9Y7XYkqgm(8IRRiC*hUp@KvlE1ht27JX(1Cw(+#gI+#zk2jIey9EeOFTa z2m-6+KGL8B9{%Uj6{z^-rE~99GQjcaT9WOou1ln4oEX`lJyDS&LfX|;WS+bsy z!(ofi*}49CL^vn^Z*z7>zze7F`=v!0BTK}!>7%D#qX&Pfz((IE;0d;#61;pv()&o7 zxyKS;hC-QHZKRTN{6V@7yRrx4D|^FK)i7-H)a zRFa}`B@8qG2Dzazm+pYo4|IJYq7ApZfSdbFPcgI8D zcDhWqbdG=91g=x4)g&w8UxzP$$YU*;e;wpu5WH-6b?FJWV|(NK;doyZdJC}EQ9 zMvR)qRcfdpt7fb)-LEbu34WC~-D&4=K4xR^V9w;{!5t@IELiv3sDr548v=(?*`Bv; zee|PFN<;VlH^j=2sQOe6`mUhyMnuVCbyohWpdUU6)NI#LA1ox@si|85*khFBD>-uH zhR^Kx;EWMWlA^H3j~h^&MB!TyQh$HCU_fwCb-G|(>PqBzsl=!`cI z9D^OL9bLqg%Z%(U^XUMNTF@kQ5&Wb37Y@kP8f6u}hw|94K zo?|ds9@DG9&6eDjZ66Yupp}%%y!^6nsUs7q)dDobE<4){&DXQ46L=JXpHA6xo69!^x?Vgo)6+M1Gzd_E|B2@4Iy%Z{96vKhp0$Q)iX(WJv7~ zcpDl))t4V}hlFqKGl5+7Xh~Rj921w0wq*gfVaWhH$8ro;3d;I0n{}vo7YO(U{RE_u zwNE8(ml8@OO4Z)zY}z1V($imm@zL>B*>YA@aT0P>zuUV5-Y)_C9qX1Cmh6 zv@Cd2@7n>vGh1%W`BZ)%Q7-9RW@IJ|)a>=OQS<0n2c}0u$t_;T-qUG<+sZgyfXy89 z2loJJxd5)JYJ43%u11jN!YLriFobXd-u%dL@ZKaKN`Kna`mz8`bcMNmj&!$cK*>6W z=g$jX)9Y>PXUu45j+MwP7s#`NwkZ~IJVL?4ed0s_P3*cypl4(}#lz|OH9gUTd%N-l z!h{`i?uP@(o!#YzD}z5*om6kU*4*pcI}o@z6a_qmhh9lGm~t!Dq!En$^Zjx1iuhBQ zIN*?sQX6uU{VU}~TRF`+bkz|*l6TU*-g#uR(-|7*o6@$4oU)Q&r0X6w&7bZ8g;}Mq zLEMDU_fO#O7J>kay#9%zxkPt(8g||gG6}S_^w6Z@DJD8v^3>*vrN#~}3|&H9n3zYX z>F&?Z&l`S^EuL?4G^-6)XsCeZq|?L+tMjwN{i>y+5(_q?H??8S=#o6rTwrBon$z?t zY{k;}B3&XLI1Zbexwm`*__!1x5kds~uc6gVBA(}ur;AyIBh73)uGe%karL<~W(*(s z5E1{-gy@i0>fezW#D-3=Xk)m@lh&@=!IRA$19xqXw|gmUa!`qc+E@o)%I65yNEMNF z=L*By%_UORg2^Gj>tn$8;KN%pVJawahzVw}HQ1J)X5>GhDz z7~6jzEsPECXM1>+H@|U{p%xhxFVZ7D_(F$TC64p6POI%=O@Pnm0mEEDo3#~%%5zrY zTiq>sVcX$Z&D|r;#}^SRCm2nZw(tfE!`_2B4p_G>OK>wXRO7umSC`bJFjL!QLKks{ z`sR+_H}lgET0e(cgA)+0bbp(S5b)FblH8!`U3?F~;VI_hv(z;?DI`$9xR6C4l`_7T z;_)oG6q}aX6Lx=5L9WP3XTz_Vx;dy%;BXM`;Zr=XSvOKFcSUDW6m@Li;X02#sz1^-HOTj>R{xkz>`|w;n4NFg zX-TkZZI34E>}Tp3RW#H_M>-yze+Jyg)#y%-N*@CoaDKeHBCoqI&du%>)jM-)8oCi_ zYo}x4U>OO(=>Lm_6@#zDMk=O|a$aZRncv##JL9rBbt|o0cMQ7_IOFv(z#{Q?4xB3- zsXOS8jPbVWkI(Dr$^HF`YII+b1zssx5f>qA?hacoQ&7@@BjvzCj=A({>V4U(a!&9F zSED@@Vz22S6nY|kNh`afTG|IKQ4SIL1LA|D)#GuFF5qQXjp8Kyrzz1206DyMt}a2w zs$~?rn|VwWk{RxIFHAU}j&|Ijd^YRv*PcfYdRh8dX{*qZc(>5c(fpjH)=vE-(@A5= zIVQD316%1qX1-&cWjpPuD$RiG$Qi}yJ$_tUn}AS@hfkW#%Vv0X0GbBRdtyU`u#);R zA!7nj3%hIZ!^_W%D+UNh5o#+cx<5G0RsC&`VXVl$DU@WTXIW-YgK>6o zVI8CH1StIsrXpB9`vXPus+k<9PLzM9x28V3oM!_ys{C;h z=W!ZfKFPQq@9aTBsE8BfPgx{*RAZTCmp?}CUs9})cpxIuil)6%qWaD8BS`LP>G9*w z6uzQ58!jhb%k^SRB-6OU~+_UbY1ODa4 zZmgCIhWpRFrOnak!k#`4n$6Jia^TtO_^02CKDVo{Nax4J^;Q(Us;o0Lc1e z0Scvb>l9E*!kjKe<5OzqHXx&{)a(fz?o^NbHB|TEX3nEDfXkb(&Fux6WRWJk`T^zT zZ2jcw++-L}6{1$wdGf^tIgwr}J~Lf(DxV)yg0H+$&%Y-(BSS}{N>5H#PGH)AhU0kh z=R;Qd0nSl_*Tga=pD^TT!eFTi7A6)}o!!3sTIM=ydR3)X^PkO*$D3l7?Vu_5gR)y# zq?U=Pb%#!=l+}JWxoM6!0G2uxgFxh4ke<%^%gr-pux};f3C+nnmN&1&X$h=knYE)( zd!Y2AJDv1!ab9KzDex90UITP;S9T_j$1(m;1UW(#tD2ri5bu1kFv`!2M~IYsoTM|( zWObV;hH(^ks7GxNk*o{)N#|hkkLk`HJ zG{IKAt&ur1#?4-!9o6mRUl)Kp*dQT4{!Nob_2BeHenKBpd^R>|wz(s-)E!vBZsHtu zd56UOQ-0VKh1VTlQVULN{e{>@XPaUwNv5ZUBp1I;epnhV6Z0HV)0Uu*!X@X(F#zg{ zvujL1!J)o`hdsjg58dww5St#`o-Dq8w4lK|ujKi5lZ!PNyoi4HQ)OZzngZL8q8^34 zBcbknOdxU2st%2e6$5S0TYh^4{D9a?_;Iy}w@AX09JU_CV5VejqS#XYKu$NLnW2Lc<$pmOo6_tuLQZ8%U2qo{}KmlIE>`g57DjmZ#+G( zy3Bz8>LSPI3i(VF@l^lH7vzx&W2(PxxJ=8&-xUTfoKp6b24*^Rq~DWNnOtO`rgQtT zV^7pYuAw-dD04;MM^Eap#zyRs5Ad&rO`A6FI5jQIZDHenexccR(ZaPQ!i#cQMN;M( zh{Q+dRaBQ4^{y@_M?27l4~Ey8Qx|#K7gSNlQXBi)nZ?%ST5Y0B52MS@s}AoC>dIi` zJ9h< zE!3Dvej>F>&zHq3TJi_7L||8F6+~peN|V?9BI-{%mW@a-grK-2ztcJm+SOGwa>=h zGO^ijMx@Tfc6_N{tPaPM7r;)NY0k_k7zL)#3XK{=Zxss<0fJ`#-k2_3$P}6EA;AOU zsBwLs&%k&IET=`4vQvZU>gIqwQ1j1}dAbSyP%DWRqm+4Z)b#ePSXiKzZor%1_T2T; z>%XlJ{7l{4lCic-dnK1ad}k`p^c0@A^f~Rj=jBEoaY{eUV?@F&cj&#a$_*F9nw=D< zp08sJ3b`UN#c7I4cY*a$Kk6jJCCse$d$cxhVOKPKqi14YU{msvUxEE{v`PeW`rCaJ zIheEi1?Za2cB|1cOGH*Ihwn!omd;#r`xk%LSHzG5+FP4_OQ0J zK7KelOQ$X?VWY`kNnL+L8CVdTh~k{gm!YR-{f@~yuR2C2pv$tgnGIdj;K8&2upJ<` zyd^4Z@22>%pZe7Md5j!G=r=Urs|kNAQ5?p1^-(0~wR;DqFL+)JAcG$GPcARX1O#k) z?-rLA>#XnLfSV1mot=7rXVS)#=n`q^=>_R|o-y~t;YHmYr`ndhfqHBA{~BCS{e)1a zGuV>D3n%uMUCI#4%lGb{RWM1qx`g5aY~f#&|9)MhM8PutdP4q`8&iT*6@y<%a&Sm1 zT2NgIk*Mu>D~?6#N~vFpFN3PHkA-D=S`H=%ECiaDYl!NnvkG{V5!$6|?JTK!z-BU+ z?)xeSKMDqhb4l$KDQjdzB&2k1Dngj|L_3l?@AucY{h^+no3?udL|ndtAl;ho1!c0( zV^)W&j^vjuRM!Ko+LC=PZo&@=O%0);(Mhi_T8E=JiRf)01_L$Bf|dO9xs#3Q_P;fl>_WL~RcxlH$lI!RP zoz>NsNMPI2BLbN-EPr3tDN;IzAK8k&KN3sNJLae7bB*V_z4GpQ%ZJi;{V6kjm9EaF zD@l~cY96)L5&ewUO@YJlxQ2}sEOR;hD)xGvf6KUr?D?e}UeEtlx@SeC_R!H=dDV86ta%^}vlIX#J> zPH1a=xY?4z_F38XZ-M+bkUpXxyf~p>;EHjGaw4na#FknfS4u_2iSbmL3=$*AsW~1q zja__s)0QurP?g%}<=hEQrkyffdV;PmBONt@#NyaUFXmyt%o`e@2lik5kxZG)oQk@s zD_i0$h1S~$K`SaL`K^3%B1|*|I)&&nD0gFt=9hr660+nyE2= zPki;*ni@aiNlc+aN}rH47zv1a=3YG-bm3X^xORT;IUylsX;&e+KYr6YZ4v=`ba)Tk zig3CUL8LZ@0WQTv-fPTIHMQZwd*%T^DLGnqy~saD(YQ z3J#8M4bP?|<0!(WWNgd)L6E8%lNAE0^(y9L$9W~mll>|OQCWeu^{{*N`3c&r5h=e3 zPR{MQG5q9r-8)vBt>C=0S2sLQB@kA+URA`e6mqv?_f$_kyeZ%W1|kwo>lMBc2#qSc zK>_l~)zIDDG!e8HRG&c#3k7TL`O#(0x+DbrG`e?1n*v3v>S}Hzqd(Ndv7e+TEB_^sCRGanG8wp8L}Tg zzWZi)WY|$tSj>{OESDF=>NJ!l%+9Y%(H1YfHV{)K*dUOQMMEAeX|{07jFfDq9Ratv zuB;K~>w}5&$^Ya6u=Wb6QSc4C1&tkOMbOFRiga;q16L+4AuZuP0NhP}23A`zD*XKyN(pzO+YP9?p zXR9`_&=olr?3zBSE6Nhgly@s&Vx|Uf8CfL|)~`{ExtqfxS;(6yDG*I;>yHJ?sQ&|U ztErnUl{Zc1a730R()`fVOG13$hR4iwCLi@`=|OEdE1pe?{(?BA4LUH5SK&q=8>96n zVU{9DSdHLf%FK%Y=ES$A^49&m1kb$_oKvpNZq65bF0?Gn@>(l}rBA;pYQWcd0;6L` zHoTK*S*#KrF02!dN~l|Mkg5L6p+sCHk_<<`TxmJRS9%;yNEvWR_bpGDZKgqxk%wwV zR)SOV&x!z#8NUf+6v<7bFl7oF4~A_^F3C-g@FSWhh72LGL<<^Sn}4=`;Lxg^`|P1B zO5Ce%b_K~iuf&@%9XDG|tZsg|X`P||Uo8N=5C7I+MeA5ojPcfJ$7sWl$NB2)8?HZ8 z3&>Q)INpy?Q{#iTvBz;xp;PlwKrkZaIepB%h~ z=S2tVc&#HiXyuU`E5>I#+YeBBW2q*xBBb(P`cu5fP5kw*0!MRN;dX4qd^JoclG6=s z%foCFJNdDScS&6T+^%V+GVkX7|8LE;3tCC4tEnj%qC10XS?Uy|Wo(>RSCO=u?V$w9 zZT^AAH2&tYVQHtd0_Ty~(&g!l0n+~{k<3zpW}#(Dw)+4pMj~-kt93Hirlzute4uA6 z!7idk&db3IAh}RYQo&q9-QVLMtk%>-_qls_ln-cx%$!`@4K}aD*owK6(W!d8KhZ>l zWqBn$W=RCla(7Id@=(cOl@%wvw+p%PGC&jmt;&+^{QBk6=y6w4cX@hVQd<)#NG z|BFXM$Hl5B`r29bq5OCc%9nBj!dG!4&h{zFsE%NE)mPcx;ZQ4?)(PHi~kk=N~( zxqIe5y0;Vj&_9zo##&;jwz)Idw2{(<_plj%qLWZuhukxH1q({$N7s(KmGngbcn|4zbdv!D{R$DjsNF~$ZT1+8wJ#`c_~ zo+s(sH@Su%M)I33h|?Bs=i`)3Uqwh;U4Sdk^QA13qx1XWnj~W16J6f%UHs^|Hfj1o zX-=T}!}DOWrJad+G3BO}GMkR+iol&19U+~6=Rv4oHxO0JE+x)KAkYjAeQ99OYnX0ZZx95|5TNBn#7Z= zP0aI+P6i_a5|H{yN!;-@e-8>BW3wz6KTJC~#Xv#09u?nD4W@D9L>j`wGatHe42A=V z?EAInj*|4A_0F;QN|`EB35KZBvNQ9SI`gTFboY+;ABC`X)3Ms!SJp3GOqQzp^irano=Kj1je+a#~tZ=z@SgZWz(9Go& zlI`{rpj~j$BPChOE(*KKSSM2LDPtscd=AOfQjnwQ~si+lT zJB&$fF@>*OEg5{Di@%5NlG>5marKJ7*_DlcsIzG^QGNW}Zt?HxpTLf3kb1qd+bNf? z(;`lpd`(n*@osm|dswXSJE{DvINfrAbowe~25-hcR0%JqEq_e5R3i)6?x4e^|b{pgTY3@81+mQDL#PkZ?j( zZ_CfxmTdoQo&zH#IGDfkjQvr{>ZZA<+G)5Z3`YyyI-SgL(LT!wnt<%dH}?7Zh*}G* zU7#)R-}irwP7Ga}Lh_6Zhy5_ac=iRms3Bx)O8`h+ry3)bqXVQVCiZs`}DcV|cROc_ihfefR8WEJ`&zPn^+h_OLmp=f-Z`S9fXOC zQG3pY=q-$R4vl#ZfAW}d&|Q&JA7znt#5BVROIim@qKJlX14!omqi z;QDo=amjWtB|dUB<(E*!P8tTNdD-0>buo@Qv-R%9X0(<@s`lNQS31j7yTrPpX27NNLgYx{ZCNkmv<&UtePy!Ijs6ft0VQvlDg9L?3Jmgv3A4oSq+ z%uN3$rKQWjC-j(?@uK^C=D@lpM+@*+o~|7#Uyh_8KIi^kSgsvvj(4#sYXco3sJrUW zs(o|G>HNVHrJ?Q+0Xt}=5tM4Zv>TeP5Av!mdxOC{3R3Ry zU8n74yXWI>?(U!e(=&gv5xKsNBN{_RW|G=)axbBiYU26;`@>y+N*|N_WSAJ{7rW|W z`(?UU!xBk5lR_F588| zh+5O)8qWRktBLTiI1O8b`grZ{I~J{sW_(Kj$$e=uuP2z+7oRZ}=>cmnB;}yGf%Mv& zYI%NI(4Q3cfN;T?qz3cZUUwE8e#48qYR~^DsCUX|?GV}L?TU)ERj`tYrG%MNMY(e< z@toj0^y~j@-7_zR8Y|CKyxf%gZ$zXrjw#mh!9g5k%%6hbh6l+|+I^mHmr^;5)NVz6-rK9aH;IFxwfWLrckyAFltfRi*of#*qbJqT9gS8ZqAE`^ zjHD?J6Q-wxb`bD|^Omh*R73Kl2EvNYTc{GpIH}v?3%pCueK(cU<_c%dJ+6(W0_uC- z`KGIU!vtTGX6wvHysg_m6gVqn%(J;g+#a3Hqg#mryNDccvxMq5Q&KFnw47@L-nI z_okCyNh<)nz^__?{rcM!Ug^PpRvJwJACj@;J(yMd*20T+e40ISE^;o3rs; z{U=jVZwBwYrC{Z1V-(5TX|;0oomNl|DQ;FX5u?;Ay!Ncb6MqTg|JL-c5Z<~1qBn_# zNmAaDy-obrh>@S%PVIUdpMwpq4}4`AfL|Ol`eB>)NR7#q^Ye;gsRxu$t5!{ok99IB zW23d9ka=bnh=TSGqt ztwmnNlNWZJ}(Hy(9Cey#tKFv0<`p@9xT2GNmq_S@B0h zlgX}nO}WTp;44b|E%`d_m)k0ewo*`Jr}`(4xX@HW^7`;QE$ITOqYfwN{HW0B3hFcV z=mD14`=dqht!6tuRdqD03~6+Vx%b><;T)LXQjL2WM#N`XKFWZ^?yfAIc!P z`AnCR18&`l;`+bTj$%u$-L4isNzaVJcR5TVkhgj~@Oz#NX`bGB(Pna{JR=<$H&dn} z9BPTd{~991jrHpK5RE=h@CvbZlc%OL_R6tMh&QAA?w;sRbgmc6r4<|wzQDN+l{U-) zL}g1El?2Hr%XFGkD>hdg4gx_wN^T}NRTwUjGQn8GA>n}$ZT0b6` z5HFJRQ!f}o{l|UoQK4xTzK<#Z1f%0{DS#&6F^W)Qz}-I|HaYjLQXjOQGt^TZONQfz zn;ui&6emSTiouGzu5I7!*p?b(?L73L=`HRp!^bPymaDj;KwU4-61`t;+NR$`)i3HWf9sAq@({nn6mnzuinxSw*} z!WSfhte;TkN7(^t$7g332d_Qv{0=oW2EVjxjJ5?J*}6X*J-ok(H}u0 zsnr45=iNR4hbjfYP&RDK%9N@Dpuc!qtG5evQ$gPb`NyCalarR&VU!+`C-{tqoQyo>)v2Tv2z1?_IbrKW$g z$R%nIW9uogP@bO;^y1sFz-nMP>wG6qkERo;PXEK|%AMS~bk zG%bg>+auyM{VPQBYGk6df1Zt(<=;k1Km2WgiO_^~z}VUV>W=2SjP_;p>=8fa+DGeQ zNb5yHd{gZSJ~PWFEL!FsY3$A> z)RFm`Sl6pz1@|657Sht!+iPb}nM;5k@?W4XkFg`uH?qE(YWH7ho&cS9Zg6$WUNL#x za=5Ag$)dxvn+bfgD|UU!eo}#~>kdMqW0-u-_dQh{iq_GB6ziWekPC1S@XJPi@`Hsq z54wJR2YWc#*@5|W*&hIDSU_$ps<75f$-27=pE;%lxUw!;i`MHnYMB!I?y8%T)mgf~ z-H-&LVtK^nF+>C{?DpC=DRUY_HxkPJ;|>uOap2P|M5NHtq;%38fLfIQ!8%wbIlE6b z9JIBz)?N>utdUU3!+e)n4j~J&Z&ZA>#KdLHw>!~~9l0XiHjwyRoJ6c7uGa-kFO)OT z_;^*=3n2c|V0xS|Cv2v8GX?IQONt#>P#g#nSjiAvSi5w%?W*yYxrER)Swc&M8ZYiq znO}<0=&f0e-+)0At$uq@(tS80^<`59H(-ZFpvAUlQkgnOnvfltdLeZ}X10=t100di zh3$(gQ3Ve(a{MAIAI*3Mp_MuchYpcZw1&dE=70csBgs>#4c}l*?h8VX{~3)I6Sp(R zolHxyNyFOd$!T6$6@-L3`JhaDsuS?5iE@BGX=^Q&-ZsxBH&Vm(O8fN5`u>qd}zH&nf!A$hw6TW&OCFfF05sMv$^w=f&F`9w#*xEKd|&1p$om) zervkJfu*>hAd3OuLYEHHRvJjsdQwltNo;>`m~2x@OZV5=R7Q_4XGaJw5Ro}nU?(Uy zhx-Ij{4Y|*U3MOF}%7#6UL`Rb}*>rNHO;MX_$4sVI6Xcclih6L= ztCPzqM&|9Wxs%4kMabRP&bZ-*Iv@0HZrER;R|(WqR9Q@wqt>q5PG)gs2L+8b_Hkz> zYu}mIUHnF_d@}tugrhH_lhy0w*KU(vN}uD;!mSHyK5_cpCx(sFm>eYVy^iJgJLXp> z`t7otZg+_5^ci3CijjRQujZJPzPoK*vJO&r~DQolD{j)bNkhuJ^&z4e-AveRrsq}3l-q2X?H4}KM z^No?s{k3HxJ!(8_45rx7$b3tJtl~MoptvynmH30~Dgb02)!`jh3yT%&_KNaAHx8Ch z^mtD+EPDxwO%7dT z(_Py5bbA^mC`uV>4uN@>dl5qx2f!nJ<|EKz7o2zJw7hF@yf@&9M!iz^g~%qC<~4$LEr&U7-E> zJ|G}=&dkiy&mhP50S2X|>2~!TAg*C9S?cfVj-6|$@B~m-PnII~4|*I2wMs{S!M~3A+h`=eebVyG)TSDlG60vds54X-#r^MsCA&7|Wr0~O0&^gDO z`0sxQ2tt3hYtsZ31CQIsC{3ER>U0#+<1qhuI$e7X7B#w;cfyr57x zf64@#y{dbCEcGB)G_|Fuu+u9*RD_e?T7)f;F2H zTbr*>Q?UUe8na6Q^Z(f7%gT*sa{MOKc^D+ePU?at4#|Xs=Ka=l%nN4@`_Avdmu5oD zAay(coVb>Hpdm7J*f;ZVxth+f;~c+VhjN_c)zpiNOrh(u{_V5V&}70`Odo}u;S>AbsQdemq|%K~A)h0*CSSkY zHK%av`NiziM`DVS{GcTNc4H6suJsD-X`fxn+vlu9sQY%|5a1(JF8w9Rl*zpG65r_h zA1A$B@Y!YZmp^kG(T$7qRb2NMy%iuDzZ}c1zlVmyOy52*Ob}up3*@;i5k*XFgdX2s zY;3oC*wXgCqHqDt7OG>PJV)>!mi%QM0(kn1l%mbA>Co4BWM!zW%$yws)D&x(>*ywf z&O5|MB(j9*&R7-|Q-he=W0LZ^Fw3CYy?4h#c@KA4*b=hf){Am}{TG1Jz%D|6Dw=~p z?)&a#Ac5DLS1j<*cBDBQxY0(?pnbF#nW~)jv&w^$5&Ih$d>$N`{%jek`)7S6VW;_s zgRHK;+Gxd)eW5>@!=K!6G>t-Z?$Sof#AF0)WgDgDqf})l&Gzq#^clp10sAs+x4R=b z{wf;vRXP?qMGi%Qm3h-d9U|(5IzzJHC6{a_^bOj>lyXnL{xABMXe>Y8WLYCR;uob? z(N~zlZoZAg$H$9`>r=RKxuM%(O5~T7r3U~WF)~|h_kkgICe@)M=Vq(7ORlc2DAt6f zqA6ywa#BAuH26vRR+PE?38|3)3Fk`-u zd#_j~FwP^U;73x&E@b3=o@w@2ySC}U6--q9g}j}g4x{C0FN>Op0m5WTcOM-AsK;Dw zxP(X`)ozsREh-To4Jgu z;;sEYZm(|~JFc5!e9yyupvhv$_iv;d;1{W;4W;cnn}bAz(C=}C&`E>X3(Z0R6bm&W zD+NeUJbs(0L|t2gT)OG~@ghPo0pNkWAlui+l86lDBlJYjx1APH%W%ncI*=C8)!p@n z;d<{rs<4cYjle#Q8e2W5R}iI4Q0E5Y{JibI3k~P`g@IoSAQl6RL}wvjRE@Q|(1`B7l%-vU}feyu50@Fjuzc4}m*y*JFFCuOc-4&4T)jFmhP z9qI+Dqd7f4C)m-&#n-CBwr{<6IWV`l`)Ef70@S|oFYn$JK-VNr92&3xC4yHs*4|Gs zDOk4({pIf|xk1W>O@vkw8R@gJqN=OA{!(9Gp9aH7(U3Yj*FskyPl=KAW$ypbbdAAv zHBP@V8>g{t+cujtwr#7iZL={N+qRR&w(aCS&%E=WxgRofXY%Fj-LtztT}(Ja@()Jq z$Q-hId^%mTw+Svl9$2uxHp`ISU@L3CSA4xku~qZi)%~ zTxHu=t?&Qi0H)9B_w2a!BVkZH)y3ZI^Ao=4n>f|=Rb-o{iN zVFO*;0Vb)G*4@5A&MbKZymHg`f%5BK^8|d+ntDW{9!s`DPy`79U$O6tXKr4{ZeGON zQH8^x<-UZ-wBgkiu)TfthxJZtw*2ZZM83JXHkocSM-H{meNO4!`UrdFL*lerGyRs0 zj83ntn^xLGURU%-V-@t#T5iBxm@GAVtXwXzF}$Vg8>qML!oyw>Y}?JTF#gzt(6*`- zFK0R?o;rlm8xaz(enHtJ}{=Lt`a+_CnlLXC!7w z@4Gw7UehJkAVVSKGv_Eoxc%48(+tkRs-(-Sxg6Odx zoVc~w&aX6)vyh;VEfZNr>!XRL*+aaw$RHk>WT)DL;GGPg>O&4Sr6XxjmTq2oS)15r zmR2Sxc=!$Xref8bT~v=lv0(3ioa?Vk?zd@J*pTe<{UE*atiz*TPW;CS?m8p11Npn0 zWRJ)bS3y3^V>lH<^%!?;DzX$mpQaI&0UmP?YW*G~S z6~~&j1=CWJx^++HSm56@j~#uXwa9%mJ zUDpl!+Y|2gd8<>J-_XQ0*R5l(@vglYhG`_0SbfYH|LYrH(I1GVz^8{+CA2_RD`7<) zoyeepg>Y12nDW7Zk*VE@*7}vdE<83>7Y@tTXT$yx1#=r`qK?v~T zdA)+sl(VX8CnuLX{cL9^r{@LT)01O=tkSrBnCDI9Oje_)08U;o1!ciB7Vp+T_+D8J z&qVow6(*M>_#MUs+mZ`3krYZKgT}<&gWLjJ)8CS|6qvo?nTj9^2kf&<#&nx~3=D_C zXD;tLezBQY7~#D~RFB^dhI=Z+$FJ@BgqlS*6;E4!U!h0YZaG~s-zX7HY6wOre8kGSM=RRu-H19>JMyt*o?Lf|B&Z{Ga8_*;3ST!L0kG>vR7N$9cLk-6>c zS)sfXQ0E}K7w1@vaW}Yj) z-%;BLEG^yH7jNtVzq6qNtvraKoqsDzXpKZebCD9B%$k@Vg%MY@?dAJqznc>)v~a5E zbvuy$x}y|Ay0L}tMovt`)1czFplH%-8w(lW3r33>kq+xUpHn1k&HRaOX_$bo(74c< zD@;u4AVXn7M6hta$jEdrDPnrQAQrz%V{_w%o5~4YLBSa} zC45Ft-rV$^o428i5O!10a|9VFATRsy4Tk!9hxY08S@->TK(i7$wPv3=uw)WwC4j06g! zfi|J#ta6(PT}+e@!1tEV3hV0=tF+v%Padogtu&6(7Qd?GJ zyZdw%RldK%_(v!m0KZW5# z8EAf;EAa=34oK|FFN`Y3wxh+MQIwNpm<~oc$4hUVP{H#-Ha>a@-sb!kBm+TCG~=A& z%T4;J$BYmn5jvY*=>DP*8<&1j;(d=<-F{rbe^yzY@S47)k zh0Gn#C(G|JKro-SH;i~Det2q%{yl;r%Zpd)Hg9pY!+|DoD8}Hm2qqq#nWg_I-ejuv z81(UcL&D3<%<&__%W$Kp1N$xN{_W#@wKX#bABw=|Z_KF(n>#Q57|j@<(J&ZGlA`5x z;@=xqi%(C-$3~pwaY9&YzSwS5+uW~k^A69#-Ai&8C)I)O{m$VNBk*y_;LfYNRQdW_ z>?qgC`GfU$Ipwp{!?PLsHv%O)109~~;z_M`2w7%Bo4xv~!cVazYjA)r6 zxR)sIi#OQcz75G)#IX9qW~1#P5eAi9bQyz8L*|=NpLNhs&Y~>C3j&53U+DOrfHMu15Aa>rdp9MVcd;e%ZHH^`D3 z#ox4HIu3p$o9~#4xuo#xFtYnFd}~G?pV#UO3?sTD^J9>nUc6ZzQ>yz1M+obb*b>Y; zS^64k5)v3!U7hedYAsJ+J~)wsxTU$d1VU3FbMx~`s>A_*2eYVSa=ukn*(9N(v&bpT z$T1g{h%IEY_~BQi)))^z+Gu+W{u#YzJZulokzDQmBD7Mg8a#T~y?e8;IU}Ut2;FFZ zBpyd$uZV_09b);2vH-v-Ax%&0*4EZEy2IbWTHj^psi=w#ogorvXP1{TQuC~q`$rOd zKNG}gecT}E=&C047%?(8ekcsY8N5!ES+CV)i!3g>cj^Mq8HayWvZfFtztno8CHuSs zm%j6q_>z0i@w4LvNZ0!buoJ(_y9D6xOyM6Xw0^#xE$ew*G;Wa3f0=m|5J$&)i z1H@u7l{i6RLQ!OW&-z~dJzbPx*L6v4L!Q>r2L_I1hpbH}pZUbh%oftxy-TaTkamzv zgklV(V8PO-E|zTZzf_2=j5ELT}rlUzy`# z9+AV=HeT?zFQ|@S)7m=tkQbIqpvr%R@DO*+slt5ABj5F2JdY#;PgBgNENvLa&nJR3)BjJZPH#{I zOyCc2$PZl{#?1)*@nVI4?Op_Wt$V*B;3UdNoAw#!;AiIN`@>O(9XN5Z;dV^s`yXw1 zn4}QQfs|QBV>YHDo!n5$c>I>?e<^2QMBOZMCVmNE}Bd#z=X1P!mVd)XlWdc6P32 zU}ecs4^YS5Z(7wxCUJy(g?!KFSvp8Ny@j#bUC)avDk7gYbp}_bu^>BHn;wBx_Fb<4c>v;?$++2jkNl930i7-;b^h@3H$2O zyRF-2OCwF1O3EyD_67$B({@}7Yieo+1_gwPUFVGNINuJP*%{T7Gy6oC78em|^F0x9 zaA3#;1OuCYo%M<`?O~7SNjWQOhpeiC0>hW*D@0p+n-VYnobu0F^@P$icJ$0iaiCH> zUObA5|HQ?o3%SatYU!_Q*_(Zqa_J;b{5aDaiYajbUm3G z8>jzT?3otpC<+U8#2zm13pU2a%ww zF$2q~q~85r<(KLe_7d|T^6<&FH_>rClJo${pYg`VMk^5*Zb98sGHRh%5Zl&VdI|Is zm;*uMzDlvW*58>F-Xn)?dOufgiSc4Azs*&WO-y|uk{_%`kluq;!n3;JZtraQCUdBD zi%L6UpID?jyEf0yZXCMFAtLmJEJtrNPrDT$p;VEz^9x^6oNA&}i;TehLu{&=Eb-Hx zv6~bEgZv36HP3;G1r{?Yii3`6t)BUcJ$mrX&bj;VAySw?lL5st;`qavrEhKey! zz6}_XgwL8sVNb}l=eT=GP(&2OrKcmWr5BbTW~6o%{B@m#JeL`XIx+_R zv%zO%VydXQ22m@4?bJ&>$eG?!KC7s?B(`SL6Yhn?5ftmEHS#o@(-J%|#YyjtVy~|8 zj6Bq|9-yo^*;Ohz>%Az%lkc<5=1**R8$O(^XSEV;*Jr_Ka8E~>_;H+ErO7V zDn2U8Sv(nSR((rS$UsQ(^V$pL)A;Z(T7T&rtK)SH=X|9zHXy*es25=!9#p;rj0mQ> z^-`LfhkK>_>Dy?NaS9+nBu@-KaVjPm|7)>f`iu7qTkmUWSzWCjv1FLf+9ZM`X1@1D zeufD00XOcy@FTYWfX!=yebth?&bt|*Ze&B+u@LrFVYQ=lnnw?KG;+^&0)P>9lFiI9aQvDRddQ>8UF8i(EK!X zQ71{H8e>ZOeF=+&)8H)&aj}^?>npNj*}JFFBWs4uqeQzP!iy`R-#|-kc!kR8viC)` z{&nHKan#$sK>>u6uU&ht58G4zxyn2T8N{EpVsQl*7-cSt&JW$&tta^&tdBFJ7*bS- z+~V_8h>7!oUCzGd^M%frP{NW&Un&UEBrz<}KarbUl5Z7tYk;ZG?^mY@F_CaKa&Hql znQhSu~T`ssCdlg?Ty%=p&u z0d`CKRH*KUWf-LuRYx>WKIz6h?S9gp2$%Jc*I1 zIwP4i9h1XXHG6mH<9pzIG4(%=xP@YScbG;x`KYDr?CiEOGBa`5l}xQ4Q&%+aY`((p zhbqy*EtVXN$;+MM8jR~IN)k1e%g?O3?q;pt56q_FcToaoH`Yd*zut5oaaV`#rJP13 zCkHO;=4V&NYTHQeAKczxgN+qA-bpn@vI3zSQjTFSd@q==wKlhUgLuk6<>DQ!Z3q&u;jAam`4aJ>}XWJc8bPSNf7~8Cj(5yP$E&s8t@c1)ToX zrQ}Cfr&iKrglV*SG^C>BW`3^pAW}oawR#sX>^-P6;MSG3%C@H!TN?3psSa7*jrL=!}&%%ihRCjU)xEn9P~;HmhL^SoMdrfdBN$Sj%*^0U92%a z`_B|iET~k1{p!VtEuwrXYW)f>r8qcQC$Qu%G^pUpQ($$OAN`V9z(Xt8PQyzM z1cEkk_O5F`e962X4x3x=de2^JLO-Qlhr<4L%qniF^f&uCF?hvz2gHG|(Qt$yALfOf z83f_lA#qGb-yq)-*5UmtlNtHwb_sK)Btn#d8^vfN+2<=gE;?~7cHxFXZil;>a>qSl zDsasvPPn6#ZVic}`Aw?t9WgON8thyjpaEI}e}Nz*SF#2jX?bei@NFrdrsx&A zeV1kD2ww@!1#jS}*v~b;Vp^Q!Xg174GKmdBowaY!BHS44oufJLzoK?@>!b z)ee??Nmu&Mv&k<8kB6h16gE($r=#1>ng_Ip?MflVl7@=(xz)<^)w=Yg22%QV%W!No zdhxkKd+wD6131(A^n_idor7=28m9drlYo6^N)R{=)u_+9`@7o806dyeya(uiyzO1zG6w|jR>@w=>$RK6c7&;OHx zekIV~t^1|-iq|O#N#}r&j(AD3wAnxytR2+S8Qjucgbaog^ASP){ylm^i!n4S>%51Wwd)onp||IFR?OJU zWNvoFzt?=%*oN=ziGY-WA+`AM$7+VAq>MsINgy?jK5ne__hPz*tT*9MgPTk5&RtG) z?;p@@KO&}vONz&TYybRt_YMTOUEL9EldYwP&$n3aia)Zyr<5;Y9J{d*1%+qrbIFeT zr3EaI3BCAmD!E1Zb-s2>iqD{IUw$I^KJCYP+z0F34OUzk^hW(LE{P?iBMZFTq}N=< zDYf7nYWm1@zvwqp>KMF7_JKxn>~tAg{P0f=zkI(|hiQFmfqwjG@3THWpCQ@jhUo>2 z*D3`p{kS(*V)j^q)Z1hNl_q>h_Y=Gkm(e^&6e6lbOyl$~q0?JQ77(U$*n3~a!#Ao> zTzvdviUcy8%y)u!DF`*|@HO@IpnjRh+|jN55M2?0%bHtm&eYb8c)@=I#ojnx|RtuUBV~hSto;kdl`+yv#}`T4!;0l-U^p?Q)!U z%%aCYXiv<^(Ou=?1{~y_-JbNBG3~vK<#BoZhfkCkHb@e468hdf>W`04hq97m=vcD% zlT?zg&CFQrE@_!rSQs-V)agt~r2yds;2XumlR1?5u&|J2eBLrQqlK>0B?1-u``keG zhH_4s6=Y|<)=P<$j4VDkH*|c@V0nsxt%lCVk0!6oibqUZ9e*<5aC09-d-FlE z4dX{-aLB4SuPaA=iGoQzt;4xwqx6hXaO#K_?GNiFW8h%U>3$47Z>EiOmlU@1d(?P0 zg<)&L&hJJ-Gea}o&6=7ZdBM*TXj%w-S$5z$M>gjQv7!?`1c-$AzRx?uxxTL_Yh+T= zpaIzNq<5007#}Gb!C%o5wMI!O{qh*A$L}8p0=vJmGS~)%#9wb z1F&_1zQK?p>u`&(B0_8`dHNMKQx)^nxc5xE z(~#`+ePyN?3fZZ5fHi$>$Q?7)({rqEA3k^#3%{p7AEEb+f^4&f4ZOluXpt{ZQJnZp z)5J%x0AKVoTz+m(`n45igBw|gYuSl3e1EQ*l95}=)HEeh$^xIf$U~BOsG2Z`8YK$$ zi)(P|Sq0H=#jY3iT0xD#M2zfIP+w6ewh~S(x713!_v5tVDdT}r9)A)@`IeHCIfQH0 zj_t)%jx7{|=GI($J}!JPHZ@B~$-$n31gFl9WyOYvL;*gPFhze#l-1K}9z{9`G*WKd zc_}0M+7OrL|IG{(Ksc%U&L71qTT^PJr%jN%;9Y^}GpDCn`24SQ1U0Wz%C97`ga+Mp{DH;b<2wKKP~_S1K$(M_eM6}$A1FPha6uz^UJWm zCOZB4dKhf`#L4+z%R)E4?3VinSTB5?7)M zC{+k}0DD557l@brgIL89oIEW&hqkOJD!L(aZXR<9M`-|4l{Z-AA*i(*XRpq9;@UJ1 zYi}ENc-%fOr4oshq>E2U1M^+v4J#6OT6ePfdddBInYWlP{-Bb8ItaOGSCqru3W7lcbK-i>Wy#(%n8#oN5x4H9n_-S z#^v60IpR8q4wX?JN$a!7GD-MbMe?pQxgIZv;-wZd(ognB8yV~Jklzf!jOx+qc@>#! z?=%|gkS$u4=SsYVwJm>aD2#NHlJBlN*L1p{Ir`chY9FEq8BTqf3%NhifJJKfxaN8CZ`DnFdg(ifX~J&*IlX=N-@IYbWX3)@J>K1mWsfswcRHveQ%^)8qQC(v#4mD zl5{_#*A>|BA1g`A43{3LG?=K`w{I*gbIh!+4(&%s0o5%?f-gVkGl-x(*)Qc~XGc(1 zp!oBrjFlCROXGvHo9m7vmpZT8kC#>wNx9nD&Be)^a?~hVYWxsJ!%OyDZ3{PSlT zrw1=xm5sYGB9I$hI2%Mf4&8HUKexO!91wY^%vyf^Q+2vg-F1 z8qC(@xVT~fxTonq6SH}9&nVB%C>(!=y?i}3cdM>OSABKe_tD6=60$uEKQH+)Nc=J; zl`8FB>mV2AJ?5Gl)g^X%6&M9|KnaZ*UnB!lSZAz&B<$1tX$#mt|%3Ng|lZaL=%MRukWrh!Lp z`m;y2zcz!TexDOkdU+WSiKKvYq`Ahp-lQ;VDm&K7N#^LQlb;w$@#LUXoe4j_y?EQt zM*sQw3q$Wij-{sh!a(Lv_Sl|$`Kh)FzFVEEnt(#*yqsKOCfpb;sb=A{PE$HMJ-xlr z57@O%Qa(ij)-Nl}rZ2pTn}k_$(x$@m)y37 z)gd`8^HQtBv5M4qq+(XM8kXNE+E?#`4<2Aa0yGu;&tZJ+j}C)#VzgQfNX#sRfZq^a zIHBp0u~S}MLiMA(I0i<1N{Vz|KkBg~D@XI+)6PY-!y|_~7c~g@qT*7eb*S(HC9x6M zg&fY&B|5E<>1hfY7Lw9j5*k<>S;{i0Cs|B46sw^y5gaN5cVjn&DM_qH%3O3*Wu-LtIwUZ$`<9G`8r- zA9RE~R@;AqrqSTV!B$A>Ryu7s=o+KncXO_8B$+J9o~s-1%Y^(c=0ABJ9>k z_DU_jZccKa-}_%m^WO5F#1}h?`xfpAd7F20|LOR=pY8}4HNsDslojt_B7W`YYU5M% zZCmjK@`UeH9}@33yYU=D{NYjzYUmYqLiW5o2X6vf=}WgSim9AgqY3~N-y#t#Jwi&1 z>o&@`4p}8(;#26kG_;ItI)_clh=WZ#ZflDLGsV|EXozajDIS+`154zM9Ms;@xq{Jx zwAVvV*W6E@i-H7nO7CB9S$>TdMzqr0n?}3QlV-5o``#ASiCW@3nDO?Ofhr7Dmj(wR zy8N4q1V2C3_)iVy`FRjGQl?bzARi~%Eh;15Nz-%f>bI4k7f=ZhF+jK8oxk{dp1K8? zTNRX^v=FBL1k1_qUq5zYPNHDYY5blw#!a%~i4d0XqM#^ONJvTunlu&pwV%iPnNd>u z5`4v-l((P9O|7`hS5sj{$VC=z63HOSgh|LnWhoa^RH{|=3k@8Yy}hqLsV}>Z#@mMN zHt%6v+XEf`kok*BVK;cSH^FnPyMu)y5)@bG$E50d2J>9C1$L}!`{fLUkS|qfD#)86 zNB}PI*&i&x|G#f= zHq~VezP!}6?efOrN>2w&j`0gQ+S>Y0L{UH!skXn{zeVR7UHOXu7#0;3^?u)rQ;Xxu zAh5dbIJVdF;5s|I88KtWn_#(h<3+AS5pUaDkw7DdIs1j^v!>s)=qTkb0@p>27H1HG zj>e25d>?Ekm)}$%z*#ZGo*&hjF9^k%ui)~iz^>y7rJbhPVY*81C~|p|E$@aewCQHH z$<6(7@@w#5X@Y0DLCU~b+i7WANCEjc>NL*bON|n1d3Et{k!a6gSnO|Ay8;CtrIec} zA+O&4p2|SxDCce%G8olS#AC^>-G?ppjlI#$_WJAQ%KC7*jStXO<`!uZ6ZV}}L~`jRJA24;=^}m zsx7$EqMU?VBaDOccNvPWu6WXIo{npGZZaZQC|BTsJ7G^M* zL`tr?I!p3l&RxIBKRRz?ie7i*_X+N=hpyb2{g>~EaJf0OT=RnX>25fPMX3YKN5d~m z7tr~CKQCrti(Zy|;kz!BSQ5_Q#r=CJiLfBJxVYBsU+`$N8k5eWkku!CoyYiUa$~Q! z*t76ZbJQ6eS$;`2O<#<>q5tCMW3Fyin2n*e+DH?=3)N-INR>a{nEAvs6s{)xp2mT4 zk5>J+)m#MHB@5lRI75!&dUF7QFNOo0&ad-sNkLs+{fjW)wBGT{jY!)Y>-IwwI z)+sGkTfe~|3(woWV5=jaf0pynw|sp6HP`?8r3;E0f_Wr6B(D{+_k!K=@e}789>Q9Q zIZLHHW$y{L+ZE%Jt61+a3x+9At6x^O%3aw;e96|uA(TQx&RlK&$Rz7+ArDK7^I;E| z&W3#NbW_I4k~Q=_m-h$aRy}=@VD|vW_SNKBx2B&DtlxvY<6GuEk_ni_|I`?FV8X?cO+t!Q<}t)uG813M3XY^cE3WzD^- z^W8r?Um;J;a!X1YRAuYhtxV|S^W*)e?l-W^rK*yeip+@#cwplIj$q7Yt14X|i(dBa zeD5FW7+kKqq<4o?MGYNEHaw4tsyZU3n$&;9f{lUQgt%N#sX?R2G*qC%WzYi?> zPjaW7f|oQOr)wUb{6@UrVY=EOk5-a7^ce+p^4+}A6)lN_}3dK zwTTUs%3BER(XyvUKwZ!~LWUC;DXfQN2xhrP6re8O4FhxD8lWCs@)MI#n_9ej;bL}6 zZLJ-|oHpNLqS*3jU?X?=TPSh3r}N?6QIQ+^1&NWQ(duYR?53T=+()5AnCgeE-azaHJ$fTwne`l&r@LtE=rcV^4mDH?to9Z6VGNT2Di=b9G;{57N9 z7ZUMkt#-EzX!m5}=z^GLl4{BfRkek$4-o9mG0%9q+*3E2cs}{!i8NVHQ*L~ZZIDjE z4_K83Bh_KX3}8$vCiJp_mNMsl?eaXKDJdX=2NnDE?!yeh#;GYMM_0GjAHY?Q$y1Cu zXnIF;1V(khAJo=fQuI%~W8d)8ZOaz{mv>;@qkeR3>^8NiygUPyjZM4um>9+9Zap}R z`^bk;QcmU|KeT(&X_{ZbWs!;c=>lrB=g77-p615{$zU-8sP{wl+Dh%!Hwx@b57$8+ z)`kW~E1=-p%=yWJA=13#&pN@`QYDJ33IiXDP0Pz;=4WOGHhrM} zK0N_{1#Q?$d`rhV!R2wyiix3TW|@8G7urb>M;`g2X0n+UZiJ;1m_AHq4^#0EzH@%ir$iN zh@ioJP@lodh%6pMqkcgl-}KO5h_}qed|rVp6CiT^!a)aH5-cPTV-v)KAOJk|YC9Uha@J3aa^^0#qrYDt>M&$BhLy;^ps>rB~* z`|%VdI6(z5)|zeR@ZJNpY(QGl-y@(8r*oBqOp=FWOBDG-e5P!He zT(Jh(CIdr{;q0jH{ohDqLC=<&YptUZ0}mqNX7mS~-ZLm127OU!X_(8|(#@IP7q{i- z@YeBeA>POP@s#YLe4Y5D*R(MPdwrjgm)!P6>@$G_mEx0#&er0{mHGNsCsaPla-`h6 zN7p4&ol|{Ur~VIscP(1C;Q8a&*Tv0cc5Pk;xR(lWNkGS;%PqJ<5RqObp*LFBLH|Rx zJOBr&E26JyavHX+W2t`zz(9kh6~)Tl_?^Xh@Y7qt*>#^c25n~|t!8VJjnhTmyL&hw zOeIxPULG>9rbaK?Cb;Lowc>KSjH>*WSM;wst@p|JIItz)@;XB&&Poaifv`uWU!J0# zuXg5QY<>R5OAZ~GoZ256Pi1cO#sz+^6?a45b(w9M0!J&U_e|@X@l@hK~5WgV44KYTt~wB>0{;T~Kbip+}Uf$?A}%(||QMevuTR4tRq zs`2UBsm8-6Hb7Glb#+Z?OUjsqZZbYTCuSo;uI~-&zre2ZVqF*+naFdWpnNIi!4bp7|E?{tEC;rgq;Ka3|NY1&QF&tb;GpF$m z`5y{MSwvAqq1xdBT4=l8V?n(4``k^1_yiIFQ47L3`ObV_`{s*u<=azAtC6dP-(jci zFIzyq#s7wsgsaKJ$9rqdGwm}osa7p_up|TN3YDD(BanTxW0px~`POp@3q%m6> z0J13eo&L`=q$&*22~uUT75c3?DId_L@G!$jAmg2obBas%y*1$X*H4t)&!hi)OD8^F z_980#9Pw-!-0Ti&JC2flHdxhkrrh2-`rc)3{q_6$3>(JhKiOT%WxbyFVuq?@-~d`_ zI+}0&2HLUT^GI__!&`V;>ecI?DE+$6)^{2Ai_s${jVSFFW1<97h>-_Dq`oaenC+2x zqx*+7EFEU*ydF50MJx@ivY^Up`9c`@lbWN5=|;S`{EGU_=rax@nWp&E#OSOf%rH=b zUs?X>E*GB*3o4h-w~nq?n{q}XsDOtBbMv|<>Jm5&nNb9jrd-jFgGA0YNwS_FPS#H* zag16MvN^n?UtV9If$ML{UlMkD1kIbye0nVpkg&IoR8&|Os-qB+kjZ(WJ$pAZGyevL z&)9F~#_bd}JmLURU$Guz@B;t`>$1m;CJs5B|8qdV*9om@HN@h_*H+oG4PQN;5Vg4ouSO zj!(zO^Y!7;a*-t2G#Mk|ti0iylO1mzQ@W)s{bIHb?ge^z)x{UM#+Hw`G8U<=37} z?~kK0A0u5TFnaHMmwrcdV|df97sxsQ#;BWH$dOiP(x7;!;PiCntulKSdK>+RvqUi0 z%g-8eUCoXDQA(#t1$3QG=8ER0yQLvrannplZ7A+VP!=cjrX{$x?Bn}xpXh=%I&TZl zueYO2NQCcJ^{j8;#H31JN81k7jo&NU`022w_GHxE}i`X^s@bmA6WFxmTO`y z?H(R|&*g(5&}TPFhyTzd6^&F@-%Kw5`oh?@LkzfV_)6&D`Uu1XeG7rqJU!EBFh_|z zJW8}KCsA53K&Hby9wd=zF-g=@jeYlLt5`O?a%1#4RI#RF_m-~ah8S)~%k;|=6#A+C zZj(Vfzq)j(mvb@GOY&btM2*i20yVjUsBzISD+7n~lt)qP2^JPsQgUjo8IZY*&o|}| z1_d-Z0F4T@;c>gJ3dJWi8h~a*@Z-=-L2t6%m?~GbYCX&te~JBirjDfVirbw-VRo!j zX(BdW+ z%Fv@2aAbgUZX^cX{!D&zI-E0)DnG5W?v=AAzSoIy>aFe4f#$&c;FV{h6H`Ll6{~l^z(+`^j7@J zKFhYm)%@aQjhAn&-{WKUB3JA{6;w|M)-%qLK1_5?gWgnMFq}raE8R5z8|_usn{5;ZdJkUepX(W$6ZOg2tLYur=R-?>sK$*4Dl1`> zO`9E6*kHeJsKTp;TTM^nerF3w_S577Qn89n&=+Stm@!9I*N;Zc9P3R`7C&c;sdgyf zRA)Nz#E7;j61~zhkbSnxD%1{3zO45SVj;JL7^tat8<%yBP8Z57k=LszV>Hp4%ZFe@ z%3nJ�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�C+JsfE)TR)P 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 import('@/pages/reversi/game.vue')), 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', component: page(() => import('@/pages/timeline.vue')), diff --git a/packages/frontend/src/pages/games.vue b/packages/frontend/src/pages/games.vue index 45a135a459..42f03242ed 100644 --- a/packages/frontend/src/pages/games.vue +++ b/packages/frontend/src/pages/games.vue @@ -18,6 +18,11 @@ SPDX-License-Identifier: AGPL-3.0-only +
    + + + +
    diff --git a/packages/frontend/src/pages/mahjong/index.vue b/packages/frontend/src/pages/mahjong/index.vue new file mode 100644 index 0000000000..e0c20b178a --- /dev/null +++ b/packages/frontend/src/pages/mahjong/index.vue @@ -0,0 +1,166 @@ + + + + + + + diff --git a/packages/frontend/src/pages/mahjong/room.game.vue b/packages/frontend/src/pages/mahjong/room.game.vue new file mode 100644 index 0000000000..99b1a7a104 --- /dev/null +++ b/packages/frontend/src/pages/mahjong/room.game.vue @@ -0,0 +1,307 @@ + + + + + + + diff --git a/packages/frontend/src/pages/mahjong/room.setting.vue b/packages/frontend/src/pages/mahjong/room.setting.vue new file mode 100644 index 0000000000..cc433fed42 --- /dev/null +++ b/packages/frontend/src/pages/mahjong/room.setting.vue @@ -0,0 +1,128 @@ + + + + + + + diff --git a/packages/frontend/src/pages/mahjong/room.vue b/packages/frontend/src/pages/mahjong/room.vue new file mode 100644 index 0000000000..51d68d4a76 --- /dev/null +++ b/packages/frontend/src/pages/mahjong/room.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index 35d112f6ec..de1c8bf2db 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -130,7 +130,7 @@ export function getConfig(): UserConfig { // https://vitejs.dev/guide/dep-pre-bundling.html#monorepos-and-linked-dependencies commonjsOptions: { - include: [/misskey-js/, /misskey-reversi/, /misskey-bubble-game/, /node_modules/], + include: [/misskey-js/, /misskey-reversi/, /misskey-bubble-game/, /misskey-mahjong/, /node_modules/], }, }, diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 26f100e452..cfcad77d7c 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1635,6 +1635,11 @@ declare namespace entities { ReversiSurrenderRequest, ReversiVerifyRequest, ReversiVerifyResponse, + MahjongCreateRoomResponse, + MahjongJoinRoomRequest, + MahjongJoinRoomResponse, + MahjongShowRoomRequest, + MahjongShowRoomResponse, Error_2 as Error, UserLite, UserDetailedNotMeOnly, @@ -1673,7 +1678,8 @@ declare namespace entities { RoleLite, Role, ReversiGameLite, - ReversiGameDetailed + ReversiGameDetailed, + MahjongRoomDetailed } } export { entities } @@ -2169,6 +2175,24 @@ type IWebhooksShowResponse = operations['i/webhooks/show']['responses']['200'][' // @public (undocumented) 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) type MeDetailed = components['schemas']['MeDetailed']; diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index c97b95e536..5897fa74ed 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -1,6 +1,6 @@ /* - * version: 2024.2.0-beta.6 - * generatedAt: 2024-01-24T07:32:10.455Z + * version: 2024.2.0-beta.7 + * generatedAt: 2024-01-26T05:23:04.911Z */ import type { SwitchCaseResponseType } from '../api.js'; @@ -4084,5 +4084,38 @@ declare module '../api.js' { params: P, credential?: string | null, ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:account* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:account* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:account* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; } } diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index e356de3453..8cf669cab6 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -1,6 +1,6 @@ /* - * version: 2024.2.0-beta.6 - * generatedAt: 2024-01-24T07:32:10.453Z + * version: 2024.2.0-beta.7 + * generatedAt: 2024-01-26T05:23:04.909Z */ import type { @@ -556,6 +556,11 @@ import type { ReversiSurrenderRequest, ReversiVerifyRequest, ReversiVerifyResponse, + MahjongCreateRoomResponse, + MahjongJoinRoomRequest, + MahjongJoinRoomResponse, + MahjongShowRoomRequest, + MahjongShowRoomResponse, } from './entities.js'; export type Endpoints = { @@ -926,4 +931,7 @@ export type Endpoints = { 'reversi/show-game': { req: ReversiShowGameRequest; res: ReversiShowGameResponse }; 'reversi/surrender': { req: ReversiSurrenderRequest; res: EmptyResponse }; '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 }; } diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index bfe40dc947..41a6f6c7a7 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -1,6 +1,6 @@ /* - * version: 2024.2.0-beta.6 - * generatedAt: 2024-01-24T07:32:10.452Z + * version: 2024.2.0-beta.7 + * generatedAt: 2024-01-26T05:23:04.908Z */ 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 ReversiVerifyRequest = operations['reversi/verify']['requestBody']['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']; diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts index b7dcbfd951..01a5cddbb8 100644 --- a/packages/misskey-js/src/autogen/models.ts +++ b/packages/misskey-js/src/autogen/models.ts @@ -1,6 +1,6 @@ /* - * version: 2024.2.0-beta.6 - * generatedAt: 2024-01-24T07:32:10.450Z + * version: 2024.2.0-beta.7 + * generatedAt: 2024-01-26T05:23:04.907Z */ import { components } from './types.js'; @@ -43,3 +43,4 @@ export type RoleLite = components['schemas']['RoleLite']; export type Role = components['schemas']['Role']; export type ReversiGameLite = components['schemas']['ReversiGameLite']; export type ReversiGameDetailed = components['schemas']['ReversiGameDetailed']; +export type MahjongRoomDetailed = components['schemas']['MahjongRoomDetailed']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index b5eca12a19..c4595ef4f2 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -2,8 +2,8 @@ /* eslint @typescript-eslint/no-explicit-any: 0 */ /* - * version: 2024.2.0-beta.6 - * generatedAt: 2024-01-24T07:32:10.370Z + * version: 2024.2.0-beta.7 + * generatedAt: 2024-01-26T05:23:04.825Z */ /** @@ -3535,6 +3535,33 @@ export type paths = { */ 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; @@ -4537,6 +4564,38 @@ export type components = { logs: unknown[][]; 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; 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']; + }; + }; + }; + }; }; diff --git a/packages/misskey-mahjong/.eslintignore b/packages/misskey-mahjong/.eslintignore new file mode 100644 index 0000000000..f22128f047 --- /dev/null +++ b/packages/misskey-mahjong/.eslintignore @@ -0,0 +1,7 @@ +node_modules +/built +/coverage +/.eslintrc.js +/jest.config.ts +/test +/test-d diff --git a/packages/misskey-mahjong/.eslintrc.cjs b/packages/misskey-mahjong/.eslintrc.cjs new file mode 100644 index 0000000000..db37a01098 --- /dev/null +++ b/packages/misskey-mahjong/.eslintrc.cjs @@ -0,0 +1,10 @@ +module.exports = { + root: true, + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, + extends: [ + '../shared/.eslintrc.js', + ], +}; diff --git a/packages/misskey-mahjong/build.js b/packages/misskey-mahjong/build.js new file mode 100644 index 0000000000..4744dfaf7b --- /dev/null +++ b/packages/misskey-mahjong/build.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); +}); diff --git a/packages/misskey-mahjong/package.json b/packages/misskey-mahjong/package.json new file mode 100644 index 0000000000..36348665ac --- /dev/null +++ b/packages/misskey-mahjong/package.json @@ -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" + } +} diff --git a/packages/misskey-mahjong/src/engine.ts b/packages/misskey-mahjong/src/engine.ts new file mode 100644 index 0000000000..5dd50817d5 --- /dev/null +++ b/packages/misskey-mahjong/src/engine.ts @@ -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; + } +} diff --git a/packages/misskey-mahjong/src/index.ts b/packages/misskey-mahjong/src/index.ts new file mode 100644 index 0000000000..1fc4505942 --- /dev/null +++ b/packages/misskey-mahjong/src/index.ts @@ -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'; diff --git a/packages/misskey-mahjong/src/serializer.ts b/packages/misskey-mahjong/src/serializer.ts new file mode 100644 index 0000000000..7444812b2b --- /dev/null +++ b/packages/misskey-mahjong/src/serializer.ts @@ -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 = { + '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; +} diff --git a/packages/misskey-mahjong/tsconfig.json b/packages/misskey-mahjong/tsconfig.json new file mode 100644 index 0000000000..f56b65e868 --- /dev/null +++ b/packages/misskey-mahjong/tsconfig.json @@ -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/**/*" + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fa466569ef..6fe4772ec2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -263,6 +263,9 @@ importers: misskey-js: specifier: workspace:* version: link:../misskey-js + misskey-mahjong: + specifier: workspace:* + version: link:../misskey-mahjong misskey-reversi: specifier: workspace:* version: link:../misskey-reversi @@ -778,6 +781,9 @@ importers: misskey-js: specifier: workspace:* version: link:../misskey-js + misskey-mahjong: + specifier: workspace:* + version: link:../misskey-mahjong misskey-reversi: specifier: workspace:* version: link:../misskey-reversi @@ -831,7 +837,7 @@ importers: version: 1.7.2(vue@3.4.15) vite: 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: specifier: 3.4.15 version: 3.4.15(typescript@5.3.3) @@ -1009,7 +1015,7 @@ importers: version: 1.0.3 vitest: 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: specifier: 0.2.2 version: 0.2.2(vitest@0.34.6) @@ -1166,6 +1172,40 @@ importers: specifier: 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: dependencies: crc-32: @@ -1906,7 +1946,7 @@ packages: '@babel/traverse': 7.22.11 '@babel/types': 7.22.17 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 json5: 2.2.3 semver: 6.3.1 @@ -1929,7 +1969,7 @@ packages: '@babel/traverse': 7.23.5 '@babel/types': 7.23.5 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 json5: 2.2.3 semver: 6.3.1 @@ -2031,7 +2071,7 @@ packages: '@babel/core': 7.23.5 '@babel/helper-compilation-targets': 7.22.15 '@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 resolve: 1.22.8 transitivePeerDependencies: @@ -3430,7 +3470,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.5 '@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 transitivePeerDependencies: - supports-color @@ -3448,7 +3488,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.6 '@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 transitivePeerDependencies: - supports-color @@ -4155,7 +4195,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: 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 globals: 13.19.0 ignore: 5.2.4 @@ -4172,7 +4212,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: 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 globals: 13.19.0 ignore: 5.2.4 @@ -4407,7 +4447,7 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@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 transitivePeerDependencies: - supports-color @@ -4711,7 +4751,7 @@ packages: magic-string: 0.27.0 react-docgen-typescript: 2.2.2(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 /@jridgewell/gen-mapping@0.3.2: @@ -4735,7 +4775,6 @@ packages: dependencies: '@jridgewell/gen-mapping': 0.3.2 '@jridgewell/trace-mapping': 0.3.18 - dev: false /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} @@ -6772,7 +6811,7 @@ packages: magic-string: 0.30.5 rollup: 3.29.4 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: - encoding - supports-color @@ -6977,7 +7016,7 @@ packages: util: 0.12.5 util-deprecate: 1.0.2 watchpack: 2.4.0 - ws: 8.16.0 + ws: 8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3) transitivePeerDependencies: - bufferutil - encoding @@ -7146,7 +7185,7 @@ packages: react: 18.2.0 react-docgen: 7.0.1 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: - '@preact/preset-vite' - encoding @@ -7272,7 +7311,7 @@ packages: '@storybook/vue3': 7.6.10(vue@3.4.15) '@vitejs/plugin-vue': 4.5.2(vite@5.0.12)(vue@3.4.15) 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) transitivePeerDependencies: - '@preact/preset-vite' @@ -7772,7 +7811,7 @@ packages: dom-accessibility-api: 0.5.16 lodash: 4.17.21 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 /@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/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3) '@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 graphemer: 1.4.0 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/utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3) '@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 graphemer: 1.4.0 ignore: 5.2.4 @@ -8501,7 +8540,7 @@ packages: '@typescript-eslint/types': 6.11.0 '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3) '@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 typescript: 5.3.3 transitivePeerDependencies: @@ -8522,7 +8561,7 @@ packages: '@typescript-eslint/types': 6.18.1 '@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3) '@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 typescript: 5.3.3 transitivePeerDependencies: @@ -8557,7 +8596,7 @@ packages: dependencies: '@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) - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.53.0 ts-api-utils: 1.0.1(typescript@5.3.3) typescript: 5.3.3 @@ -8577,7 +8616,7 @@ packages: dependencies: '@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) - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.56.0 ts-api-utils: 1.0.1(typescript@5.3.3) typescript: 5.3.3 @@ -8606,7 +8645,7 @@ packages: dependencies: '@typescript-eslint/types': 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 is-glob: 4.0.3 semver: 7.5.4 @@ -8627,7 +8666,7 @@ packages: dependencies: '@typescript-eslint/types': 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 is-glob: 4.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) magic-string: 0.27.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: - supports-color dev: true @@ -8719,7 +8758,7 @@ packages: vite: ^4.0.0 || ^5.0.0 vue: ^3.2.25 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) dev: true @@ -8730,7 +8769,7 @@ packages: vite: ^5.0.0 vue: ^3.2.25 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) dev: false @@ -8750,7 +8789,7 @@ packages: std-env: 3.7.0 test-exclude: 6.0.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: - supports-color dev: true @@ -9091,7 +9130,7 @@ packages: engines: {node: '>= 6.0.0'} requiresBuild: true dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -9099,7 +9138,7 @@ packages: resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} engines: {node: '>= 14'} dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: false @@ -9485,7 +9524,7 @@ packages: resolution: {integrity: sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==} dependencies: 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 transitivePeerDependencies: - supports-color @@ -9889,7 +9928,6 @@ packages: requiresBuild: true dependencies: node-gyp-build: 4.6.0 - dev: false /bullmq@5.1.4: resolution: {integrity: sha512-j/AjaPc8BhyrH7b2MyZpi4cUtGH8TJTxonZUmXEefmKU8z5DcldzmlXPief0P4+qvN0A7qwWZH3n0F+GsWgQkg==} @@ -10434,7 +10472,6 @@ packages: /commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - dev: false /commander@6.2.1: resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} @@ -10931,6 +10968,7 @@ packages: dependencies: ms: 2.1.2 supports-color: 5.5.0 + dev: true /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} @@ -10943,7 +10981,6 @@ packages: dependencies: ms: 2.1.2 supports-color: 8.1.1 - dev: true /decamelize-keys@1.1.1: resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} @@ -11160,7 +11197,7 @@ packages: hasBin: true dependencies: address: 1.2.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true @@ -11484,7 +11521,7 @@ packages: peerDependencies: esbuild: '>=0.12 <1' dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) esbuild: 0.18.20 transitivePeerDependencies: - supports-color @@ -11793,7 +11830,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 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 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -11840,7 +11877,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 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 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -12471,7 +12508,7 @@ packages: debug: optional: true dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) /for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} @@ -13027,6 +13064,7 @@ packages: /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} + dev: true /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -13164,7 +13202,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: false @@ -13224,7 +13262,7 @@ packages: engines: {node: '>= 6.0.0'} dependencies: agent-base: 5.1.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true @@ -13234,7 +13272,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -13243,7 +13281,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: false @@ -13403,7 +13441,7 @@ packages: dependencies: '@ioredis/commands': 1.2.0 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 lodash.defaults: 4.2.0 lodash.isarguments: 3.1.0 @@ -13849,7 +13887,7 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} 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 source-map: 0.6.1 transitivePeerDependencies: @@ -15624,7 +15662,6 @@ packages: resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} hasBin: true requiresBuild: true - dev: false /node-gyp@10.0.1: resolution: {integrity: sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==} @@ -17159,7 +17196,7 @@ packages: engines: {node: '>=8.16.0'} dependencies: '@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 https-proxy-agent: 4.0.0 mime: 2.6.0 @@ -18159,7 +18196,7 @@ packages: dependencies: '@hapi/hoek': 10.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 transitivePeerDependencies: - supports-color @@ -18359,7 +18396,7 @@ packages: engines: {node: '>= 14'} dependencies: 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 transitivePeerDependencies: - supports-color @@ -18512,7 +18549,7 @@ packages: arg: 5.0.2 bluebird: 3.7.2 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 lazy-ass: 1.6.0 ps-tree: 1.2.0 @@ -18770,6 +18807,7 @@ packages: engines: {node: '>=4'} dependencies: has-flag: 3.0.0 + dev: true /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} @@ -18932,7 +18970,6 @@ packages: acorn: 8.11.3 commander: 2.20.3 source-map-support: 0.5.21 - dev: false /test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} @@ -19391,7 +19428,7 @@ packages: chalk: 4.1.2 cli-highlight: 2.1.11 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 glob: 10.3.10 ioredis: 5.3.2 @@ -19654,7 +19691,6 @@ packages: requiresBuild: true dependencies: node-gyp-build: 4.6.0 - dev: false /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -19746,17 +19782,17 @@ packages: core-util-is: 1.0.2 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==} engines: {node: '>=v14.18.0'} hasBin: true dependencies: 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 pathe: 1.1.2 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: - '@types/node' - less @@ -19772,7 +19808,7 @@ packages: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} 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==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -19805,6 +19841,7 @@ packages: postcss: 8.4.33 rollup: 4.9.6 sass: 1.70.0 + terser: 5.27.0 optionalDependencies: fsevents: 2.3.3 @@ -19815,12 +19852,12 @@ packages: vitest: '>=0.16.0' dependencies: 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: - encoding 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==} engines: {node: '>=v14.18.0'} hasBin: true @@ -19863,7 +19900,7 @@ packages: acorn-walk: 8.3.2 cac: 6.7.14 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 local-pkg: 0.4.3 magic-string: 0.30.5 @@ -19873,8 +19910,8 @@ packages: strip-literal: 1.3.0 tinybench: 2.6.0 tinypool: 0.7.0 - vite: 5.0.12(@types/node@20.11.5)(sass@1.70.0) - vite-node: 0.34.6(@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)(terser@5.27.0) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -19945,7 +19982,7 @@ packages: peerDependencies: eslint: '>=6.0.0' dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.56.0 eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 @@ -20275,19 +20312,6 @@ packages: async-limiter: 1.0.1 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): resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} engines: {node: '>=10.0.0'} @@ -20302,7 +20326,6 @@ packages: dependencies: bufferutil: 4.0.7 utf-8-validate: 6.0.3 - dev: false /xev@3.0.2: resolution: {integrity: sha512-8kxuH95iMXzHZj+fwqfA4UrPcYOy6bGIgfWzo9Ji23JoEc30ge/Z++Ubkiuy8c0+M64nXmmxrmJ7C8wnuBhluw==} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 193669e7a4..378cdc84ad 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,3 +6,4 @@ packages: - 'packages/misskey-js/generator' - 'packages/misskey-reversi' - 'packages/misskey-bubble-game' + - 'packages/misskey-mahjong' diff --git a/scripts/clean-all.js b/scripts/clean-all.js index 160014a3f3..daebf1ea59 100644 --- a/scripts/clean-all.js +++ b/scripts/clean-all.js @@ -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/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 + '/../node_modules', { recursive: true, force: true }); diff --git a/scripts/clean.js b/scripts/clean.js index 09ac786853..10e8a40450 100644 --- a/scripts/clean.js +++ b/scripts/clean.js @@ -12,5 +12,6 @@ const fs = require('fs'); 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-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 }); })(); diff --git a/scripts/dev.mjs b/scripts/dev.mjs index 9b60af6570..dd8b9abd01 100644 --- a/scripts/dev.mjs +++ b/scripts/dev.mjs @@ -46,6 +46,12 @@ await execa('pnpm', ['--filter', 'misskey-bubble-game', 'build'], { stderr: process.stderr, }); +await execa('pnpm', ['--filter', 'misskey-mahjong', 'build'], { + cwd: _dirname + '/../', + stdout: process.stdout, + stderr: process.stderr, +}); + execa('pnpm', ['build-pre', '--watch'], { cwd: _dirname + '/../', stdout: process.stdout, @@ -87,3 +93,9 @@ execa('pnpm', ['--filter', 'misskey-bubble-game', 'watch'], { stdout: process.stdout, stderr: process.stderr, }); + +execa('pnpm', ['--filter', 'misskey-mahjong', 'watch'], { + cwd: _dirname + '/../', + stdout: process.stdout, + stderr: process.stderr, +});