mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2024-11-23 10:27:28 -07:00
parent
ff6980c0be
commit
ceb2a0b7e5
2 changed files with 81 additions and 3 deletions
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue