Skip to content

Commit

Permalink
chore: reorganized GameRepository for better code presentation
Browse files Browse the repository at this point in the history
  • Loading branch information
stoerti committed Sep 23, 2024
1 parent efb7654 commit d0e0b55
Showing 1 changed file with 60 additions and 34 deletions.
94 changes: 60 additions & 34 deletions frontend/src/services/GameRepository.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,37 +31,24 @@ interface GameEventHandler {
}

export class GameRepository {
SOCKET_URL = (process.env.NODE_ENV == 'production') ?
( window.location.protocol == 'https:' ? 'wss://' : 'ws://' )
+ window.location.host + '/ws-message' : 'ws://localhost:8080/ws-message';

client?: Client;
currentGameState: Game | undefined
lastReceivedSeqNo: number = -1

public subscribeToGame(gameId: string, gameEventHandler: GameEventHandler) {
if (this.client == null) {
this.findGame(gameId, () => {
this.client = new Client({
brokerURL: this.SOCKET_URL,
onConnect: () => {
this.client!.subscribe('/game/' + gameId, message => {
const wrapper: GameEventWrapper = JSON.parse(message.body);
console.log(wrapper)
this.handleEvent(wrapper, gameEventHandler)
})
},
onWebSocketError: (e: Event) => {
console.log(e)
}
});
this.client.activate();
})
// find
this.findGame(gameId, () => this.client = this.createStompClient(gameId, gameEventHandler))
} else {
console.log("Client already active - should not happen")
}
}

// STOMP connect URL
SOCKET_URL = (process.env.NODE_ENV == 'production') ?
(window.location.protocol == 'https:' ? 'wss://' : 'ws://')
+ window.location.host + '/ws-message' : 'ws://localhost:8080/ws-message';

public unsubscribeFromGame() {
if (this.client != null) {
this.client.deactivate()
Expand All @@ -73,16 +60,6 @@ export class GameRepository {
}
}

private handleEvent(wrappedEvent: GameEventWrapper, gameEventHandler: GameEventHandler) {
if (wrappedEvent.sequenceNumber <= this.lastReceivedSeqNo) {
console.log("Ignoring event with SeqNo ", wrappedEvent.sequenceNumber)
return
}
this.lastReceivedSeqNo = wrappedEvent.sequenceNumber
this.currentGameState = this.currentGameState!.onGameEvent(wrappedEvent.payload, wrappedEvent.eventType)
gameEventHandler.onGameEvent(wrappedEvent.payload, wrappedEvent.eventType, this.currentGameState)
}

public findGame(gameId: string, successHandler: (game: Game) => void, errorHandler: () => void = () => {
}) {
fetch('/api/game/' + gameId + '/events', {
Expand All @@ -97,10 +74,7 @@ export class GameRepository {
console.log("game does no exist");
errorHandler()
} else {
let game = new Game(events[0].payload as GameCreatedEvent)
for (let i = 1; i < events.length; i++) {
game = game.onGameEvent(events[i].payload, events[i].eventType)
}
const game = this.initializeGameModel(events);
this.currentGameState = game

successHandler(game)
Expand All @@ -111,6 +85,58 @@ export class GameRepository {
errorHandler()
});
}

/**
* Creates a STOMP-JS client and subscribes to messages on specified game channel
* @param gameId the game to subscribe
* @param gameEventHandler the event handler to forward the event and current state to
* @return the activated client
*/
private createStompClient(gameId: string, gameEventHandler: GameEventHandler): Client {
const client = new Client({
brokerURL: this.SOCKET_URL,
onConnect: () => {
this.client!.subscribe('/game/' + gameId, message => {
const wrapper: GameEventWrapper = JSON.parse(message.body);
this.handleEvent(wrapper, gameEventHandler)
})
},
onWebSocketError: (e: Event) => {
console.log(e)
}
});
client.activate();

return client;
}

/**
* Uses the given wrapped event to evolve the current read model state to the next one.
* @param wrappedEvent the wrapped GameEvent
* @param gameEventHandler the eventHandler to forward the result to
*/
private handleEvent(wrappedEvent: GameEventWrapper, gameEventHandler: GameEventHandler) {
if (wrappedEvent.sequenceNumber <= this.lastReceivedSeqNo) {
console.log("Ignoring duplicate event with SeqNo ", wrappedEvent.sequenceNumber)
} else {
this.lastReceivedSeqNo = wrappedEvent.sequenceNumber
// evolve read model to next state
this.currentGameState = this.currentGameState!.onGameEvent(wrappedEvent.payload, wrappedEvent.eventType)
// forward event and new read model state to the eventHandler
gameEventHandler.onGameEvent(wrappedEvent.payload, wrappedEvent.eventType, this.currentGameState)
}
}

private initializeGameModel(events: GameEventWrapper[]) {
// use first event to initialize read model
let game = new Game(events[0].payload as GameCreatedEvent)
// iterate over all following events to evolve the current read model state
for (let i = 1; i < events.length; i++) {
game = game.onGameEvent(events[i].payload, events[i].eventType)
}
return game;
}
}

export const gameRepository = new GameRepository()

0 comments on commit d0e0b55

Please sign in to comment.