-
-
Notifications
You must be signed in to change notification settings - Fork 2
Animate individual PvB and PvP moves in lobby live viewer #731
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Copilot
wants to merge
15
commits into
master
Choose a base branch
from
copilot/animate-individual-moves
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
816c68a
Initial plan
Copilot f5a1c49
animate individual PvB moves in live viewer instead of jumping 2 at once
Copilot 667cbd2
extend individual move animation to PvP games in live viewer
Copilot 6bfe857
merge pvpMoveIndexes/pvbMoveIndexes into single moveIndexes map
Copilot 7301045
Rename gameID to gameId in GameDataService
Copilot e74598e
Inline moveIndexes assignment in live-games-viewer.js
Copilot 0b31100
Move lobby live games update from HTTP polling to WebSocket session
Copilot fee2111
Simplify lobby complete-refresh to a 60s interval
Copilot 9bc6cc6
Cancel LobbyService refresher job during shutdown
Copilot caceb8e
Fix LobbyService shutdown error message wording
Copilot 3583129
Batch live games fetch across lobby sessions
Copilot 31e78b8
Guard against negative drop count in move slicing
Copilot d97e70e
Remove closed live games sessions at start of refresh
Copilot 14bddf4
Merge branch 'master' into copilot/animate-individual-moves
936e1b2
Parse UCI move strings to HalfMove before animating lobby thumbs
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
...rvice-layer/src/main/kotlin/io/elephantchess/servicelayer/dto/ws/LiveGamesSubscription.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package io.elephantchess.servicelayer.dto.ws | ||
|
|
||
| import io.elephantchess.model.GameId | ||
|
|
||
| /** | ||
| * Sent by a lobby client over the live-games WebSocket to declare which games it | ||
| * is currently displaying and wants to receive updates for. The move index per | ||
| * game is tracked server-side (in [io.elephantchess.servicelayer.services.ws.LiveGamesWebSocketSession]), | ||
| * so the client only needs to send the game ids. | ||
| */ | ||
| data class LiveGamesSubscription( | ||
| val gameIds: List<GameId> = emptyList() | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
...er/src/main/kotlin/io/elephantchess/servicelayer/services/ws/LiveGamesWebSocketSession.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| package io.elephantchess.servicelayer.services.ws | ||
|
|
||
| import io.elephantchess.model.GameEventType | ||
| import io.elephantchess.model.GameId | ||
| import io.elephantchess.servicelayer.dto.lobby.LatestGamesUpdateRequest | ||
| import io.elephantchess.servicelayer.dto.lobby.LatestGamesUpdateResponse | ||
| import kotlinx.coroutines.channels.ChannelResult | ||
|
|
||
| /** | ||
| * Batched update handed to every live games session: the [response] is fetched once | ||
| * for the union of all watched games (using the lowest tracked move index per game, | ||
| * see [batchMoveIndexes]) so each session can slice out only the moves it still needs. | ||
| */ | ||
| data class LiveGamesBatchUpdate( | ||
| val response: LatestGamesUpdateResponse, | ||
| val batchMoveIndexes: Map<String, Int>, | ||
| ) | ||
|
|
||
| /** | ||
| * WebSocket session for a lobby client watching the live games thumbnails. | ||
| * | ||
| * The client declares which games it watches via [updateSubscription]; the last | ||
| * known move index per game is tracked here (not sent by the client), so the | ||
| * refresher can request only the new moves and the session only pushes entries | ||
| * that actually changed (new moves or a status change). | ||
| */ | ||
| class LiveGamesWebSocketSession( | ||
| private val sendCb: (LatestGamesUpdateResponse) -> ChannelResult<Unit>, | ||
| ) : WebSocketSession<LiveGamesBatchUpdate>() { | ||
|
|
||
| private var subscribedGameIds: List<GameId> = emptyList() | ||
| private val moveIndexes = mutableMapOf<String, Int>() | ||
| private val lastStatuses = mutableMapOf<String, GameEventType>() | ||
|
|
||
| fun updateSubscription(gameIds: List<GameId>) { | ||
| subscribedGameIds = gameIds | ||
|
|
||
| // forget tracking for games that are not watched anymore | ||
| val watchedIds = gameIds.map { it.id }.toSet() | ||
| moveIndexes.keys.retainAll(watchedIds) | ||
| lastStatuses.keys.retainAll(watchedIds) | ||
| } | ||
|
|
||
| fun currentRequest(): LatestGamesUpdateRequest = | ||
| LatestGamesUpdateRequest( | ||
| gameIds = subscribedGameIds, | ||
| moveIndexes = moveIndexes.toMap() | ||
| ) | ||
|
|
||
| override fun update(update: LiveGamesBatchUpdate) { | ||
| val watchedIds = subscribedGameIds.map { it.id }.toSet() | ||
| val entries = update.response.entries | ||
| .filter { entry -> entry.gameId.id in watchedIds } | ||
| .map { entry -> sliceForSession(entry, update.batchMoveIndexes) } | ||
|
|
||
| val changedEntries = entries.filter { entry -> | ||
| val id = entry.gameId.id | ||
| val firstTime = id !in lastStatuses | ||
| val statusChanged = lastStatuses[id] != entry.status | ||
| firstTime || statusChanged || entry.newMoves.isNotEmpty() | ||
| } | ||
|
|
||
| if (changedEntries.isNotEmpty()) { | ||
| val result = sendCb(LatestGamesUpdateResponse(changedEntries)) | ||
| if (result.isClosed) { | ||
| markAsClosed() | ||
| return | ||
| } else if (result.isFailure) { | ||
| logger.error { "failed to send data to live games session $sessionId" } | ||
| return | ||
| } | ||
| } | ||
|
|
||
| // advance the tracked indexes/statuses so the next request only asks for newer moves | ||
| entries.forEach { entry -> | ||
| entry.moveIndex?.let { moveIndexes[entry.gameId.id] = it } | ||
| lastStatuses[entry.gameId.id] = entry.status | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * The batched moves start at [LiveGamesBatchUpdate.batchMoveIndexes] (the lowest index | ||
| * any session needed), so drop the moves this session has already seen. On the first | ||
| * update for a game (no tracked index yet) no moves are animated: the client loads the fen. | ||
| */ | ||
| private fun sliceForSession( | ||
| entry: LatestGamesUpdateResponse.Entry, | ||
| batchMoveIndexes: Map<String, Int>, | ||
| ): LatestGamesUpdateResponse.Entry { | ||
| val tracked = moveIndexes[entry.gameId.id] | ||
| val batchFrom = batchMoveIndexes[entry.gameId.id] | ||
| val newMoves = when { | ||
| tracked == null -> emptyList() | ||
| batchFrom == null -> entry.newMoves | ||
| else -> entry.newMoves.drop(maxOf(0, tracked - batchFrom)) | ||
| } | ||
| return entry.copy(newMoves = newMoves) | ||
| } | ||
|
|
||
| override fun toString() = | ||
| "${javaClass.simpleName}{sessionId=$sessionId, games=${subscribedGameIds.size}}" | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot Update the ShutdownHandler (or something) to handle this then
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done —
ShutdownHandlernow injectsLobbyServiceand callslobbyService.cancel()during the shutdown sequence (commitCancel LobbyService refresher job during shutdown).