mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-10-31 21:44:12 +00:00 
			
		
		
		
	
							parent
							
								
									3c59c6fc9b
								
							
						
					
					
						commit
						f8ad303b13
					
				
					 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…
	
	Add table
		
		Reference in a new issue