-
Notifications
You must be signed in to change notification settings - Fork 460
feat(kick-system): add translated kick reasons with error codes #1815
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
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 |
---|---|---|
|
@@ -105,15 +105,31 @@ export function joinLobby( | |
).then((r) => r.start()); | ||
} | ||
if (message.type === "error") { | ||
showErrorModal( | ||
message.error, | ||
message.message, | ||
lobbyConfig.gameID, | ||
lobbyConfig.clientID, | ||
true, | ||
false, | ||
"error_modal.connection_error", | ||
); | ||
if (message.kickReason) { | ||
const kickMessage = translateText("error_modal.kicked_message"); | ||
const reasonKey = `error_modal.kicked_reason_${message.kickReason.replace("kick_", "")}`; | ||
const reasonMessage = translateText(reasonKey); | ||
|
||
showErrorModal( | ||
kickMessage, | ||
`${reasonMessage}\nError Code: ${message.kickReason.toUpperCase()}`, | ||
lobbyConfig.gameID, | ||
lobbyConfig.clientID, | ||
true, | ||
false, | ||
"error_modal.connection_error", | ||
); | ||
} else { | ||
showErrorModal( | ||
message.error, | ||
message.message, | ||
lobbyConfig.gameID, | ||
lobbyConfig.clientID, | ||
true, | ||
false, | ||
"error_modal.connection_error", | ||
); | ||
} | ||
} | ||
}; | ||
transport.connect(onconnect, onmessage); | ||
|
@@ -336,15 +352,31 @@ export class ClientGameRunner { | |
); | ||
} | ||
if (message.type === "error") { | ||
showErrorModal( | ||
message.error, | ||
message.message, | ||
this.lobby.gameID, | ||
this.lobby.clientID, | ||
true, | ||
false, | ||
"error_modal.connection_error", | ||
); | ||
if (message.kickReason) { | ||
const kickMessage = translateText("error_modal.kicked_message"); | ||
const reasonKey = `error_modal.kicked_reason_${message.kickReason.replace("kick_", "")}`; | ||
const reasonMessage = translateText(reasonKey); | ||
|
||
showErrorModal( | ||
kickMessage, | ||
`${reasonMessage}\nError Code: ${message.kickReason.toUpperCase()}`, | ||
this.lobby.gameID, | ||
this.lobby.clientID, | ||
true, | ||
false, | ||
"error_modal.connection_error", | ||
); | ||
} else { | ||
Comment on lines
+355
to
+369
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. 🛠️ Refactor suggestion Duplicate logic: apply same heading and fallback in the in-game handler Mirror the fixes from join flow inside the in-game WebSocket handler to keep UX consistent. - if (message.type === "error") {
- if (message.kickReason) {
- const kickMessage = translateText("error_modal.kicked_message");
- const reasonKey = `error_modal.kicked_reason_${message.kickReason.replace("kick_", "")}`;
- const reasonMessage = translateText(reasonKey);
-
- showErrorModal(
- kickMessage,
- `${reasonMessage}\nError Code: ${message.kickReason.toUpperCase()}`,
- this.lobby.gameID,
- this.lobby.clientID,
- true,
- false,
- "error_modal.connection_error",
- );
- } else {
+ if (message.type === "error") {
+ if (message.kickReason) {
+ const reasonKey = `error_modal.kicked_reason_${message.kickReason.replace("kick_", "")}`;
+ let reasonMessage = translateText(reasonKey);
+ if (reasonMessage === reasonKey) {
+ reasonMessage = translateText("error_modal.kicked_reason_multi_tab");
+ }
+ showErrorModal(
+ reasonMessage,
+ `Error Code: ${message.kickReason.toUpperCase()}`,
+ this.lobby.gameID,
+ this.lobby.clientID,
+ true,
+ false,
+ "error_modal.kicked_message",
+ );
+ } else {
showErrorModal(
message.error,
message.message,
this.lobby.gameID,
this.lobby.clientID,
true,
false,
"error_modal.connection_error",
);
}
} Also applies to: 370-379 🤖 Prompt for AI Agents
|
||
showErrorModal( | ||
message.error, | ||
message.message, | ||
this.lobby.gameID, | ||
this.lobby.clientID, | ||
true, | ||
false, | ||
"error_modal.connection_error", | ||
); | ||
} | ||
} | ||
if (message.type === "turn") { | ||
if (!this.hasJoined) { | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -465,6 +465,7 @@ export const ServerDesyncSchema = z.object({ | |||||
|
||||||
export const ServerErrorSchema = z.object({ | ||||||
error: z.string(), | ||||||
kickReason: z.enum(["kick_admin", "kick_multi_tab", "kick_lobby_creator"]).optional(), | ||||||
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. 🛠️ Refactor suggestion Extract KickReason into a shared schema and type for reuse Inline string unions tend to get duplicated across server and client. Centralizing the values improves consistency and reduces drift.
export const KickReasonValues = ["kick_admin", "kick_multi_tab", "kick_lobby_creator"] as const;
export type KickReason = typeof KickReasonValues[number];
export const KickReasonSchema = z.enum(KickReasonValues);
- kickReason: z.enum(["kick_admin", "kick_multi_tab", "kick_lobby_creator"] as const).optional(),
+ kickReason: KickReasonSchema.optional(), This also lets you import 🤖 Prompt for AI Agents
Add Without Apply this minimal fix: - kickReason: z.enum(["kick_admin", "kick_multi_tab", "kick_lobby_creator"]).optional(),
+ kickReason: z.enum(["kick_admin", "kick_multi_tab", "kick_lobby_creator"] as const).optional(), 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||
message: z.string().optional(), | ||||||
type: z.literal("error"), | ||||||
}); | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -175,7 +175,7 @@ export class GameServer { | |
}); | ||
// Kick the existing client instead of the new one, because this was causing issues when | ||
// a client wanted to replay the game afterwards. | ||
this.kickClient(conflicting.clientID); | ||
this.kickClient(conflicting.clientID, "kick_multi_tab"); | ||
} | ||
} | ||
|
||
|
@@ -502,7 +502,8 @@ export class GameServer { | |
return this.gameConfig.gameType === GameType.Public; | ||
} | ||
|
||
public kickClient(clientID: ClientID): void { | ||
|
||
public kickClient(clientID: ClientID, kickReason?: "kick_admin" | "kick_multi_tab" | "kick_lobby_creator"): void { | ||
if (this.kickedClients.has(clientID)) { | ||
Comment on lines
+505
to
507
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. 🛠️ Refactor suggestion Avoid duplicating the KickReason union; import a shared type The inline union risks drift if a new reason is added elsewhere. Prefer a single source of truth (Schemas). Option A (preferred, if exported): - public kickClient(clientID: ClientID, kickReason?: "kick_admin" | "kick_multi_tab" | "kick_lobby_creator"): void {
+ public kickClient(clientID: ClientID, kickReason?: KickReason): void { And add at top: import type { KickReason } from "../core/Schemas"; Option B (if KickReason type isn’t exported yet): export it from Schemas and use it here. I can open a follow-up PR to centralize this type. 🤖 Prompt for AI Agents
|
||
this.log.warn(`cannot kick client, already kicked`, { | ||
clientID, | ||
|
@@ -513,11 +514,13 @@ export class GameServer { | |
if (client) { | ||
this.log.info("Kicking client from game", { | ||
clientID: client.clientID, | ||
kickReason, | ||
persistentID: client.persistentID, | ||
}); | ||
client.ws.send( | ||
JSON.stringify({ | ||
error: "Kicked from game (you may have been playing on another tab)", | ||
error: "Kicked from game", | ||
kickReason, | ||
type: "error", | ||
} satisfies ServerErrorMessage), | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix import path to avoid duplicate module instances of Utils
The file imports Utils twice with two different relative paths (“./Utils” and “../client/Utils”). This can cause the bundler/loader to create separate module instances, breaking shared caches in translateText and adding bytes.
Update the import at the top of this file:
🤖 Prompt for AI Agents
🛠️ Refactor suggestion
Use the “kicked” heading and add a robust translation fallback
Set the modal heading to the translated “kicked” title, and fall back to a safe reason message if the specific translation key is missing (e.g., mixed versions). Also remove the now-unused
kickMessage
variable.Apply this diff:
Also applies to: 123-132