-
Notifications
You must be signed in to change notification settings - Fork 1k
User-friendly error on ws close 1002: translate most + explain ways to fix turnstile token invalid/rejected #3863
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b05f8ea
9dadb1e
38c345c
06b35c5
035257f
460c456
eb34fe8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,11 +24,14 @@ import { | |
| ServerMessage, | ||
| ServerMessageSchema, | ||
| Winner, | ||
| WSError, | ||
| WSErrorSchema, | ||
| } from "../core/Schemas"; | ||
| import { replacer } from "../core/Util"; | ||
| import { getPlayToken } from "./Auth"; | ||
| import { LobbyConfig } from "./ClientGameRunner"; | ||
| import { LocalServer } from "./LocalServer"; | ||
| import { getEnglishText, translateText } from "./Utils"; | ||
|
|
||
| export class PauseGameIntentEvent implements GameEvent { | ||
| constructor(public readonly paused: boolean) {} | ||
|
|
@@ -177,6 +180,7 @@ export class SendStartGameEvent implements GameEvent {} | |
|
|
||
| export class Transport { | ||
| private socket: WebSocket | null = null; | ||
| private disconnectWSError: WSError | null = null; | ||
|
|
||
| private localServer: LocalServer; | ||
|
|
||
|
|
@@ -333,6 +337,7 @@ export class Transport { | |
| const workerPath = this.lobbyConfig.serverConfig.workerPath( | ||
| this.lobbyConfig.gameID, | ||
| ); | ||
| this.disconnectWSError = null; | ||
| this.socket = new WebSocket(`${wsProtocol}//${wsHost}/${workerPath}`); | ||
| this.onconnect = onconnect; | ||
| this.onmessage = onmessage; | ||
|
|
@@ -358,6 +363,12 @@ export class Transport { | |
| const parsed = JSON.parse(event.data); | ||
| const result = ServerMessageSchema.safeParse(parsed); | ||
| if (!result.success) { | ||
| const wsErrorResult = WSErrorSchema.safeParse(parsed); | ||
| if (wsErrorResult.success) { | ||
| this.disconnectWSError = wsErrorResult.data; | ||
| return; | ||
| } | ||
|
|
||
| const error = z.prettifyError(result.error); | ||
| console.error("Error parsing server message", error); | ||
| return; | ||
|
|
@@ -378,8 +389,42 @@ export class Transport { | |
| `WebSocket closed. Code: ${event.code}, Reason: ${event.reason}`, | ||
| ); | ||
| if (event.code === 1002) { | ||
| const connRefusedKey = `worker_error.connection_refused`; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a bit messy. I'm thinking we do something like create a zod schema for websocket error and it's just something like: const ErrorSchema = z.object({ there's a 123 byte limit, so maybe we should send the error and then close: import { z } from "zod"; export const WSErrorSchema = z.object({ export type WSError = z.infer; export function sendErrorAndClose(ws: WebSocket, error: WSError, code = 4000) {
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added it. Don't think it is much less 'messy'. Because we still need to test stuff, since we don't know if A) we recieved an actual translation key and not just a raw error string like "ClientJoinMessageSchema", "WS_ERR_UNEXPECTED_RSV_1" and in To clarify it a bit more, i could rename "translationKey" in WSErrorSchema and the const in Transport.ts to something like "translationKeyOrError" because it can contain both.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah let's just not support args and then it's really simple like: ws.close(1002, worker_error.game_not_found) and then here we just do: alert(translateText(ws.reason)) |
||
| const translationKey = this.disconnectWSError | ||
| ? this.disconnectWSError.translationKey | ||
| : event.reason; | ||
| const args = this.disconnectWSError | ||
| ? this.disconnectWSError.args | ||
| : undefined; | ||
|
|
||
| const errorKey = `worker_error.${translationKey}`; | ||
|
|
||
| let alertMsg = `${translateText(connRefusedKey)}: `; | ||
| const translatedError = translateText(errorKey, args); | ||
|
|
||
| if (translatedError === errorKey) { | ||
| // Raw string instead of translation key in disconnectWSError/error.reason, | ||
| // or no user lang nor default English translation found with the key | ||
| // Show the raw string or key as fallback. Eg. "WS_ERR_UNEXPECTED_RSV_1" or "ClientJoinMessageSchema" | ||
| alertMsg += `${translationKey}`; | ||
| } else { | ||
| alertMsg += translatedError; | ||
|
|
||
| // Add tips if turnstile token invalid | ||
| if (translationKey === "turnstile_invalid") { | ||
| alertMsg += `\n${translateText("worker_error.turnstile_fix_tips")}`; | ||
| } | ||
|
|
||
| // Append English translation/key if it's not already there | ||
| // Helps debugging if user shares screenshot | ||
| const englishError = getEnglishText(errorKey, args); | ||
| if (englishError !== errorKey && !alertMsg.includes(englishError)) { | ||
| alertMsg += `\n\n--- English ---\n${getEnglishText(connRefusedKey)}: ${englishError}`; | ||
| } | ||
| } | ||
|
|
||
| // TODO: make this a modal | ||
| alert(`connection refused: ${event.reason}`); | ||
| alert(alertMsg); | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| } else if (event.code !== 1000) { | ||
| console.log(`received error code ${event.code}, reconnecting`); | ||
| this.reconnect(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.