From 6d3343d0647ae1a78e61929ba8a6a9d95223187b Mon Sep 17 00:00:00 2001 From: snen Date: Fri, 24 Sep 2021 00:47:45 -0400 Subject: [PATCH] Edit page details, start integrating socket --- client/game/Game.tsx | 16 +++----- client/game/useSocket.ts | 82 ++++++++++++++++++++++++++++++++-------- client/pages/AppPage.tsx | 34 +++++++++++++---- client/styles.css | 8 ++-- common/user.ts | 2 +- server/routes/static.tsx | 4 +- 6 files changed, 104 insertions(+), 42 deletions(-) diff --git a/client/game/Game.tsx b/client/game/Game.tsx index 99c8b4b..a0169a4 100644 --- a/client/game/Game.tsx +++ b/client/game/Game.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from 'react' -import useWebSocket from 'react-use-websocket' +import useSocket from './useSocket.ts' import {CardInstance} from './types.ts' interface GameActionsContextValue {} @@ -39,19 +39,13 @@ export default function Game(props: GameProps): JSX.Element { const [state, setState] = React.useState() // ensure this is stable wrt state so that onMessage does not have to be constantly reattached - const onMessage = React.useCallback(() => {}, []) + // const onMessage = React.useCallback(() => {}, []) - const WS_URL = `ws://localhost:7636/ws` - const socket = useWebSocket(WS_URL, {onMessage}) - useEffect(() => { - setInterval(() => console.log(socket.getWebSocket()), 3000) - }, [socket]) - - const gameActions = React.useMemo(() => ({}), [socket.sendJsonMessage]) + // const handle = useSocket() return ( - -
+ +
Hello world!
) } diff --git a/client/game/useSocket.ts b/client/game/useSocket.ts index dd0e3d4..2d9cc5b 100644 --- a/client/game/useSocket.ts +++ b/client/game/useSocket.ts @@ -15,17 +15,50 @@ type AsyncHandle = | { status: 'connecting' } | { status: 'connected'; handle: T} -interface SocketAPI {} +interface SessionCommandAPI { + query: () => void + join: () => void + leave: () => void + play: () => void + poll: () => void +} + +interface GameCommandAPI { + act: () => void, + getState: () => void, + debug: () => void, +} + +// TODO +type GameCommandEnum = 'a' | 's' | 'd' + +interface SocketMessage { + playerId?: string + matchId?: string + command: keyof SessionCommandAPI + gameCommand?: { + playerId: string + type: GameCommandEnum, + cmd: unknown + } +} type State = | { status: 'not-connected' } | { status: 'connecting' } - | { status: 'connected'} + | { status: 'finding-game' } + | { status: 'in-game'; playerId: string; matchId: string } + +type Action = + | { type: 'open' } + | { type: 'close' } + | { type: 'error' } + | { type: 'game-found'; matchId: string; playerId: string } -type Action = 'open' | 'close' | 'error' function reducer(_state: State, action: Action): State { - switch (action) { - case 'open': return { status: 'connected' } + switch (action.type) { + case 'open': return { status: 'finding-game' } + case 'game-found': return { status: 'in-game', matchId: action.matchId, playerId: action.playerId } case 'close': return { status: 'not-connected' } case 'error': return { status: 'connecting' } default: return assertNever(action) @@ -34,8 +67,8 @@ function reducer(_state: State, action: Action): State { const initialState: State = {status: 'not-connected'} -export default function useSocket(): AsyncHandle { - const _user = useUser() +export default function useSocket(): AsyncHandle { + const profile = useUser() const [state, dispatch] = React.useReducer(reducer, initialState) const onMessage = React.useCallback((message: WebSocketEventMap['message']) => { @@ -45,36 +78,53 @@ export default function useSocket(): AsyncHandle { const onOpen = React.useCallback(() => { console.log('socket opened') - dispatch('open') + dispatch({type: 'open'}) }, []) const onError = React.useCallback((event: WebSocketEventMap['error']) => { console.error(event) - dispatch('error') + dispatch({type: 'error'}) }, []) const onClose = React.useCallback((_event: WebSocketEventMap['close']) => { console.log('socket closed') - dispatch('close') + dispatch({type: 'close'}) }, []) - const url = React.useMemo(() => - `ws://${window.location.hostname}${window.location.port ? `:${window.location.port}` : ''}/api/ws?name=${name}`, - [], + const url = React.useMemo( + // () => `ws://arcade.saintnet.tech:7636/ws?name=${profile.displayName}`, + () => `ws://arcade.saintnet.tech:7636/ws`, + [profile], ) const socket = useWebSocket( url, {onMessage, onOpen, onError, onClose, shouldReconnect}, ) + const sendJson = React.useCallback((message: SocketMessage) => { + socket.send(JSON.stringify(message)) + }, [socket]) + const handle = React.useMemo(() => ({ - sendJson: (value: {}) => socket.send(JSON.stringify(value)), - }), [socket]) + // session commands + query: () => {}, + join: () => {}, + leave: () => {}, + play: () => {}, + poll: () => {}, + // game commands + act: () => {}, + getState: () => {}, + debug: () => {}, + }), [sendJson]) switch (state.status) { - case 'connected': { + case 'in-game': { return {status: 'connected', handle} } + case 'finding-game': { + return {status: 'connecting'} + } case 'connecting': case 'not-connected': return state diff --git a/client/pages/AppPage.tsx b/client/pages/AppPage.tsx index 48950ad..98d8d20 100644 --- a/client/pages/AppPage.tsx +++ b/client/pages/AppPage.tsx @@ -1,13 +1,33 @@ import React from 'react' +import assertNever from '~/common/assertNever.ts' + import IntroPage from '../components/IntroPage.tsx' +import Page from '../components/Page.tsx' +import Game from '../game/Game.tsx' + +type MenuState = 'menu' | 'play' export default function AppPage() { - return ( - -
-

Hello world!

-
-
- ) + const [menuState, setMenuState] = React.useState('menu') + + switch (menuState) { + case 'menu': { + return ( + + + + ) + } + case 'play': { + return ( + + + + ) + } + default: return assertNever(menuState) + } } diff --git a/client/styles.css b/client/styles.css index adab013..f4538f6 100644 --- a/client/styles.css +++ b/client/styles.css @@ -98,10 +98,10 @@ h2 { .header { height: 90px; flex: 0 0 auto; - background-color: #4a6670; + background-color: #3454d1; padding-left: 2em; padding-right: 2em; - box-shadow: 0 1em 1em rgba(0, 0, 0, 0.25); + box-shadow: 2px 8px 4px 1px rgba(0, 0, 0, 0.25); display: flex; align-items: center; @@ -109,9 +109,8 @@ h2 { } .header-text { - font-size: 32px; font-family: sans-serif; - color: #18212b; + color: #ddf6fd; } .main { @@ -119,7 +118,6 @@ h2 { display: flex; align-items: center; justify-content: center; - background-color: #e6eaef; } .frame { diff --git a/common/user.ts b/common/user.ts index 917ca98..a56c31d 100644 --- a/common/user.ts +++ b/common/user.ts @@ -1,3 +1,3 @@ export interface UserProfile { + displayName: string } - \ No newline at end of file diff --git a/server/routes/static.tsx b/server/routes/static.tsx index 9539460..d559db1 100644 --- a/server/routes/static.tsx +++ b/server/routes/static.tsx @@ -40,8 +40,8 @@ const html = ` - Deno React Web Starter - + Tomecraft +