diff --git a/app.vue b/app.vue index 00c8942..814ae35 100644 --- a/app.vue +++ b/app.vue @@ -1,12 +1,18 @@ @@ -14,7 +20,7 @@ const req = await userStore.whoami() Loading... - + diff --git a/components/conceptHeader.vue b/components/conceptHeader.vue new file mode 100644 index 0000000..047edf1 --- /dev/null +++ b/components/conceptHeader.vue @@ -0,0 +1,71 @@ + + + + + + + + + + Logout + + Login + + + + Contacting server... + + + + Home + Words + + + + Back to Current Game + + + + + diff --git a/components/header.vue b/components/header.vue deleted file mode 100644 index 95d5054..0000000 --- a/components/header.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - Logout - - Login - - - - - Contacting server... - - - - - Home - Words - - - - - - \ No newline at end of file diff --git a/components/lobby/game.vue b/components/lobby/game.vue new file mode 100644 index 0000000..2295307 --- /dev/null +++ b/components/lobby/game.vue @@ -0,0 +1,29 @@ + + + + {{ game }} + + + + diff --git a/lobby.events.ts b/lobby.events.ts deleted file mode 100644 index 3a98aa2..0000000 --- a/lobby.events.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { GameResponseDTO } from './dto/gameresponse.dto'; -import { PlayerResponseDTO } from './dto/playerresponse.dto'; - -export class GameCreateEvent extends GameResponseDTO { - type: 'create'; -} - -export class GameDeleteEvent { - type: 'delete'; - constructor(public id?: number) {} -} -export class GameJoinEvent { - type: 'joingame'; - gameId: number; - constructor( - public player: PlayerResponseDTO, - game?: Partial, - ) { - this.gameId = game.id; - } -} diff --git a/middleware/auth.global.ts b/middleware/auth.global.ts index 36f5e08..9b76e65 100644 --- a/middleware/auth.global.ts +++ b/middleware/auth.global.ts @@ -2,18 +2,19 @@ const validPaths = ["/", "/rules", "/test", "/words"]; export default defineNuxtRouteMiddleware(async (to, from) => { const config = useRuntimeConfig(); + if (to.path.startsWith("/game/")) { + return; + } if (to.path == "/login") { - console.log("hey") const code = to.query.code; if (!code) { navigateTo(config.public.discordLoginEndpoint, { external: true }); } else { navigateTo("/login"); } - return + return; } - if (validPaths.includes(to.path)) { return; } diff --git a/netcode/events b/netcode/events index 72935ba..49adbde 160000 --- a/netcode/events +++ b/netcode/events @@ -1 +1 @@ -Subproject commit 72935ba3f59fa8b5399207937f28cc686e5239f6 +Subproject commit 49adbdec5b00604e725d1809a36a8bb94d4e9576 diff --git a/netcode/handlers/lobby.handler.ts b/netcode/handlers/lobby.handler.ts new file mode 100644 index 0000000..34e90d2 --- /dev/null +++ b/netcode/handlers/lobby.handler.ts @@ -0,0 +1,28 @@ +import { useLobbyStore } from "~/store/lobby.store"; +import type { LobbyCreateEvent, LobbyDeleteEvent, LobbyJoinEvent, LobbyLeaveEvent, LobbyStartEvent } from "../events/lobby.events"; + + + +export const handlers = { + create: (event: LobbyCreateEvent) => { + const lobbyStore = useLobbyStore(); + lobbyStore.games[event.game.id] = event.game + }, + start: (event:LobbyStartEvent)=>{ + const lobbyStore = useLobbyStore() + lobbyStore.games[event.game.id].started = true + }, + delete:(event: LobbyDeleteEvent)=>{ + const lobbyStore = useLobbyStore() + delete lobbyStore.games[event.id] + }, + join:(event: LobbyJoinEvent)=>{ + const lobbyStore = useLobbyStore() + lobbyStore.games[event.game.id].players.push(event.player) + }, + leave:(event: LobbyLeaveEvent)=>{ + const lobbyStore = useLobbyStore() + const playerIndex = lobbyStore.games[event.game.id].players.findIndex(p=>p.user.id == event.player.user.id) + lobbyStore.games[event.game.id].players.splice(playerIndex,1) + } +}; diff --git a/pages/game/[id].vue b/pages/game/[id].vue new file mode 100644 index 0000000..773d3dd --- /dev/null +++ b/pages/game/[id].vue @@ -0,0 +1 @@ +Hello diff --git a/pages/index.vue b/pages/index.vue index eb3d181..940a952 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -1,26 +1,38 @@ - - + + + + diff --git a/pages/words.vue b/pages/words.vue index 7c90638..14803fc 100644 --- a/pages/words.vue +++ b/pages/words.vue @@ -1,6 +1,4 @@ diff --git a/service/game.service.ts b/service/game.service.ts deleted file mode 100644 index 2aa5674..0000000 --- a/service/game.service.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { GameResponseDTO } from "~/netcode/events/dto/gameresponse.dto"; - -export async function getGames() { - const config = useRuntimeConfig(); - const data = await useFetch( - config.public.endpoint + `/games`, - { - credentials: "include", - immediate: true, - } - ); - return data.data -} diff --git a/store/lobby.store.ts b/store/lobby.store.ts index a8cf455..283d38e 100644 --- a/store/lobby.store.ts +++ b/store/lobby.store.ts @@ -1,25 +1,55 @@ import { defineStore } from "pinia"; +import { subscribe } from "~/netcode"; import type { GameResponseDTO } from "~/netcode/events/dto/gameresponse.dto"; -import { UserResponseDTO } from "~/netcode/events/dto/userresponse.dto"; +import { handlers } from "~/netcode/handlers/lobby.handler"; export const useLobbyStore = defineStore("lobby", { - state: () => ({ - games: {} as Record(config.public.endpoint + `/users/` + id, { - credentials: "include", - immediate: true - }) - if(data.data.value){ - for(const game of data.data.value){ - this.games[game.id] = game - } - } - - return data.data - }, + state: () => ({ + games: {} as Record, + eventSource: null as null | EventSource, + }), + actions: { + async subscribe() { + this.eventSource = subscribe(`/games/subscribe`); + this.eventSource.addEventListener("message", async (message) => { + const data = JSON.parse(message.data); + const type = data.type as string; + if (type in handlers) { + handlers[type as keyof typeof handlers](data); + } + }); }, -}); \ No newline at end of file + async close() { + this.eventSource?.close(); + }, + async fetchGames() { + const config = useRuntimeConfig(); + const data = await useFetch( + config.public.endpoint + `/games/`, + { + credentials: "include", + immediate: true, + } + ); + if (data.data.value) { + for (const game of data.data.value) { + this.games[game.id] = game; + } + } + + return data.data; + }, + async createGame() { + const config = useRuntimeConfig(); + const data = await $fetch( + config.public.endpoint + "/games", + { + method: "POST", + credentials: "include", + } + ); + this.games[data.id] = data; + return data; + }, + }, +}); diff --git a/store/word.store.ts b/store/word.store.ts index 06d743e..4ecfc82 100644 --- a/store/word.store.ts +++ b/store/word.store.ts @@ -1,60 +1,90 @@ import { defineStore } from "pinia"; +import { subscribe } from "~/netcode"; import { WordResponseDTO } from "~/netcode/events/dto/wordresponse.dto"; +import { handlers } from "~/netcode/handlers/word.handler"; export const useWordStore = defineStore("word", { - state: () => ({ - words: {} as Record, - }), - actions: { - async fetchWordList() { - const config = useRuntimeConfig(); - const data = await useFetch(config.public.endpoint + `/words/all`, { - credentials: "include", - }) - if (data.data.value && !data.error.value) { - - for (const word of data.data.value) { - this.words[word.id] = word - } - } - return data - }, - async enableWord(word: WordResponseDTO) { - const config = useRuntimeConfig(); - const data = await $fetch(config.public.endpoint + `/words/` + word.id + "/enable", { - credentials: "include", - method: "PUT" - }) - word.enabled = true - return data - }, - async disableWord(word: WordResponseDTO) { - const config = useRuntimeConfig(); - const data = await $fetch(config.public.endpoint + `/words/` + word.id + "/disable", { - credentials: "include", - method: "PUT" - }) - word.enabled = false - return data - }, - - async createWord(value: string) { - const config = useRuntimeConfig(); - const data = await $fetch(config.public.endpoint + `/words/`, { - credentials: "include", - body: { value }, - method: "POST" - }) - this.words[data.id] = data - return data - }, - async deleteWord(word: WordResponseDTO){ - const config = useRuntimeConfig(); - const data = await $fetch(config.public.endpoint + `/words/`+word.id, { - credentials: "include", - method: "DELETE" - }) - delete this.words[word.id] + state: () => ({ + words: {} as Record, + eventSource: null as null | EventSource, + }), + actions: { + async subscribe() { + this.eventSource = subscribe(`/words/subscribe`); + this.eventSource.addEventListener("message", async (message) => { + const data = JSON.parse(message.data); + const type = data.type as string; + if (type in handlers) { + handlers[type as keyof typeof handlers](data); } + }); }, -}); \ No newline at end of file + async close() { + this.eventSource?.close(); + }, + async fetchWordList() { + const config = useRuntimeConfig(); + const data = await useFetch( + config.public.endpoint + `/words/all`, + { + credentials: "include", + } + ); + if (data.data.value && !data.error.value) { + for (const word of data.data.value) { + this.words[word.id] = word; + } + } + return data; + }, + async enableWord(word: WordResponseDTO) { + const config = useRuntimeConfig(); + const data = await $fetch( + config.public.endpoint + `/words/` + word.id + "/enable", + { + credentials: "include", + method: "PUT", + } + ); + word.enabled = true; + return data; + }, + async disableWord(word: WordResponseDTO) { + const config = useRuntimeConfig(); + const data = await $fetch( + config.public.endpoint + `/words/` + word.id + "/disable", + { + credentials: "include", + method: "PUT", + } + ); + word.enabled = false; + return data; + }, + + async createWord(value: string) { + const config = useRuntimeConfig(); + const data = await $fetch( + config.public.endpoint + `/words/`, + { + credentials: "include", + body: { value }, + method: "POST", + } + ); + this.words[data.id] = data; + return data; + }, + async deleteWord(word: WordResponseDTO) { + const config = useRuntimeConfig(); + const data = await $fetch( + config.public.endpoint + `/words/` + word.id, + { + credentials: "include", + method: "DELETE", + } + ); + delete this.words[word.id]; + }, + }, +});