-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathonSocketConnection.ts
150 lines (141 loc) · 4.89 KB
/
onSocketConnection.ts
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import { Socket } from 'socket.io'
import { BOARD_COLS, BOARD_ROWS } from '@/const/gameConstants'
import {
getMovesInGame,
joinGameOrNewGame,
updateWinningOrTiedGame,
writeNextMove,
} from '@/lib/sqliteDb'
import { GameMove, GameState, PlayStack } from '@/types/dbTypes'
import {
GamePieceBoardState,
GamePieceId,
PlayStates,
} from '@/types/gameStateTypes'
import {
buildBoardState,
getCurrentPlayState,
getInitialGameState,
hasStackCountForWin,
isGameOver,
} from '@/utils/gameUtils'
import {
getWinningPlayerState,
isPlayerTwo,
randomId,
} from '@/utils/socketHelpers'
/**
* Socket Connection Listener.
*
* @param {Socket} socket The connected socket.
* @returns {Promise<void>}
*/
export default async function onSocketConnection(
socket: Socket,
): Promise<void> {
const sessionId = socket.data.sessionId
console.log('Session Id (SERVER)', sessionId)
// If we don't have a session ID we create a new one & join or start a Game.
if (!sessionId) {
console.log('creating new session')
const newSessionId = randomId()
const result = await joinGameOrNewGame(newSessionId)
socket.data.sessionId = newSessionId
socket.data.gameState = result
const moves = await getMovesInGame(result!.id)
const playState = getInitialGameState(socket.data.gameState, moves)
socket.emit('session', {
sessionId: newSessionId,
gameState: socket.data.gameState,
playState,
})
}
// If we have a Game ID, join the associated Game.
const gameId = socket.data.gameState?.id
if (gameId) {
socket.join(`game-${gameId}`)
}
// When a second player joins a Game, inform the first one and update their
// Game State to sync both players.
if (isPlayerTwo(socket.data.sessionId, socket.data.gameState)) {
console.log('Player two joined')
const moves = await getMovesInGame(socket.data.gameState)
const playState = getInitialGameState(socket.data.gameState, moves)
socket
.to(`game-${gameId}`)
.emit('playerTwoJoined', { gameState: socket.data.gameState, playState })
}
console.log('New connection (SERVER)', socket.id)
/**
* Callback function that tries to set a piece, prepare a new Board
* & determines Winning or Tied States. Emits the Game & Play States to the
* other player & returns them to the current one.
*
* @param {GamePieceId} gamePieceId The Piece to set.
* @param callback Callback that returns the Board & Play States to the current player.
*/
const setPiece = async (
gamePieceId: GamePieceId,
callback: ({
boardState,
playState,
}: {
boardState: GamePieceBoardState
playState: PlayStates
}) => void,
) => {
const player = socket.data.sessionId
const gameId = socket.data.gameState?.id
const currentGameState = socket.data.gameState.gameState
// Do we have an OPEN Game and it's ID?
if (gameId && gameId !== -1 && currentGameState === GameState.OPEN) {
// Load all Moves for a given Game ID and get the last one.
const moves = await getMovesInGame(gameId)
const lastMove = moves?.at(-1)
// Do we have a last Move & has it been the opposing player's one?
if (!lastMove || lastMove?.player !== player) {
// Generate the next Move and write it to play_stack.
const move = `${gamePieceId.row},${gamePieceId.col}`
const nextMove: GameMove = { gameId, player, move }
await writeNextMove(nextMove)
// Generate the next Play Stack.
const nextMoveId = moves?.length ? moves.length + 1 : 1
const newMoves: PlayStack[] = [
...((moves as PlayStack[]) || []),
{ id: nextMoveId, ...nextMove },
]
// Generate the next Board State.
const boardState = buildBoardState(newMoves, socket.data.gameState)
let playState
// Do we have a tie?
if (newMoves.length >= BOARD_ROWS * BOARD_COLS) {
playState = PlayStates.playersTied
} else {
// Check for winning states.
const hasWon = hasStackCountForWin(
player,
gamePieceId,
boardState,
socket.data.gameState,
)
console.log('Do we have a winner?', hasWon)
// Determine next Play State.
playState = hasWon
? getWinningPlayerState(player, socket.data.gameState)
: getCurrentPlayState(player, socket.data.gameState)
}
// When we have a winning or tied State update game_stack to FINISHED.
if (isGameOver(playState)) {
await updateWinningOrTiedGame(gameId)
socket.data.gameState.gameState = GameState.FINISHED
}
// Now emit and return the next Board & Play States.
socket
.to(`game-${gameId}`)
.emit('updatedBoard', { boardState, playState })
callback({ boardState, playState })
}
}
}
socket.on('setPiece', setPiece)
}