summaryrefslogtreecommitdiff
path: root/packages/backend/src/server/api/stream/channels/reversi-game.ts
blob: 2d8c396db9e5ae0649cf49d073d148f8808bef98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
 * SPDX-FileCopyrightText: syuilo and other misskey contributors
 * SPDX-License-Identifier: AGPL-3.0-only
 */

import { Inject, Injectable } from '@nestjs/common';
import type { MiReversiGame, ReversiGamesRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { ReversiService } from '@/core/ReversiService.js';
import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js';
import Channel, { type MiChannelService } from '../channel.js';

class ReversiGameChannel extends Channel {
	public readonly chName = 'reversiGame';
	public static shouldShare = false;
	public static requireCredential = false as const;
	private gameId: MiReversiGame['id'] | null = null;

	constructor(
		private reversiService: ReversiService,
		private reversiGamesRepository: ReversiGamesRepository,
		private reversiGameEntityService: ReversiGameEntityService,

		id: string,
		connection: Channel['connection'],
	) {
		super(id, connection);
	}

	@bindThis
	public async init(params: any) {
		this.gameId = params.gameId as string;

		const game = await this.reversiGamesRepository.findOneBy({
			id: this.gameId,
		});
		if (game == null) return;

		this.subscriber.on(`reversiGameStream:${this.gameId}`, 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 'putStone': this.putStone(body.pos, body.id); break;
			case 'syncState': this.syncState(body.crc32); break;
		}
	}

	@bindThis
	private async updateSettings(key: string, value: any) {
		if (this.user == null) return;

		// TODO: キャッシュしたい
		const game = await this.reversiGamesRepository.findOneBy({ id: this.gameId! });
		if (game == null) throw new Error('game not found');

		this.reversiService.updateSettings(game, this.user, key, value);
	}

	@bindThis
	private async ready(ready: boolean) {
		if (this.user == null) return;

		const game = await this.reversiGamesRepository.findOneBy({ id: this.gameId! });
		if (game == null) throw new Error('game not found');

		this.reversiService.gameReady(game, this.user, ready);
	}

	@bindThis
	private async putStone(pos: number, id: string) {
		if (this.user == null) return;

		// TODO: キャッシュしたい
		const game = await this.reversiGamesRepository.findOneBy({ id: this.gameId! });
		if (game == null) throw new Error('game not found');

		this.reversiService.putStoneToGame(game, this.user, pos, id);
	}

	@bindThis
	private async syncState(crc32: string | number) {
		// TODO: キャッシュしたい
		const game = await this.reversiGamesRepository.findOneBy({ id: this.gameId! });
		if (game == null) throw new Error('game not found');

		if (!game.isStarted) return;

		if (crc32.toString() !== game.crc32) {
			this.send('rescue', await this.reversiGameEntityService.packDetail(game, this.user));
		}
	}

	@bindThis
	public dispose() {
		// Unsubscribe events
		this.subscriber.off(`reversiGameStream:${this.gameId}`, this.send);
	}
}

@Injectable()
export class ReversiGameChannelService implements MiChannelService<false> {
	public readonly shouldShare = ReversiGameChannel.shouldShare;
	public readonly requireCredential = ReversiGameChannel.requireCredential;
	public readonly kind = ReversiGameChannel.kind;

	constructor(
		@Inject(DI.reversiGamesRepository)
		private reversiGamesRepository: ReversiGamesRepository,

		private reversiService: ReversiService,
		private reversiGameEntityService: ReversiGameEntityService,
	) {
	}

	@bindThis
	public create(id: string, connection: Channel['connection']): ReversiGameChannel {
		return new ReversiGameChannel(
			this.reversiService,
			this.reversiGamesRepository,
			this.reversiGameEntityService,
			id,
			connection,
		);
	}
}