diff --git a/client/game/useServerSocket.ts b/client/game/useServerSocket.ts index e1d343b..22a5e09 100644 --- a/client/game/useServerSocket.ts +++ b/client/game/useServerSocket.ts @@ -3,10 +3,10 @@ import useWebSocket from 'react-use-websocket' import assertNever from '~/common/assertNever.ts' -import { useUser } from '../user.tsx' - import { AsyncHandle, GameCommand, GameAction } from './types.ts' +const WS_URL = Deno.env.get("WS_URL") + const MY_ID = (function(){ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); @@ -81,10 +81,9 @@ interface SocketHandle { export default function useServerSocket( onUpdate: (action: GameAction) => void ): AsyncHandle { - const profile = useUser() const [state, dispatch] = React.useReducer(reducer, initialState) - // prep socket + /* prep socket */ const onMessage = React.useCallback((message: WebSocketEventMap['message']) => { const data = JSON.parse(message.data) @@ -157,13 +156,8 @@ export default function useServerSocket( dispatch({type: 'close'}) }, []) - const url = React.useMemo( - () => `ws://localhost:7636/ws`, - [profile], - ) - const {sendJsonMessage} = useWebSocket( - url, + WS_URL, {onMessage, onOpen, onError, onClose, shouldReconnect}, ) @@ -182,7 +176,7 @@ export default function useServerSocket( }) }, [state, sendJson]) - // effects to push the coordinator along + /* effects to push the coordinator along */ React.useEffect(() => { if (state.status !== 'finding-game') return @@ -194,7 +188,7 @@ export default function useServerSocket( sendJson({command: 'join', player_id: state.playerId, match_id: state.matchId}) }, [sendJson, state]) - // return game command handler in wrapper + /* return game command handler in wrapper */ const handle = React.useMemo>(() => { switch (state.status) { diff --git a/run-local.sh b/run-local.sh new file mode 100755 index 0000000..246e1bf --- /dev/null +++ b/run-local.sh @@ -0,0 +1 @@ +WS_URL=ws://localhost:7636 deno run --allow-read --allow-write --unstable --allow-env --allow-net --import-map import_map.json ./server/server.ts diff --git a/server/routes/static.tsx b/server/routes/static.tsx index d559db1..f71acbb 100644 --- a/server/routes/static.tsx +++ b/server/routes/static.tsx @@ -5,6 +5,66 @@ import { Router } from 'oak' // import App from '~/client/App.tsx' +function indexOfAll(source: string, query: string): number[] { + let currentPosition = 0 + const buffer: number[] = [] + while (currentPosition !== -1) { + const lastPosition = currentPosition + currentPosition = source.indexOf(query, lastPosition + 1) + if (currentPosition !== -1) buffer.push(currentPosition) + } + // ensure it's sorted in case while loop betrays us + return buffer.sort() +} + +interface ResolvedEnvVar { + callsiteBounds: [number, number] + varname: string + value?: string +} + +const ENV_SEARCH_STRING = "Deno.env.get(\"" +const ENV_SEARCH_END_STRING = "\")" + +function resolveClientDenoEnvVars(source: string): string { + const startIndices = indexOfAll(source, ENV_SEARCH_STRING) + const resolved = startIndices.map((startIndex) => { + const endIndex = source.indexOf(ENV_SEARCH_END_STRING, startIndex) + const varname = source.slice(startIndex + ENV_SEARCH_STRING.length, endIndex) + const bounds = [startIndex, endIndex + ENV_SEARCH_END_STRING.length] + const value = Deno.env.get(varname) + return { + callsiteBounds: bounds, + varname, + value: `"${value}"`, + } + }) + + function replaceBounds(input: string, newText: string | undefined, start: number, end: number) { + return `${input.slice(0, start)}${newText}${input.slice(end)}` + } + + // reassemble source code by reversing the order and replacing strings + const reassembledSource = resolved.reverse().reduce( + (currentSource, {callsiteBounds, value, varname}) => { + if (value === undefined) + throw new Error(`Deno env variable ${varname} found in client code but not provided to server.`) + return replaceBounds(currentSource, value, callsiteBounds[0], callsiteBounds[1]) + }, + source, + ) + return reassembledSource +} + +function mapDict( + dict: Record, + fn: (entry: [string, T], index: number) => [string, U], +): Record { + return Object.fromEntries( + Object.entries(dict).map(fn) + ) +} + async function createClientBundle() { const {files} = await Deno.emit('server/client.tsx', { bundle: 'module', @@ -16,7 +76,15 @@ async function createClientBundle() { strictPropertyInitialization: false, }, }) - return files + const bundle = mapDict(files, ([path, source], i) => { + const resolved = resolveClientDenoEnvVars(source) + if (Deno.env.get("DEBUG")) { + Deno.writeTextFileSync(`debug/output${i}.js`, source) + Deno.writeTextFileSync(`debug/resolved${i}.js`, resolved) + } + return [path, resolved] + }) + return bundle } const bundle = await createClientBundle()