mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:20:47 +00:00
Refactor: use promises instead of callbacks for joining a game (#3452)
## Description: Simplifies the interface a bit. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan
This commit is contained in:
@@ -64,15 +64,24 @@ export interface LobbyConfig {
|
||||
gameRecord?: GameRecord;
|
||||
}
|
||||
|
||||
export interface JoinLobbyResult {
|
||||
stop: (force?: boolean) => boolean;
|
||||
prestart: Promise<void>;
|
||||
join: Promise<void>;
|
||||
}
|
||||
|
||||
export function joinLobby(
|
||||
eventBus: EventBus,
|
||||
lobbyConfig: LobbyConfig,
|
||||
onPrestart: () => void,
|
||||
onJoin: () => void,
|
||||
): (force?: boolean) => boolean {
|
||||
): JoinLobbyResult {
|
||||
// Mutable clientID state — assigned by server (multiplayer) or derived from gameStartInfo (singleplayer)
|
||||
let clientID: ClientID | undefined;
|
||||
|
||||
let resolvePrestart: () => void;
|
||||
let resolveJoin: () => void;
|
||||
const prestartPromise = new Promise<void>((r) => (resolvePrestart = r));
|
||||
const joinPromise = new Promise<void>((r) => (resolveJoin = r));
|
||||
|
||||
console.log(`joining lobby: gameID: ${lobbyConfig.gameID}`);
|
||||
|
||||
const userSettings: UserSettings = new UserSettings();
|
||||
@@ -105,17 +114,17 @@ export function joinLobby(
|
||||
message.gameMapSize,
|
||||
terrainMapFileLoader,
|
||||
);
|
||||
onPrestart();
|
||||
resolvePrestart();
|
||||
}
|
||||
if (message.type === "start") {
|
||||
// Trigger prestart for singleplayer games
|
||||
onPrestart();
|
||||
resolvePrestart();
|
||||
console.log(
|
||||
`lobby: game started: ${JSON.stringify(message, replacer, 2)}`,
|
||||
);
|
||||
// Server tells us our assigned clientID (also sent on start for late joins)
|
||||
clientID = message.myClientID;
|
||||
onJoin();
|
||||
resolveJoin();
|
||||
// For multiplayer games, GameStartInfo is not known until game starts.
|
||||
lobbyConfig.gameStartInfo = message.gameStartInfo;
|
||||
createClientGame(
|
||||
@@ -176,19 +185,19 @@ export function joinLobby(
|
||||
}
|
||||
};
|
||||
transport.connect(onconnect, onmessage);
|
||||
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;
|
||||
return {
|
||||
stop: (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;
|
||||
},
|
||||
prestart: prestartPromise,
|
||||
join: joinPromise,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+103
-105
@@ -15,7 +15,7 @@ import { UserSettings } from "../core/game/UserSettings";
|
||||
import "./AccountModal";
|
||||
import { getUserMe } from "./Api";
|
||||
import { userAuth } from "./Auth";
|
||||
import { joinLobby } from "./ClientGameRunner";
|
||||
import { joinLobby, type JoinLobbyResult } from "./ClientGameRunner";
|
||||
import { getPlayerCosmeticsRefs } from "./Cosmetics";
|
||||
import { crazyGamesSDK } from "./CrazyGamesSDK";
|
||||
import "./FlagInput";
|
||||
@@ -230,7 +230,7 @@ export interface JoinLobbyEvent {
|
||||
}
|
||||
|
||||
class Client {
|
||||
private gameStop: ((force?: boolean) => boolean) | null = null;
|
||||
private lobbyHandle: JoinLobbyResult | null = null;
|
||||
private eventBus: EventBus = new EventBus();
|
||||
|
||||
private currentUrl: string | null = null;
|
||||
@@ -300,8 +300,8 @@ class Client {
|
||||
|
||||
window.addEventListener("beforeunload", async () => {
|
||||
console.log("Browser is closing");
|
||||
if (this.gameStop !== null) {
|
||||
this.gameStop(true);
|
||||
if (this.lobbyHandle !== null) {
|
||||
this.lobbyHandle.stop(true);
|
||||
await crazyGamesSDK.gameplayStop();
|
||||
}
|
||||
});
|
||||
@@ -521,10 +521,10 @@ class Client {
|
||||
};
|
||||
|
||||
const onPopState = () => {
|
||||
if (this.currentUrl !== null && this.gameStop !== null) {
|
||||
if (this.currentUrl !== null && this.lobbyHandle !== null) {
|
||||
console.info("Game is active");
|
||||
|
||||
if (!this.gameStop()) {
|
||||
if (!this.lobbyHandle.stop()) {
|
||||
console.info("Player is active, ask before leaving game");
|
||||
|
||||
const isConfirmed = confirm(
|
||||
@@ -552,7 +552,7 @@ class Client {
|
||||
};
|
||||
|
||||
const onJoinChanged = () => {
|
||||
if (this.gameStop !== null) {
|
||||
if (this.lobbyHandle !== null) {
|
||||
this.handleLeaveLobby();
|
||||
}
|
||||
|
||||
@@ -733,9 +733,9 @@ class Client {
|
||||
private async handleJoinLobby(event: CustomEvent<JoinLobbyEvent>) {
|
||||
const lobby = event.detail;
|
||||
console.log(`joining lobby ${lobby.gameID}`);
|
||||
if (this.gameStop !== null) {
|
||||
if (this.lobbyHandle !== null) {
|
||||
console.log("joining lobby, stopping existing game");
|
||||
this.gameStop(true);
|
||||
this.lobbyHandle.stop(true);
|
||||
document.body.classList.remove("in-game");
|
||||
}
|
||||
if (lobby.source === "public") {
|
||||
@@ -746,106 +746,104 @@ class Client {
|
||||
if (lobby.source !== "public") {
|
||||
this.updateJoinUrlForShare(lobby.gameID, config);
|
||||
}
|
||||
this.gameStop = joinLobby(
|
||||
this.eventBus,
|
||||
{
|
||||
gameID: lobby.gameID,
|
||||
serverConfig: config,
|
||||
cosmetics: await getPlayerCosmeticsRefs(),
|
||||
turnstileToken: await this.getTurnstileToken(lobby),
|
||||
playerName:
|
||||
this.usernameInput?.getCurrentUsername() ?? genAnonUsername(),
|
||||
gameStartInfo: lobby.gameStartInfo ?? lobby.gameRecord?.info,
|
||||
gameRecord: lobby.gameRecord,
|
||||
},
|
||||
() => {
|
||||
console.log("Closing modals");
|
||||
document.getElementById("settings-button")?.classList.add("hidden");
|
||||
if (this.usernameInput) {
|
||||
// fix edge case where username-validation-error is re-rendered and hidden tag removed
|
||||
this.usernameInput.validationError = "";
|
||||
this.lobbyHandle = joinLobby(this.eventBus, {
|
||||
gameID: lobby.gameID,
|
||||
serverConfig: config,
|
||||
cosmetics: await getPlayerCosmeticsRefs(),
|
||||
turnstileToken: await this.getTurnstileToken(lobby),
|
||||
playerName: this.usernameInput?.getCurrentUsername() ?? genAnonUsername(),
|
||||
gameStartInfo: lobby.gameStartInfo ?? lobby.gameRecord?.info,
|
||||
gameRecord: lobby.gameRecord,
|
||||
});
|
||||
|
||||
this.lobbyHandle.prestart.then(() => {
|
||||
console.log("Closing modals");
|
||||
document.getElementById("settings-button")?.classList.add("hidden");
|
||||
if (this.usernameInput) {
|
||||
// fix edge case where username-validation-error is re-rendered and hidden tag removed
|
||||
this.usernameInput.validationError = "";
|
||||
}
|
||||
document
|
||||
.getElementById("username-validation-error")
|
||||
?.classList.add("hidden");
|
||||
this.joinModal?.closeWithoutLeaving();
|
||||
[
|
||||
"single-player-modal",
|
||||
"host-lobby-modal",
|
||||
"game-starting-modal",
|
||||
"game-top-bar",
|
||||
"help-modal",
|
||||
"user-setting",
|
||||
"troubleshooting-modal",
|
||||
"territory-patterns-modal",
|
||||
"language-modal",
|
||||
"news-modal",
|
||||
"flag-input-modal",
|
||||
"account-button",
|
||||
"leaderboard-button",
|
||||
"token-login",
|
||||
"matchmaking-modal",
|
||||
"lang-selector",
|
||||
"gutter-ads",
|
||||
].forEach((tag) => {
|
||||
const modal = document.querySelector(tag) as HTMLElement & {
|
||||
close?: () => void;
|
||||
isModalOpen?: boolean;
|
||||
};
|
||||
if (modal?.close) {
|
||||
modal.close();
|
||||
} else if (modal && "isModalOpen" in modal) {
|
||||
modal.isModalOpen = false;
|
||||
}
|
||||
document
|
||||
.getElementById("username-validation-error")
|
||||
?.classList.add("hidden");
|
||||
this.joinModal?.closeWithoutLeaving();
|
||||
[
|
||||
"single-player-modal",
|
||||
"host-lobby-modal",
|
||||
"game-starting-modal",
|
||||
"game-top-bar",
|
||||
"help-modal",
|
||||
"user-setting",
|
||||
"troubleshooting-modal",
|
||||
"territory-patterns-modal",
|
||||
"language-modal",
|
||||
"news-modal",
|
||||
"flag-input-modal",
|
||||
"account-button",
|
||||
"leaderboard-button",
|
||||
"token-login",
|
||||
"matchmaking-modal",
|
||||
"lang-selector",
|
||||
"gutter-ads",
|
||||
].forEach((tag) => {
|
||||
const modal = document.querySelector(tag) as HTMLElement & {
|
||||
close?: () => void;
|
||||
isModalOpen?: boolean;
|
||||
};
|
||||
if (modal?.close) {
|
||||
modal.close();
|
||||
} else if (modal && "isModalOpen" in modal) {
|
||||
modal.isModalOpen = false;
|
||||
}
|
||||
});
|
||||
this.gameModeSelector.stop();
|
||||
document.querySelectorAll(".ad").forEach((ad) => {
|
||||
(ad as HTMLElement).style.display = "none";
|
||||
});
|
||||
});
|
||||
this.gameModeSelector.stop();
|
||||
document.querySelectorAll(".ad").forEach((ad) => {
|
||||
(ad as HTMLElement).style.display = "none";
|
||||
});
|
||||
|
||||
crazyGamesSDK.loadingStart();
|
||||
crazyGamesSDK.loadingStart();
|
||||
|
||||
// show when the game loads
|
||||
const startingModal = document.querySelector(
|
||||
"game-starting-modal",
|
||||
) as GameStartingModal;
|
||||
if (startingModal && startingModal instanceof GameStartingModal) {
|
||||
startingModal.show();
|
||||
}
|
||||
},
|
||||
() => {
|
||||
this.joinModal?.closeWithoutLeaving();
|
||||
this.gameModeSelector.stop();
|
||||
incrementGamesPlayed();
|
||||
// show when the game loads
|
||||
const startingModal = document.querySelector(
|
||||
"game-starting-modal",
|
||||
) as GameStartingModal;
|
||||
if (startingModal && startingModal instanceof GameStartingModal) {
|
||||
startingModal.show();
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll(".ad").forEach((ad) => {
|
||||
(ad as HTMLElement).style.display = "none";
|
||||
});
|
||||
this.lobbyHandle.join.then(() => {
|
||||
this.joinModal?.closeWithoutLeaving();
|
||||
this.gameModeSelector.stop();
|
||||
incrementGamesPlayed();
|
||||
|
||||
if (window.PageOS?.session?.newPageView) {
|
||||
window.PageOS.session.newPageView();
|
||||
}
|
||||
crazyGamesSDK.loadingStop();
|
||||
crazyGamesSDK.gameplayStart();
|
||||
document.body.classList.add("in-game");
|
||||
document.querySelectorAll(".ad").forEach((ad) => {
|
||||
(ad as HTMLElement).style.display = "none";
|
||||
});
|
||||
|
||||
// Ensure there's a homepage entry in history before adding the lobby entry
|
||||
if (window.location.hash === "" || window.location.hash === "#") {
|
||||
history.replaceState(null, "", window.location.origin + "#refresh");
|
||||
}
|
||||
const lobbyIdHidden = !this.userSettings.lobbyIdVisibility();
|
||||
history.pushState(
|
||||
null,
|
||||
"",
|
||||
lobbyIdHidden
|
||||
? "/streamer-mode"
|
||||
: `/${config.workerPath(lobby.gameID)}/game/${lobby.gameID}?live`,
|
||||
);
|
||||
if (window.PageOS?.session?.newPageView) {
|
||||
window.PageOS.session.newPageView();
|
||||
}
|
||||
crazyGamesSDK.loadingStop();
|
||||
crazyGamesSDK.gameplayStart();
|
||||
document.body.classList.add("in-game");
|
||||
|
||||
// Store current URL for popstate confirmation
|
||||
this.currentUrl = window.location.href;
|
||||
},
|
||||
);
|
||||
// Ensure there's a homepage entry in history before adding the lobby entry
|
||||
if (window.location.hash === "" || window.location.hash === "#") {
|
||||
history.replaceState(null, "", window.location.origin + "#refresh");
|
||||
}
|
||||
const lobbyIdHidden = !this.userSettings.lobbyIdVisibility();
|
||||
history.pushState(
|
||||
null,
|
||||
"",
|
||||
lobbyIdHidden
|
||||
? "/streamer-mode"
|
||||
: `/${config.workerPath(lobby.gameID)}/game/${lobby.gameID}?live`,
|
||||
);
|
||||
|
||||
// Store current URL for popstate confirmation
|
||||
this.currentUrl = window.location.href;
|
||||
});
|
||||
}
|
||||
|
||||
private updateJoinUrlForShare(
|
||||
@@ -864,12 +862,12 @@ class Client {
|
||||
}
|
||||
|
||||
private async handleLeaveLobby(/* event: CustomEvent */) {
|
||||
if (this.gameStop === null) {
|
||||
if (this.lobbyHandle === null) {
|
||||
return;
|
||||
}
|
||||
console.log("leaving lobby, cancelling game");
|
||||
this.gameStop(true);
|
||||
this.gameStop = null;
|
||||
this.lobbyHandle.stop(true);
|
||||
this.lobbyHandle = null;
|
||||
this.currentUrl = null;
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user