From 54b91125462609d290c6eba38007f359f893ba77 Mon Sep 17 00:00:00 2001 From: Mari Date: Fri, 16 Jul 2021 15:11:58 -0400 Subject: [PATCH] Partial work on client protobuf-ization --- .idea/hexmap.iml | 1 - client/src/actions/ClientAction.ts | 2 +- client/src/actions/ServerAction.ts | 6 +- client/src/state/NetworkState.ts | 3 +- client/src/websocket/ClientToPb.ts | 6 ++ client/src/websocket/WebsocketTranslator.ts | 96 +++++++++++++++++++++ 6 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 client/src/websocket/ClientToPb.ts create mode 100644 client/src/websocket/WebsocketTranslator.ts diff --git a/.idea/hexmap.iml b/.idea/hexmap.iml index 3787fb9..1accc2b 100644 --- a/.idea/hexmap.iml +++ b/.idea/hexmap.iml @@ -13,7 +13,6 @@ - diff --git a/client/src/actions/ClientAction.ts b/client/src/actions/ClientAction.ts index 907e21f..7f69226 100644 --- a/client/src/actions/ClientAction.ts +++ b/client/src/actions/ClientAction.ts @@ -58,7 +58,7 @@ export function isClientCommand(action: AppAction): action is ClientCommand { return isClientHelloCommand(action) || isClientRefreshCommand(action) || isClientActCommand(action) } -export type ClientAction = ClientHelloCommand | ClientRefreshCommand | ClientPendingAction | ClientActCommand +export type ClientAction = ClientCommand | ClientPendingAction export function isClientAction(action: AppAction): action is ClientAction { return isClientCommand(action) || isClientPendingAction(action) } \ No newline at end of file diff --git a/client/src/actions/ServerAction.ts b/client/src/actions/ServerAction.ts index ea6c3a0..d2027a3 100644 --- a/client/src/actions/ServerAction.ts +++ b/client/src/actions/ServerAction.ts @@ -20,10 +20,10 @@ export const SERVER_GOODBYE = "SERVER_GOODBYE" /** Synthesized out of the close message when the server closes the connection. */ export interface ServerGoodbyeCommand extends BaseAction { readonly type: typeof SERVER_GOODBYE - /** The error code sent with the close message by the server, or -1 if our side crashed.*/ + /** The exit code sent with the close message by the server. */ readonly code: number - /** The text of the server's goodbye message, or the exception if our side crashed. */ - readonly reason: string|Error + /** The text of the server's goodbye message. */ + readonly reason: string /** The current time when this close message was received. */ readonly currentTime: Date } diff --git a/client/src/state/NetworkState.ts b/client/src/state/NetworkState.ts index c6208b2..43373f1 100644 --- a/client/src/state/NetworkState.ts +++ b/client/src/state/NetworkState.ts @@ -50,14 +50,13 @@ export interface NetworkState { /** * The error code of the close message. * Non-null if and only if the current state is OFFLINE or REJECTED. - * -1 means no onClose was called, and the reason is a stringized error from onError instead. */ readonly goodbyeCode: number | null /** * The error reason of the close message. * Non-null if and only if the current state is OFFLINE or REJECTED. */ - readonly goodbyeReason: string | Error | null + readonly goodbyeReason: string | null /** The time the client will attempt to reconnect, if at all. */ readonly autoReconnectAt: Date | null /** The number of attempts at reconnecting. */ diff --git a/client/src/websocket/ClientToPb.ts b/client/src/websocket/ClientToPb.ts new file mode 100644 index 0000000..7a82651 --- /dev/null +++ b/client/src/websocket/ClientToPb.ts @@ -0,0 +1,6 @@ +import {ClientCommand} from "../actions/ClientAction"; +import {ClientCommandPB} from "../proto/client"; + +export function clientToPb(message: ClientCommand): ClientCommandPB { + +} \ No newline at end of file diff --git a/client/src/websocket/WebsocketTranslator.ts b/client/src/websocket/WebsocketTranslator.ts new file mode 100644 index 0000000..01537ae --- /dev/null +++ b/client/src/websocket/WebsocketTranslator.ts @@ -0,0 +1,96 @@ +/** Translates between websocket messages and Commands. */ +import {ClientCommand} from "../actions/ClientAction"; +import { + SERVER_GOODBYE, + SERVER_SOCKET_STARTUP, + ServerCommand, + ServerGoodbyeCommand, + ServerSocketStartupAction, + SocketState +} from "../actions/ServerAction"; + +class WebsocketTranslator { + readonly url: string + readonly protocols: readonly string[] + readonly onStartup: (startup: ServerSocketStartupAction) => void + readonly onMessage: (command: ServerCommand) => void + readonly onGoodbye: (goodbye: ServerGoodbyeCommand) => void + private socket: WebSocket|null + + constructor({ + url, + protocols, + onStartup, + onMessage, + onGoodbye + }: { + url: string, + protocols: readonly string[], + onStartup: (startup: ServerSocketStartupAction) => void, + onMessage: (command: ServerCommand) => void, + onGoodbye: (goodbye: ServerGoodbyeCommand) => void + }) { + this.url = url + this.protocols = protocols.slice() + this.onStartup = onStartup + this.onMessage = onMessage + this.onGoodbye = onGoodbye + this.socket = null + } + + connect() { + if (this.socket != null) { + throw Error("Already running") + } + this.socket = new WebSocket(this.url, this.protocols.slice()) + this.socket.addEventListener("open", this.handleOpen) + this.socket.addEventListener("message", this.handleMessage) + this.socket.addEventListener("close", this.handleClose) + this.socket.addEventListener("error", WebsocketTranslator.handleError) + this.onStartup({ + type: SERVER_SOCKET_STARTUP, + state: SocketState.CONNECTING, + }) + } + + send(message: ClientCommand) { + // TODO: Protoitize the client message and send() it. + } + + close(code: number, reason: string) { + this.socket?.close(code, reason) + } + + private handleOpen(e: Event) { + this.onStartup({ + type: SERVER_SOCKET_STARTUP, + state: SocketState.OPEN, + }) + } + + private handleMessage(e: MessageEvent) { + // TODO: Parse the server message and pass it to onMessage. + } + + private handleClose(e: CloseEvent) { + this.onGoodbye({ + type: SERVER_GOODBYE, + code: e.code, + reason: e.reason, + currentTime: new Date(), + }) + this.clearSocket() + } + + private static handleError(e: Event) { + console.log("Websocket error: ", e) + } + + private clearSocket() { + this.socket?.removeEventListener("open", this.handleOpen) + this.socket?.removeEventListener("message", this.handleMessage) + this.socket?.removeEventListener("close", this.handleClose) + this.socket?.removeEventListener("error", WebsocketTranslator.handleError) + this.socket = null + } +} \ No newline at end of file