Edit page details, start integrating socket
This commit is contained in:
parent
4bcb0e947d
commit
6d3343d064
@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import useWebSocket from 'react-use-websocket'
|
|
||||||
|
|
||||||
|
import useSocket from './useSocket.ts'
|
||||||
import {CardInstance} from './types.ts'
|
import {CardInstance} from './types.ts'
|
||||||
|
|
||||||
interface GameActionsContextValue {}
|
interface GameActionsContextValue {}
|
||||||
@ -39,19 +39,13 @@ export default function Game(props: GameProps): JSX.Element {
|
|||||||
const [state, setState] = React.useState()
|
const [state, setState] = React.useState()
|
||||||
|
|
||||||
// ensure this is stable wrt state so that onMessage does not have to be constantly reattached
|
// 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 handle = useSocket()
|
||||||
const socket = useWebSocket(WS_URL, {onMessage})
|
|
||||||
useEffect(() => {
|
|
||||||
setInterval(() => console.log(socket.getWebSocket()), 3000)
|
|
||||||
}, [socket])
|
|
||||||
|
|
||||||
const gameActions = React.useMemo<GameActionsContextValue>(() => ({}), [socket.sendJsonMessage])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GameActionsContext.Provider value={gameActions}>
|
<GameActionsContext.Provider value={{}}>
|
||||||
<div />
|
<div>Hello world!</div>
|
||||||
</GameActionsContext.Provider>
|
</GameActionsContext.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -15,17 +15,50 @@ type AsyncHandle<T> =
|
|||||||
| { status: 'connecting' }
|
| { status: 'connecting' }
|
||||||
| { status: 'connected'; handle: T}
|
| { 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 =
|
type State =
|
||||||
| { status: 'not-connected' }
|
| { status: 'not-connected' }
|
||||||
| { status: 'connecting' }
|
| { 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 {
|
function reducer(_state: State, action: Action): State {
|
||||||
switch (action) {
|
switch (action.type) {
|
||||||
case 'open': return { status: 'connected' }
|
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 'close': return { status: 'not-connected' }
|
||||||
case 'error': return { status: 'connecting' }
|
case 'error': return { status: 'connecting' }
|
||||||
default: return assertNever(action)
|
default: return assertNever(action)
|
||||||
@ -34,8 +67,8 @@ function reducer(_state: State, action: Action): State {
|
|||||||
|
|
||||||
const initialState: State = {status: 'not-connected'}
|
const initialState: State = {status: 'not-connected'}
|
||||||
|
|
||||||
export default function useSocket(): AsyncHandle<SocketAPI> {
|
export default function useSocket(): AsyncHandle<GameCommandAPI> {
|
||||||
const _user = useUser()
|
const profile = useUser()
|
||||||
const [state, dispatch] = React.useReducer(reducer, initialState)
|
const [state, dispatch] = React.useReducer(reducer, initialState)
|
||||||
|
|
||||||
const onMessage = React.useCallback((message: WebSocketEventMap['message']) => {
|
const onMessage = React.useCallback((message: WebSocketEventMap['message']) => {
|
||||||
@ -45,36 +78,53 @@ export default function useSocket(): AsyncHandle<SocketAPI> {
|
|||||||
|
|
||||||
const onOpen = React.useCallback(() => {
|
const onOpen = React.useCallback(() => {
|
||||||
console.log('socket opened')
|
console.log('socket opened')
|
||||||
dispatch('open')
|
dispatch({type: 'open'})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const onError = React.useCallback((event: WebSocketEventMap['error']) => {
|
const onError = React.useCallback((event: WebSocketEventMap['error']) => {
|
||||||
console.error(event)
|
console.error(event)
|
||||||
dispatch('error')
|
dispatch({type: 'error'})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const onClose = React.useCallback((_event: WebSocketEventMap['close']) => {
|
const onClose = React.useCallback((_event: WebSocketEventMap['close']) => {
|
||||||
console.log('socket closed')
|
console.log('socket closed')
|
||||||
dispatch('close')
|
dispatch({type: 'close'})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const url = React.useMemo(() =>
|
const url = React.useMemo(
|
||||||
`ws://${window.location.hostname}${window.location.port ? `:${window.location.port}` : ''}/api/ws?name=${name}`,
|
// () => `ws://arcade.saintnet.tech:7636/ws?name=${profile.displayName}`,
|
||||||
[],
|
() => `ws://arcade.saintnet.tech:7636/ws`,
|
||||||
|
[profile],
|
||||||
)
|
)
|
||||||
const socket = useWebSocket(
|
const socket = useWebSocket(
|
||||||
url,
|
url,
|
||||||
{onMessage, onOpen, onError, onClose, shouldReconnect},
|
{onMessage, onOpen, onError, onClose, shouldReconnect},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const sendJson = React.useCallback((message: SocketMessage) => {
|
||||||
|
socket.send(JSON.stringify(message))
|
||||||
|
}, [socket])
|
||||||
|
|
||||||
const handle = React.useMemo(() => ({
|
const handle = React.useMemo(() => ({
|
||||||
sendJson: (value: {}) => socket.send(JSON.stringify(value)),
|
// session commands
|
||||||
}), [socket])
|
query: () => {},
|
||||||
|
join: () => {},
|
||||||
|
leave: () => {},
|
||||||
|
play: () => {},
|
||||||
|
poll: () => {},
|
||||||
|
// game commands
|
||||||
|
act: () => {},
|
||||||
|
getState: () => {},
|
||||||
|
debug: () => {},
|
||||||
|
}), [sendJson])
|
||||||
|
|
||||||
switch (state.status) {
|
switch (state.status) {
|
||||||
case 'connected': {
|
case 'in-game': {
|
||||||
return {status: 'connected', handle}
|
return {status: 'connected', handle}
|
||||||
}
|
}
|
||||||
|
case 'finding-game': {
|
||||||
|
return {status: 'connecting'}
|
||||||
|
}
|
||||||
case 'connecting':
|
case 'connecting':
|
||||||
case 'not-connected':
|
case 'not-connected':
|
||||||
return state
|
return state
|
||||||
|
@ -1,13 +1,33 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import assertNever from '~/common/assertNever.ts'
|
||||||
|
|
||||||
import IntroPage from '../components/IntroPage.tsx'
|
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() {
|
export default function AppPage() {
|
||||||
return (
|
const [menuState, setMenuState] = React.useState<MenuState>('menu')
|
||||||
<IntroPage>
|
|
||||||
<div>
|
switch (menuState) {
|
||||||
<p>Hello world!</p>
|
case 'menu': {
|
||||||
</div>
|
return (
|
||||||
</IntroPage>
|
<IntroPage>
|
||||||
)
|
<button onClick={() => setMenuState('play')}>
|
||||||
|
Play
|
||||||
|
</button>
|
||||||
|
</IntroPage>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case 'play': {
|
||||||
|
return (
|
||||||
|
<Page>
|
||||||
|
<Game />
|
||||||
|
</Page>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
default: return assertNever(menuState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,10 +98,10 @@ h2 {
|
|||||||
.header {
|
.header {
|
||||||
height: 90px;
|
height: 90px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
background-color: #4a6670;
|
background-color: #3454d1;
|
||||||
padding-left: 2em;
|
padding-left: 2em;
|
||||||
padding-right: 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;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -109,9 +109,8 @@ h2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.header-text {
|
.header-text {
|
||||||
font-size: 32px;
|
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
color: #18212b;
|
color: #ddf6fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
@ -119,7 +118,6 @@ h2 {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: #e6eaef;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.frame {
|
.frame {
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export interface UserProfile {
|
export interface UserProfile {
|
||||||
|
displayName: string
|
||||||
}
|
}
|
||||||
|
|
@ -40,8 +40,8 @@ const html = `
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Deno React Web Starter</title>
|
<title>Tomecraft</title>
|
||||||
<meta name="description" content="Template for SSR React apps with Deno, Oak, and Firebase." />
|
<meta name="description" content="Tactical CCG" />
|
||||||
<link href="styles.css" rel="stylesheet" type="text/css">
|
<link href="styles.css" rel="stylesheet" type="text/css">
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
|
Loading…
Reference in New Issue
Block a user