Skip to content

Commit 9eb389a

Browse files
committed
Merge branch 'socket'
2 parents be79952 + e6017dd commit 9eb389a

File tree

13 files changed

+788
-51
lines changed

13 files changed

+788
-51
lines changed

.github/workflows/nuxtjs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44
workflow_dispatch:
55
push:
66
branches:
7-
- master
7+
- socket
88
jobs:
99
build:
1010
runs-on: ubuntu-latest

.vscode/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"editor.minimap.renderCharacters": false,
3+
"editor.cursorSmoothCaretAnimation": "on",
4+
"editor.fontLigatures": true
5+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<script lang="ts" setup>
2+
import type { BoardCellServer, Cell, Side } from '~/types/IGameBoardServer';
3+
4+
const props = withDefaults(defineProps<{
5+
boards: BoardCellServer[],
6+
playerTurn: Side|null,
7+
isPlayer: boolean,
8+
isSpectactor: boolean,
9+
playerSide?: Side
10+
}>(), {})
11+
12+
interface CellInput {
13+
x: Cell,
14+
y: Cell
15+
}
16+
17+
const { boards, playerTurn, isPlayer, isSpectactor, playerSide } = useVModels(props)
18+
19+
const emit = defineEmits<{
20+
(e: "draw-cell", cells: CellInput): void
21+
}>()
22+
23+
const drawCell = (cells: CellInput) => {
24+
emit('draw-cell', cells)
25+
}
26+
27+
</script>
28+
<template>
29+
<div class="aspect-square grid grid-cols-3 grid-rows-3 gap-1">
30+
<div v-for="(cell, i) in boards" :key="i"
31+
@click="drawCell({x: cell.x, y: cell.y})"
32+
class="bg-slate-800 rounded-lg text-white flex flex-col justify-center items-center transition-all cursor-pointer hover:ring"
33+
:class="[
34+
{
35+
// empty cell
36+
'hover:ring-blue-800 active:bg-blue-900 fill-blue-600': playerTurn === 'blue' && cell.value === null && !cell.mark && isPlayer && playerSide === 'blue',
37+
'hover:ring-rose-800 active:bg-rose-900': playerTurn === 'red' && cell.value === null && !cell.mark && playerSide === 'red',
38+
'hover:ring-slate-800': cell.value === null && !cell.mark && isSpectactor,
39+
40+
'hover:ring-blue-800 fill-blue-600': cell.value === 'o' && !cell.mark,
41+
'hover:ring-rose-800 fill-rose-600': cell.value === 'x' && !cell.mark,
42+
43+
'hover:ring-0 !bg-slate-900 fill-blue-600': cell.value === 'o' && cell.mark,
44+
'hover:ring-0 !bg-slate-900 fill-red-600': cell.value === 'x' && cell.mark
45+
}
46+
]">
47+
<div v-if="cell.value === 'x'" :class="[{ 'opacity-30': cell.deprecated, 'animate-pulse': cell.mark }]">
48+
<svg xmlns="http://www.w3.org/2000/svg" width="4rem" height="4rem" viewBox="0 0 24 24" style=""><path d="m16.192 6.344-4.243 4.242-4.242-4.242-1.414 1.414L10.535 12l-4.242 4.242 1.414 1.414 4.242-4.242 4.243 4.242 1.414-1.414L13.364 12l4.242-4.242z"></path></svg>
49+
</div>
50+
<div v-else-if="cell.value === 'o'" :class="[{ 'opacity-30': cell.deprecated, 'animate-pulse': cell.mark }]">
51+
<svg xmlns="http://www.w3.org/2000/svg" width="3.2rem" height="3.2rem" viewBox="0 0 24 24" style=""><path d="M12 2C6.486 2 2 6.486 2 12c.001 5.515 4.487 10.001 10 10.001 5.514 0 10-4.486 10.001-10.001 0-5.514-4.486-10-10.001-10zm0 18.001c-4.41 0-7.999-3.589-8-8.001 0-4.411 3.589-8 8-8 4.412 0 8.001 3.589 8.001 8-.001 4.412-3.59 8.001-8.001 8.001z"></path></svg>
52+
</div>
53+
</div>
54+
</div>
55+
</template>
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import type { BoardCell, BoardCellHistory, Cell, Player } from "~/types/IGameBoard"
2+
import type { BoardCellHistoryServer, BoardCellServer, ConnectionServer, PlayerServer, ScoresServer, Side } from "~/types/IGameBoardServer"
3+
4+
5+
let ws: WebSocket | null = null
6+
interface Options {
7+
onDraw?: ()=>void,
8+
onWin?: ()=>void,
9+
}
10+
11+
export const useGameConnection = (options?: Options) => {
12+
13+
const playerTurn = ref<Side|null>(null)
14+
const connectionId = ref<string | undefined>()
15+
const connectionsCount = ref<number>(0)
16+
const boards = ref<BoardCellServer[]>([]);
17+
const scores = ref<ScoresServer>({ blue: 0, red: 0 });
18+
const connections = ref<ConnectionServer[]>([]);
19+
const histories = ref<BoardCellHistoryServer[]>([]);
20+
const players = ref<PlayerServer[]>([])
21+
const isScpectactors = computed(()=> players.value.findIndex(({id}) => id === connectionId.value) === -1)
22+
const isPlayer = computed(()=> players.value.findIndex(({id}) => id === connectionId.value) > -1)
23+
const playerSide = computed(()=> players.value.find(({id}) => id === connectionId.value)?.side)
24+
const winnerPlayer = ref<PlayerServer|undefined>()
25+
const isWinner = computed(()=> winnerPlayer.value?.id === connectionId.value)
26+
const waitingPlayer = computed(()=> players.value.length < 2)
27+
28+
const connect = () => {
29+
30+
const config = useRuntimeConfig()
31+
32+
ws = new WebSocket(config.public.wsBase as string);
33+
34+
// event emmited when connected
35+
ws.onopen = function () {
36+
ws?.send(stringifyData("wants-connection-id", {}));
37+
}
38+
39+
// event emmited when receiving message
40+
ws.onmessage = function (ev) {
41+
const data = ev.data
42+
try {
43+
44+
const objData = bindDataResponse(data)
45+
46+
if (typeof objData !== 'undefined') {
47+
switch (objData.key) {
48+
case 'on-connect':
49+
if (typeof objData.data.id === 'string') {
50+
connectionId.value = objData.data?.id as string | undefined
51+
ws?.send(stringifyData("wants-connections-count", {}))
52+
}
53+
break;
54+
case 'on-connections-update':
55+
if (typeof objData.data?.count === 'number') {
56+
connectionsCount.value = objData.data?.count
57+
connections.value = objData.data?.connections as ConnectionServer[]
58+
}
59+
break;
60+
case 'on-board-update':
61+
if (typeof objData.data?.boards === 'object' && Array.isArray(objData.data?.boards)) {
62+
boards.value = objData.data?.boards as BoardCellServer[]
63+
}
64+
break;
65+
case 'on-score-update':
66+
if (typeof objData.data?.score === 'object') {
67+
scores.value = objData.data?.score as ScoresServer
68+
}
69+
break;
70+
case 'on-turn-update':
71+
if (typeof objData.data?.turn === 'string') {
72+
if(playerTurn.value != objData.data?.turn){
73+
options?.onDraw && options?.onDraw()
74+
}
75+
playerTurn.value = objData.data?.turn as Side
76+
}
77+
break;
78+
case 'on-winner':
79+
if (typeof objData.data?.turn === 'string') {
80+
try {
81+
const playersCloned = useCloned(players)
82+
options?.onWin && options?.onWin()
83+
winnerPlayer.value = playersCloned.cloned.value.find(player => player.side === objData.data?.turn)
84+
} catch (error) {
85+
86+
}
87+
}
88+
break;
89+
case 'on-players-update':
90+
if (typeof objData.data?.players === 'object' && Array.isArray(objData.data?.players)) {
91+
try {
92+
players.value = objData.data?.players as PlayerServer[]
93+
} catch (error) {
94+
}
95+
}
96+
break;
97+
case 'on-board-reset':
98+
winnerPlayer.value = undefined
99+
break;
100+
case 'player-disconnect':
101+
102+
break;
103+
default:
104+
break;
105+
}
106+
}
107+
108+
109+
} catch (error) {
110+
111+
}
112+
}
113+
114+
ws.onerror = () => {
115+
connect()
116+
}
117+
118+
ws.onclose = () => {
119+
connect()
120+
}
121+
ws.addEventListener("player-on-side", (event) => {
122+
123+
})
124+
125+
}
126+
127+
const playerDraw = (position: { x: Cell; y: Cell }): void => {
128+
if(ws?.readyState === ws?.OPEN){
129+
ws?.send(stringifyData("draw-cell", position))
130+
}
131+
};
132+
133+
const loadPlayersAndBoards = (): void => {
134+
if(ws?.readyState === ws?.OPEN){
135+
ws?.send(stringifyData("wants-board-update", {}))
136+
}
137+
};
138+
139+
const disconnect = () => {
140+
ws?.close()
141+
}
142+
const tryConnect = () => {
143+
if (ws?.readyState === ws?.CLOSED || !ws) {
144+
connect()
145+
}
146+
}
147+
148+
return {
149+
connect,
150+
disconnect,
151+
connectionId,
152+
connectionsCount,
153+
playerTurn,
154+
tryConnect,
155+
waitingPlayer,
156+
boards,
157+
histories,
158+
players,
159+
playerDraw,
160+
loadPlayersAndBoards,
161+
isPlayer,
162+
isScpectactors,
163+
winnerPlayer,
164+
isWinner,
165+
playerSide,
166+
connections,
167+
scores,
168+
}
169+
}

nuxt.config.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,12 @@ export default defineNuxtConfig({
44
ssr: false,
55
app: {
66
baseURL: "/tic-tac-toe-nuxt-vue-ts",
7-
buildAssetsDir: "/tic-tac-toe-nuxt-vue-ts/_nuxt",
7+
// buildAssetsDir: "/tic-tac-toe-nuxt-vue-ts/_nuxt",
88
},
99
modules: ["@nuxtjs/tailwindcss", "@vueuse/nuxt"],
1010
imports: {
1111
dirs: ["composables/**"],
1212
},
13-
// devServer: {
14-
// host: "192.168.1.9",
15-
// },
1613
vite: {
1714
optimizeDeps: {
1815
include: ["howler"],
@@ -22,13 +19,19 @@ export default defineNuxtConfig({
2219
runtimeConfig: {
2320
app: {
2421
baseURL: "/tic-tac-toe-nuxt-vue-ts",
25-
buildAssetsDir: "/tic-tac-toe-nuxt-vue-ts/_nuxt",
22+
// buildAssetsDir: "/tic-tac-toe-nuxt-vue-ts/_nuxt",
2623
},
2724
},
28-
prerender: {
29-
// crawlLinks: false,
30-
// routes: [
31-
// ],
25+
prerender: {},
26+
experimental: {
27+
// websocket: true,
28+
// tasks: true,
3229
},
3330
},
31+
runtimeConfig: {
32+
public: {
33+
// wsBase: 'ws://localhost'
34+
wsBase: 'wss://sunrise-blushing-stilton.glitch.me/'
35+
}
36+
}
3437
});

0 commit comments

Comments
 (0)