refactor web
This commit is contained in:
121
web/src/services/gameGateway.ts
Normal file
121
web/src/services/gameGateway.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import type { TickPayload } from '../types/game'
|
||||
|
||||
export interface GatewayHandlers {
|
||||
onTick?: (payload: TickPayload) => void
|
||||
onStatusChange?: (connected: boolean) => void
|
||||
onError?: (error: unknown) => void
|
||||
}
|
||||
|
||||
export interface GatewayOptions {
|
||||
reconnect?: boolean
|
||||
url?: string
|
||||
baseDelay?: number
|
||||
maxDelay?: number
|
||||
}
|
||||
|
||||
export function createGameGateway(
|
||||
handlers: GatewayHandlers,
|
||||
options: GatewayOptions = {}
|
||||
) {
|
||||
const reconnectEnabled = options.reconnect !== false
|
||||
const baseDelay = options.baseDelay ?? 1000
|
||||
const maxDelay = options.maxDelay ?? 8000
|
||||
|
||||
let ws: WebSocket | null = null
|
||||
let reconnectTimer: number | null = null
|
||||
let reconnectAttempts = 0
|
||||
let manuallyClosed = false
|
||||
|
||||
function getUrl() {
|
||||
if (options.url) return options.url
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||
return `${protocol}//${window.location.host}/ws`
|
||||
}
|
||||
|
||||
function clearReconnectTimer() {
|
||||
if (reconnectTimer != null) {
|
||||
window.clearTimeout(reconnectTimer)
|
||||
reconnectTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
function cleanupSocket() {
|
||||
if (!ws) return
|
||||
ws.onopen = null
|
||||
ws.onmessage = null
|
||||
ws.onerror = null
|
||||
ws.onclose = null
|
||||
ws = null
|
||||
}
|
||||
|
||||
function scheduleReconnect() {
|
||||
if (!reconnectEnabled || manuallyClosed) return
|
||||
clearReconnectTimer()
|
||||
const delay = Math.min(maxDelay, baseDelay * 2 ** reconnectAttempts)
|
||||
reconnectAttempts += 1
|
||||
reconnectTimer = window.setTimeout(() => {
|
||||
connect()
|
||||
}, delay)
|
||||
}
|
||||
|
||||
function handleMessage(event: MessageEvent) {
|
||||
try {
|
||||
const data = JSON.parse(event.data)
|
||||
if (data?.type === 'tick') {
|
||||
handlers.onTick?.(data as TickPayload)
|
||||
}
|
||||
} catch (error) {
|
||||
handlers.onError?.(error)
|
||||
}
|
||||
}
|
||||
|
||||
function handleOpen() {
|
||||
reconnectAttempts = 0
|
||||
handlers.onStatusChange?.(true)
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
handlers.onStatusChange?.(false)
|
||||
cleanupSocket()
|
||||
scheduleReconnect()
|
||||
}
|
||||
|
||||
function handleError(error: Event) {
|
||||
handlers.onError?.(error)
|
||||
}
|
||||
|
||||
function connect() {
|
||||
manuallyClosed = false
|
||||
clearReconnectTimer()
|
||||
cleanupSocket()
|
||||
|
||||
try {
|
||||
ws = new WebSocket(getUrl())
|
||||
ws.onopen = handleOpen
|
||||
ws.onmessage = handleMessage
|
||||
ws.onerror = handleError
|
||||
ws.onclose = handleClose
|
||||
} catch (error) {
|
||||
handlers.onError?.(error)
|
||||
scheduleReconnect()
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
manuallyClosed = true
|
||||
clearReconnectTimer()
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.close()
|
||||
} else {
|
||||
cleanupSocket()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
connect,
|
||||
disconnect,
|
||||
get readyState() {
|
||||
return ws?.readyState ?? WebSocket.CLOSED
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user