Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions src/client/ClientGameRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function joinLobby(
lobbyConfig: LobbyConfig,
onPrestart: () => void,
onJoin: () => void,
): () => void {
): (force?: boolean) => boolean {
console.log(
`joining lobby: gameID: ${lobbyConfig.gameID}, clientID: ${lobbyConfig.clientID}`,
);
Expand All @@ -79,6 +79,8 @@ export function joinLobby(

const transport = new Transport(lobbyConfig, eventBus);

let currentGameRunner: ClientGameRunner | null = null;

let hasJoined = false;

const onconnect = () => {
Expand Down Expand Up @@ -122,9 +124,15 @@ export function joinLobby(
terrainLoad,
terrainMapFileLoader,
)
.then((r) => r.start())
.then((r) => {
currentGameRunner = r;
r.start();
})
.catch((e) => {
console.error("error creating client game", e);

currentGameRunner = null;

const startingModal = document.querySelector(
"game-starting-modal",
) as HTMLElement;
Expand Down Expand Up @@ -165,9 +173,19 @@ export function joinLobby(
}
};
transport.connect(onconnect, onmessage);
return () => {
return (force: boolean = false) => {
if (!force && currentGameRunner?.shouldPreventWindowClose()) {
console.log("Player is active, prevent leaving game");

return false;
}

console.log("leaving game");

currentGameRunner = null;
transport.leaveGame();

return true;
};
}

Expand Down Expand Up @@ -256,6 +274,21 @@ export class ClientGameRunner {
this.lastMessageTime = Date.now();
}

/**
* Determines whether window closing should be prevented.
*
* Used to show a confirmation dialog when the user attempts to close
* the window or navigate away during an active game session.
*
* @returns {boolean} `true` if the window close should be prevented
* (when the player is alive in the game), `false` otherwise
* (when the player is not alive or doesn't exist)
*/
public shouldPreventWindowClose(): boolean {
// Show confirmation dialog if player is alive in the game
return !!this.myPlayer?.isAlive();
}

private async saveGame(update: WinUpdate) {
if (this.myPlayer === null) {
return;
Expand Down
55 changes: 42 additions & 13 deletions src/client/Main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {
import { UserSettingModal } from "./UserSettingModal";
import "./UsernameInput";
import { UsernameInput } from "./UsernameInput";
import { incrementGamesPlayed, isInIframe } from "./Utils";
import { incrementGamesPlayed, isInIframe, translateText } from "./Utils";
import "./components/baseComponents/Button";
import "./components/baseComponents/Modal";
import "./styles.css";
Expand Down Expand Up @@ -101,9 +101,12 @@ export interface JoinLobbyEvent {
}

class Client {
private gameStop: (() => void) | null = null;
private gameStop: ((force?: boolean) => boolean) | null = null;
private eventBus: EventBus = new EventBus();

private currentUrl: string | null = null;
private preventHashUpdate: boolean = false;

private usernameInput: UsernameInput | null = null;
private flagInput: FlagInput | null = null;

Expand Down Expand Up @@ -166,7 +169,7 @@ class Client {
window.addEventListener("beforeunload", async () => {
console.log("Browser is closing");
if (this.gameStop !== null) {
this.gameStop();
this.gameStop(true);
await crazyGamesSDK.gameplayStop();
}
});
Expand Down Expand Up @@ -508,12 +511,11 @@ class Client {
// Attempt to join lobby
this.handleUrl();

let preventHashUpdate = false;

const onHashUpdate = () => {
// Prevent double-handling when both popstate and hashchange fire
if (preventHashUpdate) {
preventHashUpdate = false;
if (this.preventHashUpdate) {
this.preventHashUpdate = false;

return;
}

Expand All @@ -527,11 +529,34 @@ class Client {
this.handleUrl();
};

// Handle browser navigation & manual hash edits
window.addEventListener("popstate", () => {
preventHashUpdate = true;
const onPopState = () => {
this.preventHashUpdate = true;

if (this.currentUrl !== null && this.gameStop && !this.gameStop()) {
console.info("Player is active, ask before leaving game");

const isConfirmed = confirm(
translateText("help_modal.exit_confirmation"),
);

if (!isConfirmed) {
console.debug(
"Player denied leaving game, restore navigator history",
);

// Rollback navigator history
history.pushState(null, "", this.currentUrl);
return;
}
}

console.info("Player not active, handle hash update");

onHashUpdate();
});
};

// Handle browser navigation & manual hash edits
window.addEventListener("popstate", onPopState);
window.addEventListener("hashchange", onHashUpdate);

function updateSliderProgress(slider: HTMLInputElement) {
Expand Down Expand Up @@ -660,7 +685,7 @@ class Client {
console.log(`joining lobby ${lobby.gameID}`);
if (this.gameStop !== null) {
console.log("joining lobby, stopping existing game");
this.gameStop();
this.gameStop(true);
document.body.classList.remove("in-game");
}
const config = await getServerConfigFromClient();
Expand Down Expand Up @@ -760,6 +785,9 @@ class Client {
history.replaceState(null, "", window.location.origin + "#refresh");
}
history.pushState(null, "", `#join=${lobby.gameID}`);

// Store current URL for popstate confirmation
this.currentUrl = window.location.href;
},
);
}
Expand All @@ -769,8 +797,9 @@ class Client {
return;
}
console.log("leaving lobby, cancelling game");
this.gameStop();
this.gameStop(true);
this.gameStop = null;
this.currentUrl = null;

document.body.classList.remove("in-game");

Expand Down
Loading