You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
hexmap/client/src/ui/DOMWebsocketTranslator.ts

124 lines
3.9 KiB

/** Translates between ws messages and Commands. */
import {ClientCommand} from "../../../common/src/actions/ClientAction";
import {
SERVER_GOODBYE,
SERVER_MALFORMED,
SERVER_SOCKET_STARTUP,
ServerCommand,
ServerGoodbyeAction,
ServerMalformedAction,
ServerSocketStartupAction,
SocketState
} from "../../../common/src/actions/ServerAction";
import {clientToPb} from "../../../common/src/pbconv/ClientToPb";
import {ClientCommandPB} from "../../../common/src/proto/client";
import {Reader} from "protobufjs";
import {ServerCommandPB} from "../../../common/src/proto/server";
import {serverFromPb} from "../../../common/src/pbconv/ServerFromPb";
export class DOMWebsocketTranslator {
readonly url: string
readonly protocols: readonly string[]
readonly onStartup: (startup: ServerSocketStartupAction) => void
readonly onMessage: (command: ServerCommand) => void
readonly onError: (error: ServerMalformedAction) => void
readonly onGoodbye: (goodbye: ServerGoodbyeAction) => void
private socket: WebSocket|null
constructor({
url,
protocols,
onStartup,
onMessage,
onError,
onGoodbye,
}: {
url: string,
protocols: readonly string[],
onStartup: (startup: ServerSocketStartupAction) => void,
onMessage: (command: ServerCommand) => void,
onError: (error: ServerMalformedAction) => void,
onGoodbye: (goodbye: ServerGoodbyeAction) => void,
}) {
this.url = url
this.protocols = protocols.slice()
this.onStartup = onStartup
this.onMessage = onMessage
this.onError = onError
this.onGoodbye = onGoodbye
this.socket = null
}
connect() {
console.log("entering connect()", this)
if (this.socket !== null) {
throw Error("Already running")
}
this.socket = new WebSocket(this.url, this.protocols.slice())
this.socket.binaryType = "arraybuffer"
this.socket.addEventListener("open", () => this.handleOpen())
this.socket.addEventListener("message", (m) => this.handleMessage(m))
this.socket.addEventListener("close", (c) => this.handleClose(c))
this.socket.addEventListener("error", () => this.handleError())
this.onStartup({
type: SERVER_SOCKET_STARTUP,
state: SocketState.CONNECTING,
})
}
send(message: ClientCommand) {
this.socket?.send(ClientCommandPB.encode(clientToPb(message)).finish())
}
close(code: number, reason: string) {
if (this.socket === null || this.socket.readyState !== WebSocket.OPEN) {
return
}
this.socket?.close(code, reason)
}
private handleOpen() {
this.onStartup({
type: SERVER_SOCKET_STARTUP,
state: SocketState.OPEN,
})
}
private handleMessage(e: MessageEvent) {
const data: ArrayBuffer = e.data
const reader: Reader = Reader.create(new Uint8Array(data))
const proto = ServerCommandPB.decode(reader)
let decoded;
try {
decoded = serverFromPb((proto))
} catch (e) {
this.onError({
type: SERVER_MALFORMED,
error: e,
})
return
}
this.onMessage(decoded)
}
private handleError() {
this.onError({
type: SERVER_MALFORMED,
error: null
})
}
private handleClose(e: CloseEvent) {
this.onGoodbye({
type: SERVER_GOODBYE,
code: e.code,
reason: e.reason,
currentTime: new Date(),
})
this.clearSocket()
}
private clearSocket() {
this.socket = null
}
}