リバーシの観戦者を表示するように

Resolve #1830
This commit is contained in:
syuilo 2020-11-03 22:02:55 +09:00
parent 3c59c6fc9b
commit f8ad303b13
2 changed files with 81 additions and 3 deletions

View File

@ -57,7 +57,7 @@
<p class="status"><b>{{ $t('_reversi.turnCount', { count: logPos }) }}</b> {{ $t('_reversi.black') }}:{{ o.blackCount }} {{ $t('_reversi.white') }}:{{ o.whiteCount }} {{ $t('_reversi.total') }}:{{ o.blackCount + o.whiteCount }}</p> <p class="status"><b>{{ $t('_reversi.turnCount', { count: logPos }) }}</b> {{ $t('_reversi.black') }}:{{ o.blackCount }} {{ $t('_reversi.white') }}:{{ o.whiteCount }} {{ $t('_reversi.total') }}:{{ o.blackCount + o.whiteCount }}</p>
<div class="actions" v-if="!game.isEnded && iAmPlayer"> <div class="actions" v-if="!game.isEnded && iAmPlayer">
<MkButton @click="surrender">{{ $t('_reversi.surrender') }}</MkButton> <MkButton @click="surrender" inline>{{ $t('_reversi.surrender') }}</MkButton>
</div> </div>
<div class="player" v-if="game.isEnded"> <div class="player" v-if="game.isEnded">
@ -76,6 +76,10 @@
<p v-if="game.loopedBoard">{{ $t('_reversi.loopedMap') }}</p> <p v-if="game.loopedBoard">{{ $t('_reversi.loopedMap') }}</p>
<p v-if="game.canPutEverywhere">{{ $t('_reversi.canPutEverywhere') }}</p> <p v-if="game.canPutEverywhere">{{ $t('_reversi.canPutEverywhere') }}</p>
</div> </div>
<div class="watchers">
<MkAvatar v-for="user in watchers" :key="user.id" :user="user" class="avatar"/>
</div>
</div> </div>
</template> </template>
@ -113,6 +117,7 @@ export default defineComponent({
o: null as Reversi, o: null as Reversi,
logs: [], logs: [],
logPos: 0, logPos: 0,
watchers: [],
pollingClock: null, pollingClock: null,
faAngleDoubleLeft, faAngleLeft, faAngleRight, faAngleDoubleRight, fasCircle, farCircle, faPlay faAngleDoubleLeft, faAngleLeft, faAngleRight, faAngleDoubleRight, fasCircle, farCircle, faPlay
}; };
@ -198,12 +203,14 @@ export default defineComponent({
this.connection.on('set', this.onSet); this.connection.on('set', this.onSet);
this.connection.on('rescue', this.onRescue); this.connection.on('rescue', this.onRescue);
this.connection.on('ended', this.onEnded); this.connection.on('ended', this.onEnded);
this.connection.on('watchers', this.onWatchers);
}, },
beforeUnmount() { beforeUnmount() {
this.connection.off('set', this.onSet); this.connection.off('set', this.onSet);
this.connection.off('rescue', this.onRescue); this.connection.off('rescue', this.onRescue);
this.connection.off('ended', this.onEnded); this.connection.off('ended', this.onEnded);
this.connection.off('watchers', this.onWatchers);
clearInterval(this.pollingClock); clearInterval(this.pollingClock);
}, },
@ -309,6 +316,10 @@ export default defineComponent({
this.$forceUpdate(); this.$forceUpdate();
}, },
onWatchers(users) {
this.watchers = users;
},
surrender() { surrender() {
os.api('games/reversi/games/surrender', { os.api('games/reversi/games/surrender', {
gameId: this.game.id gameId: this.game.id
@ -506,5 +517,18 @@ export default defineComponent({
} }
} }
} }
> .watchers {
padding: 0 0 16px 0;
&:empty {
display: none;
}
> .avatar {
width: 32px;
height: 32px;
}
}
} }
</style> </style>

View File

@ -5,7 +5,8 @@ import Reversi from '../../../../../games/reversi/core';
import * as maps from '../../../../../games/reversi/maps'; import * as maps from '../../../../../games/reversi/maps';
import Channel from '../../channel'; import Channel from '../../channel';
import { ReversiGame } from '../../../../../models/entities/games/reversi/game'; import { ReversiGame } from '../../../../../models/entities/games/reversi/game';
import { ReversiGames } from '../../../../../models'; import { ReversiGames, Users } from '../../../../../models';
import { User } from '../../../../../models/entities/user';
export default class extends Channel { export default class extends Channel {
public readonly chName = 'gamesReversiGame'; public readonly chName = 'gamesReversiGame';
@ -13,17 +14,58 @@ export default class extends Channel {
public static requireCredential = false; public static requireCredential = false;
private gameId: ReversiGame['id'] | null = null; private gameId: ReversiGame['id'] | null = null;
private watchers: Record<User['id'], Date> = {};
private emitWatchersIntervalId: any;
@autobind @autobind
public async init(params: any) { public async init(params: any) {
this.gameId = params.gameId; this.gameId = params.gameId;
// Subscribe game stream // Subscribe game stream
this.subscriber.on(`reversiGameStream:${this.gameId}`, data => { this.subscriber.on(`reversiGameStream:${this.gameId}`, this.onEvent);
this.emitWatchersIntervalId = setInterval(this.emitWatchers, 5000);
const game = await ReversiGames.findOne(this.gameId!);
if (game == null) throw new Error('game not found');
// 観戦者イベント
this.watch(game);
}
@autobind
private onEvent(data: any) {
if (data.type === 'watching') {
const id = data.body;
this.watchers[id] = new Date();
} else {
this.send(data); this.send(data);
}
}
@autobind
private async emitWatchers() {
const now = new Date();
// Remove not watching users
for (const [userId, date] of Object.entries(this.watchers)) {
if (now.getTime() - date.getTime() > 5000) delete this.watchers[userId];
}
const users = await Users.packMany(Object.keys(this.watchers), null, { detail: false });
this.send({
type: 'watchers',
body: users,
}); });
} }
@autobind
public dispose() {
// Unsubscribe events
this.subscriber.off(`reversiGameStream:${this.gameId}`, this.onEvent);
clearInterval(this.emitWatchersIntervalId);
}
@autobind @autobind
public onMessage(type: string, body: any) { public onMessage(type: string, body: any) {
switch (type) { switch (type) {
@ -314,5 +356,17 @@ export default class extends Channel {
if (crc32.toString() !== game.crc32) { if (crc32.toString() !== game.crc32) {
this.send('rescue', await ReversiGames.pack(game, this.user)); this.send('rescue', await ReversiGames.pack(game, this.user));
} }
// ついでに観戦者イベントを発行
this.watch(game);
}
@autobind
private watch(game: ReversiGame) {
if (this.user != null) {
if ((game.user1Id !== this.user.id) && (game.user2Id !== this.user.id)) {
publishReversiGameStream(this.gameId!, 'watching', this.user.id);
}
}
} }
} }