summaryrefslogtreecommitdiff
path: root/packages/backend/src/server/api/endpoints/bubble-game
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2024-01-11 18:13:39 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2024-01-11 18:13:39 +0900
commitcf54c2ba4750c307d840016828f837a61f886726 (patch)
treeb9fbd0cc73ca12f0a541b2362314b62e5402b7a4 /packages/backend/src/server/api/endpoints/bubble-game
parentenhance(drop-and-fusion): make game engine headless for server-side running (diff)
downloadsharkey-cf54c2ba4750c307d840016828f837a61f886726.tar.gz
sharkey-cf54c2ba4750c307d840016828f837a61f886726.tar.bz2
sharkey-cf54c2ba4750c307d840016828f837a61f886726.zip
feat: ranking system of bubble game
Resolve #12961
Diffstat (limited to 'packages/backend/src/server/api/endpoints/bubble-game')
-rw-r--r--packages/backend/src/server/api/endpoints/bubble-game/ranking.ts75
-rw-r--r--packages/backend/src/server/api/endpoints/bubble-game/register.ts86
2 files changed, 161 insertions, 0 deletions
diff --git a/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts b/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts
new file mode 100644
index 0000000000..0cba129a09
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts
@@ -0,0 +1,75 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { MoreThan } from 'typeorm';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import type { BubbleGameRecordsRepository } from '@/models/_.js';
+import { DI } from '@/di-symbols.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+
+export const meta = {
+ tags: [],
+
+ allowGet: true,
+ cacheSec: 60,
+
+ errors: {
+ },
+
+ res: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ properties: {
+ id: { type: 'string', format: 'misskey:id' },
+ score: { type: 'integer' },
+ user: { ref: 'UserLite' },
+ },
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ gameMode: { type: 'string' },
+ },
+ required: ['gameMode'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+ constructor(
+ @Inject(DI.bubbleGameRecordsRepository)
+ private bubbleGameRecordsRepository: BubbleGameRecordsRepository,
+
+ private userEntityService: UserEntityService,
+ ) {
+ super(meta, paramDef, async (ps) => {
+ const records = await this.bubbleGameRecordsRepository.find({
+ where: {
+ gameMode: ps.gameMode,
+ seededAt: MoreThan(new Date(Date.now() - 1000 * 60 * 60 * 24 * 7)),
+ },
+ order: {
+ score: 'DESC',
+ },
+ take: 10,
+ relations: ['user'],
+ });
+
+ const users = await this.userEntityService.packMany(records.map(r => r.user!), null, { detail: false });
+
+ return records.map(r => ({
+ id: r.id,
+ score: r.score,
+ user: users.find(u => u.id === r.user!.id),
+ }));
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/bubble-game/register.ts b/packages/backend/src/server/api/endpoints/bubble-game/register.ts
new file mode 100644
index 0000000000..af0f69e4ad
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/bubble-game/register.ts
@@ -0,0 +1,86 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import ms from 'ms';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { IdService } from '@/core/IdService.js';
+import type { BubbleGameRecordsRepository } from '@/models/_.js';
+import { DI } from '@/di-symbols.js';
+import { ApiError } from '../../error.js';
+
+export const meta = {
+ tags: [],
+
+ requireCredential: true,
+
+ kind: 'write:account',
+
+ limit: {
+ duration: ms('1hour'),
+ max: 120,
+ minInterval: ms('30sec'),
+ },
+
+ errors: {
+ invalidSeed: {
+ message: 'Provided seed is invalid.',
+ code: 'INVALID_SEED',
+ id: 'eb627bc7-574b-4a52-a860-3c3eae772b88',
+ },
+ },
+
+ res: {
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ score: { type: 'integer', minimum: 0 },
+ seed: { type: 'string', minLength: 1, maxLength: 1024 },
+ logs: { type: 'array' },
+ gameMode: { type: 'string' },
+ gameVersion: { type: 'integer' },
+ },
+ required: ['score', 'seed', 'logs', 'gameMode', 'gameVersion'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+ constructor(
+ @Inject(DI.bubbleGameRecordsRepository)
+ private bubbleGameRecordsRepository: BubbleGameRecordsRepository,
+
+ private idService: IdService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const seedDate = new Date(parseInt(ps.seed, 10));
+ const now = new Date();
+
+ // シードが未来なのは通常のプレイではありえないので弾く
+ if (seedDate.getTime() > now.getTime()) {
+ throw new ApiError(meta.errors.invalidSeed);
+ }
+
+ // シードが古すぎる(1時間以上前)のも弾く
+ if (seedDate.getTime() < now.getTime() - 1000 * 60 * 60) {
+ throw new ApiError(meta.errors.invalidSeed);
+ }
+
+ await this.bubbleGameRecordsRepository.insert({
+ id: this.idService.gen(now.getTime()),
+ seed: ps.seed,
+ seededAt: seedDate,
+ userId: me.id,
+ score: ps.score,
+ logs: ps.logs,
+ gameMode: ps.gameMode,
+ gameVersion: ps.gameVersion,
+ isVerified: false,
+ });
+ });
+ }
+}