diff --git a/src/App.vue b/src/App.vue
index 84e41c3..ea4825e 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,5 +1,10 @@
diff --git a/src/common/interfaces.ts b/src/common/interfaces.ts
index 94778fb..7d9900d 100644
--- a/src/common/interfaces.ts
+++ b/src/common/interfaces.ts
@@ -32,6 +32,10 @@ export interface MatchSessionState {
maxPlayers: number
numPlayers: number
waitingSeconds: number
+ scoreboard: Map
+ matchWinner: PlayerDto | null
+ matchInProgress: boolean
+ playersReady: number
}
export interface GameState {
@@ -48,9 +52,6 @@ export interface GameState {
tileSelectionPhase: boolean
boardFreeEnds: number[]
lastMove: Movement
- scoreboard: Map
- matchWinner: PlayerDto | null
- matchInProgress: boolean
}
export interface Movement {
@@ -79,3 +80,8 @@ export interface Dimension {
x: number
y: number
}
+
+export interface SocketEvent {
+ event: string
+ data: any
+}
diff --git a/src/components/GameComponent.vue b/src/components/GameComponent.vue
index bb7fca4..c1e6114 100644
--- a/src/components/GameComponent.vue
+++ b/src/components/GameComponent.vue
@@ -21,7 +21,7 @@ const game = new Game(
{
width: 1200,
height: 650,
- boardScale: 0.8,
+ boardScale: 0.7,
handScale: 1
},
emit,
@@ -52,7 +52,6 @@ onMounted(async () => {
() => gameStore.gameState,
(value: GameState | undefined) => {
if (value === undefined) return
- logger.debug('gameState-------------------------------------- :>> ', value)
game.board?.setState(value, playerState.value?.id ?? '')
}
)
@@ -61,6 +60,7 @@ onMounted(async () => {
() => gameStore.sessionState,
(value: MatchSessionState | undefined) => {
if (value === undefined) return
+
// logger.debug('gameSessionState-------------------------------------- :>> ', value)
}
)
diff --git a/src/game/Game.ts b/src/game/Game.ts
index 9e386d2..36abe4b 100644
--- a/src/game/Game.ts
+++ b/src/game/Game.ts
@@ -17,7 +17,7 @@ export class Game {
boardScale: 1,
handScale: 1,
width: 1200,
- height: 650
+ height: 800
},
private emit: any,
private socketService: SocketIoClientService,
@@ -27,7 +27,7 @@ export class Game {
async setup(): Promise {
const width = 1200
- const height = 650
+ const height = 800
await this.app.init({ width, height })
return this.app.canvas
@@ -77,7 +77,7 @@ export class Game {
this.hand.on('nextClick', async () => {
await this.socketService.sendMessageWithAck('playerReady', {
- user: this.playerId,
+ userId: this.playerId,
sessionId: this.sessionId
})
})
diff --git a/src/game/Hand.ts b/src/game/Hand.ts
index 8133c48..a2dbd5a 100644
--- a/src/game/Hand.ts
+++ b/src/game/Hand.ts
@@ -48,12 +48,16 @@ export class Hand extends EventEmitter {
gameFinished() {
this.logger.debug('gameFinished')
this.tiles = []
- this.container.removeChildren()
+
this.initialized = false
this.buttonNext = this.createButton(
'NEXT',
{ x: this.width / 2 - 25, y: this.height / 2, width: 50, height: 20 },
- 'nextClick'
+ () => {
+ this.container.removeChildren()
+ this.container.removeChild(this.buttonNext)
+ this.emit('nextClick')
+ }
)
}
diff --git a/src/main.ts b/src/main.ts
index 7397ea3..7f2f964 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -11,6 +11,7 @@ import router from './router'
import { SocketIoClientService } from '@/services/SocketIoClientService'
import { LoggingService } from '@/services/LoggingService'
import { AuthenticationService } from './services/AuthenticationService'
+import { GameService } from './services/GameService'
const app = createApp(App)
@@ -20,5 +21,6 @@ app.use(router)
app.provide('socket', new SocketIoClientService('http://localhost:3000'))
app.provide('logger', new LoggingService())
app.provide('auth', new AuthenticationService())
+app.provide('game', new GameService())
app.mount('#app')
diff --git a/src/managers/SocketIoEventManager.ts b/src/managers/SocketIoEventManager.ts
index 7a086ef..9d1e153 100644
--- a/src/managers/SocketIoEventManager.ts
+++ b/src/managers/SocketIoEventManager.ts
@@ -1,6 +1,6 @@
import { useGameStore } from '@/stores/game'
import { wait } from '@/common/helpers'
-import type { MatchSessionState } from '@/common/interfaces'
+import type { MatchSessionState, SocketEvent } from '@/common/interfaces'
import { storeToRefs } from 'pinia'
import { useEventBusStore } from '@/stores/eventBus'
@@ -8,7 +8,45 @@ export class SocketIoEventManager {
gameStore: any = useGameStore()
eventBus = useEventBusStore()
- handleSessionStateEvent(data: MatchSessionState) {
+ handleGameEvent(gameEvent: SocketEvent) {
+ const { event, data } = gameEvent
+ switch (event) {
+ case 'session-created':
+ this.updateSessionState(data)
+ break
+ case 'game-finished':
+ default:
+ this.eventBus.publish(event, data)
+ break
+ }
+ }
+
+ handleGameEventAck(gameEvent: SocketEvent) {
+ const { event, data } = gameEvent
+ try {
+ switch (event) {
+ case 'update-match-session-state':
+ this.updateSessionState(data)
+ break
+ case 'update-game-state':
+ this.updateGameState(data)
+ break
+ case 'update-player-state':
+ this.updatePlayerState(data)
+ break
+ case 'ask-client-for-move':
+ return this.handleCanMakeMoveEvent(data)
+ default:
+ this.eventBus.publish(event, data)
+ break
+ }
+ return { status: 'ok' }
+ } catch (error) {
+ return { status: 'error', error }
+ }
+ }
+
+ private updateSessionState(data: MatchSessionState) {
const { updateSessionState } = this.gameStore
updateSessionState(data)
return {
@@ -16,7 +54,7 @@ export class SocketIoEventManager {
}
}
- handleGameStateEvent(data: any) {
+ private updateGameState(data: any) {
const { updateGameState } = this.gameStore
updateGameState(data)
return {
@@ -24,7 +62,7 @@ export class SocketIoEventManager {
}
}
- handlePlayerStateEvent(data: any) {
+ private updatePlayerState(data: any) {
const { updatePlayerState } = this.gameStore
updatePlayerState(data)
return {
@@ -32,7 +70,7 @@ export class SocketIoEventManager {
}
}
- async handleCanMakeMoveEvent(data: any) {
+ private async handleCanMakeMoveEvent(data: any) {
const { canMakeMove, moveToMake } = storeToRefs(this.gameStore)
const { updateCanMakeMove, setIncomingFreeEnds } = this.gameStore
setIncomingFreeEnds(data.freeHands)
@@ -46,7 +84,7 @@ export class SocketIoEventManager {
}
}
- async handleCanSelectTileEvent() {
+ private async handleCanSelectTileEvent() {
const { canSelectTile } = storeToRefs(this.gameStore)
const { updateCanSelectTile } = this.gameStore
updateCanSelectTile(true)
@@ -57,8 +95,4 @@ export class SocketIoEventManager {
status: 'ok'
}
}
-
- handleGameFinishedEvent() {
- this.eventBus.publish('game-finished')
- }
}
diff --git a/src/services/AuthenticationService.ts b/src/services/AuthenticationService.ts
index 3544675..c67bb5d 100644
--- a/src/services/AuthenticationService.ts
+++ b/src/services/AuthenticationService.ts
@@ -2,9 +2,9 @@ import { ServiceBase } from '@/services/ServiceBase'
import { useAuthStore } from '@/stores/auth'
import { storeToRefs } from 'pinia'
import { NetworkService } from '@/services/NetworkService'
+import dayjs from 'dayjs'
export class AuthenticationService extends ServiceBase {
- private apiUrl = import.meta.env.VITE_API_URL
private networkService = new NetworkService()
isAuthenticated() {
@@ -28,6 +28,10 @@ export class AuthenticationService extends ServiceBase {
}
async logout() {
+ this.removePersistence()
+ }
+
+ private removePersistence() {
const auth = useAuthStore()
const { setJwt, setUser } = auth
setJwt(undefined)
@@ -53,6 +57,27 @@ export class AuthenticationService extends ServiceBase {
return JSON.parse(window.atob(base64))
}
+ fromStorage() {
+ const token = sessionStorage.getItem('token')
+ if (token) {
+ try {
+ const parsed = this.parseJwt(token)
+ const isAfter = dayjs().isAfter(parsed.exp * 1000)
+ if (isAfter) {
+ this.removePersistence()
+ return
+ }
+ this.persist(token)
+ this.logger.debug('Token loaded from storage', parsed)
+ } catch (error) {
+ this.logger.error(error, 'Error parsing token')
+ this.removePersistence()
+ }
+ } else {
+ this.removePersistence()
+ }
+ }
+
hasRoles(rolesToCheck: string[]) {
const auth = useAuthStore()
const { roles } = storeToRefs(auth)
diff --git a/src/services/GameService.ts b/src/services/GameService.ts
new file mode 100644
index 0000000..36e18e8
--- /dev/null
+++ b/src/services/GameService.ts
@@ -0,0 +1,16 @@
+import { NetworkService } from './NetworkService'
+import { ServiceBase } from './ServiceBase'
+
+export class GameService extends ServiceBase {
+ private networkService = new NetworkService()
+
+ async createMatch(sessionName: string, seed: string) {
+ const response = await this.networkService.post({
+ uri: '/game/match',
+ body: { sessionName, seed },
+ auth: true
+ })
+ const { sessionId } = response
+ return sessionId
+ }
+}
diff --git a/src/services/LoggingService.ts b/src/services/LoggingService.ts
index f648ea3..acb6d9e 100644
--- a/src/services/LoggingService.ts
+++ b/src/services/LoggingService.ts
@@ -27,7 +27,15 @@ export class LoggingService {
} else {
messages.unshift(firstMessage)
}
- console.log(`${logStr.join(' ')}:`, ...messages)
+
+ if (messages.length > 0) {
+ console.log(
+ `${logStr.join(' ')}:`,
+ ...messages.filter((m) => m !== undefined && m !== null)
+ )
+ } else {
+ console.log(logStr.join(' '))
+ }
}
}
}
diff --git a/src/services/NetworkService.ts b/src/services/NetworkService.ts
index d514841..8885df1 100644
--- a/src/services/NetworkService.ts
+++ b/src/services/NetworkService.ts
@@ -82,7 +82,7 @@ export class NetworkService {
'Content-Type': 'application/json'
}
if (auth) {
- headers.Authorization = jwt
+ headers.Authorization = jwt.value
}
return headers
}
diff --git a/src/services/SocketIoClientService.ts b/src/services/SocketIoClientService.ts
index af38ad3..fe195a1 100644
--- a/src/services/SocketIoClientService.ts
+++ b/src/services/SocketIoClientService.ts
@@ -1,29 +1,45 @@
import type { MatchSessionState, GameState, PlayerDto } from '@/common/interfaces'
import { io, Socket } from 'socket.io-client'
import { SocketIoEventManager } from '@/managers/SocketIoEventManager'
-import { LoggingService } from './LoggingService'
+import { useAuthStore } from '@/stores/auth'
+import { storeToRefs } from 'pinia'
+import { ServiceBase } from './ServiceBase'
-export class SocketIoClientService {
- public socket: Socket
+export class SocketIoClientService extends ServiceBase {
+ private socket!: Socket
private isConnected = false
private gameEventManager = new SocketIoEventManager()
- private logger: LoggingService = new LoggingService()
- constructor(url: string) {
- this.socket = io(url)
- this.addEvents()
+ constructor(private url: string) {
+ super()
+ }
+
+ async connect(): Promise {
+ const auth = useAuthStore()
+ const { jwt, isLoggedIn } = storeToRefs(auth)
+ return new Promise((resolve, reject) => {
+ if (!isLoggedIn) {
+ reject('Not logged in')
+ }
+ this.socket = io(this.url, {
+ auth: {
+ token: jwt.value
+ }
+ })
+ this.socket.on('connect', () => {
+ if (this.socket && this.socket.recovered) {
+ console.log('socket recovered succesfully')
+ } else {
+ console.log('socket connected')
+ }
+ this.isConnected = true
+ this.addEvents()
+ resolve()
+ })
+ })
}
addEvents(): void {
- this.socket.on('connect', () => {
- this.isConnected = true
- if (this.socket && this.socket.recovered) {
- console.log('socket recovered succesfully')
- } else {
- console.log('socket connected')
- }
- })
-
this.socket.on('disconnect', () => {
this.isConnected = false
console.log('Disconnected from server')
@@ -39,36 +55,28 @@ export class SocketIoClientService {
console.log('Failed to reconnect to server')
})
- this.socket.on('matchState', (data: MatchSessionState, callback: any) => {
- callback(this.gameEventManager.handleSessionStateEvent(data))
- })
-
- this.socket.on('gameState', (data: GameState, callback: any) => {
- callback(this.gameEventManager.handleGameStateEvent(data))
- })
-
- this.socket.on('playerState', (data: PlayerDto, callback: any) => {
- this.logger.debug('playerState', data)
- callback(this.gameEventManager.handlePlayerStateEvent(data))
- })
-
- this.socket.on('makeMove', async (data: any, callback: any) => {
- callback(await this.gameEventManager.handleCanMakeMoveEvent(data))
- })
-
- this.socket.on('chooseTile', async (data: any, callback: any) => {
- callback(await this.gameEventManager.handleCanSelectTileEvent())
- })
-
- this.socket.on('game-finished', () => {
- this.logger.debug('game-finished event received')
- this.gameEventManager.handleGameFinishedEvent()
- })
-
this.socket.on('ping', () => {
console.log('Ping received from server')
this.socket.emit('pong') // Send pong response
})
+
+ // Custom events
+
+ // this.socket.on('makeMove', async (data: any, callback: any) => {
+ // callback(await this.gameEventManager.handleCanMakeMoveEvent(data))
+ // })
+
+ // this.socket.on('chooseTile', async (data: any, callback: any) => {
+ // callback(await this.gameEventManager.handleCanSelectTileEvent())
+ // })
+
+ this.socket.on('game-event', (data: any) => {
+ this.gameEventManager.handleGameEvent(data)
+ })
+
+ this.socket.on('game-event-ack', async (data: any, callback: any) => {
+ callback(await this.gameEventManager.handleGameEventAck(data))
+ })
}
sendMessage(event: string, data: any): void {
diff --git a/src/stores/game.ts b/src/stores/game.ts
index 5ec71a0..52d9fb4 100644
--- a/src/stores/game.ts
+++ b/src/stores/game.ts
@@ -15,6 +15,12 @@ export const useGameStore = defineStore('game', () => {
const showReadyButton = ref(false)
const isSessionStarted = computed(() => sessionState.value !== undefined)
+ const amIHost = computed(
+ () =>
+ sessionState.value !== undefined &&
+ playerState.value !== undefined &&
+ playerState.value.id === sessionState.value.creator
+ )
function updateSessionState(newState: MatchSessionState) {
sessionState.value = newState
@@ -77,6 +83,7 @@ export const useGameStore = defineStore('game', () => {
setShowReadyButton,
setReadyForStart,
updateGameFinished,
- isSessionStarted
+ isSessionStarted,
+ amIHost
}
})
diff --git a/src/views/GameView.vue b/src/views/GameView.vue
index 0ca4182..bc88cdc 100644
--- a/src/views/GameView.vue
+++ b/src/views/GameView.vue
@@ -60,7 +60,7 @@ function copySeed() {
FreeEnds: {{ gameState?.boardFreeEnds }} - Current Player:{{
gameState?.currentPlayer?.name
}}
- - Score: {{ gameState?.scoreboard }}
+ - Score: {{ sessionState?.scoreboard }}
SessionID: {{ sessionState.id }} PlayerID: {{ playerState?.id }}
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
index 2371e88..18301f4 100644
--- a/src/views/HomeView.vue
+++ b/src/views/HomeView.vue
@@ -1,21 +1,24 @@