From f9ce40811ac1692f0c37559d0b71541da95a5b20 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Mon, 5 Jan 2026 15:34:33 -0800 Subject: [PATCH] crazygames --- src/client/CrazyGamesSDK.ts | 79 +++++++++++++++++++ src/client/Main.ts | 15 +++- src/client/SinglePlayerModal.ts | 3 + src/client/UsernameInput.ts | 12 ++- .../graphics/layers/GameRightSidebar.ts | 15 ++-- src/client/graphics/layers/SettingsModal.ts | 9 ++- src/client/graphics/layers/UnitDisplay.ts | 2 +- src/client/graphics/layers/WinModal.ts | 3 + startup.sh | 2 +- 9 files changed, 127 insertions(+), 13 deletions(-) diff --git a/src/client/CrazyGamesSDK.ts b/src/client/CrazyGamesSDK.ts index b00cfe7de..7caf54de8 100644 --- a/src/client/CrazyGamesSDK.ts +++ b/src/client/CrazyGamesSDK.ts @@ -3,6 +3,22 @@ declare global { CrazyGames?: { SDK: { init: () => Promise; + user: { + getUser(): Promise<{ + username: string; + profilePictureUrl: string; + } | null>; + }; + ad: { + requestAd: ( + adType: string, + callbacks: { + adStarted: () => void; + adFinished: () => void; + adError: (error: any) => void; + }, + ) => void; + }; game: { gameplayStart: () => Promise; gameplayStop: () => Promise; @@ -15,6 +31,7 @@ declare global { }) => string; hideInviteButton: () => void; getInviteParam: (paramName: string) => string | null; + isInstantMultiplayer?: boolean; }; }; }; @@ -24,6 +41,24 @@ declare global { export class CrazyGamesSDK { private initialized = false; private isGameplayActive = false; + private readyPromise: Promise; + private resolveReady!: () => void; + + constructor() { + this.readyPromise = new Promise((resolve) => { + this.resolveReady = resolve; + }); + } + + async ready(): Promise { + const timeout = new Promise((resolve) => { + setTimeout(() => resolve(false), 3000); + }); + + const ready = this.readyPromise.then(() => true); + + return Promise.race([ready, timeout]); + } isOnCrazyGames(): boolean { try { @@ -70,12 +105,29 @@ export class CrazyGamesSDK { try { await window.CrazyGames.SDK.init(); this.initialized = true; + this.resolveReady(); console.log("CrazyGames SDK initialized"); } catch (error) { console.error("Failed to initialize CrazyGames SDK:", error); } } + async getUsername(): Promise { + const isReady = await this.ready(); + if (!isReady) { + return null; + } + return (await window.CrazyGames!.SDK.user.getUser())?.username ?? null; + } + + async isInstantMultiplayer(): Promise { + const isReady = await this.ready(); + if (!isReady) { + return false; + } + return window.CrazyGames!.SDK.game.isInstantMultiplayer ?? false; + } + async gameplayStart(): Promise { if (!this.isReady()) { return; @@ -200,6 +252,33 @@ export class CrazyGamesSDK { return null; } } + + requestMidgameAd(): Promise { + return new Promise((resolve) => { + if (!this.isReady()) { + resolve(); + return; + } + + try { + const callbacks = { + adFinished: () => { + console.log("End midgame ad"); + resolve(); + }, + adError: (error: any) => { + console.log("Error midgame ad", error); + resolve(); + }, + adStarted: () => console.log("Start midgame ad"), + }; + window.CrazyGames!.SDK.ad.requestAd("midgame", callbacks); + } catch (error) { + console.error("Failed to request midgame ad:", error); + resolve(); + } + }); + } } export const crazyGamesSDK = new CrazyGamesSDK(); diff --git a/src/client/Main.ts b/src/client/Main.ts index 978d35691..569fd6e3c 100644 --- a/src/client/Main.ts +++ b/src/client/Main.ts @@ -103,6 +103,7 @@ class Client { private patternsModal: TerritoryPatternsModal; private tokenLoginModal: TokenLoginModal; private matchmakingModal: MatchmakingModal; + private hostModal: HostPrivateLobbyModal; private gutterAds: GutterAds; @@ -305,17 +306,17 @@ class Client { settingsModal.open(); }); - const hostModal = document.querySelector( + this.hostModal = document.querySelector( "host-lobby-modal", ) as HostPrivateLobbyModal; - if (!hostModal || !(hostModal instanceof HostPrivateLobbyModal)) { + if (!this.hostModal || !(this.hostModal instanceof HostPrivateLobbyModal)) { console.warn("Host private lobby modal element not found"); } const hostLobbyButton = document.getElementById("host-lobby-button"); if (hostLobbyButton === null) throw new Error("Missing host-lobby-button"); hostLobbyButton.addEventListener("click", () => { if (this.usernameInput?.isValid()) { - hostModal.open(); + this.hostModal.open(); this.publicLobby.leaveLobby(); } }); @@ -390,6 +391,14 @@ class Client { console.log(`CrazyGames: joining lobby ${lobbyId} from invite param`); return; } + crazyGamesSDK.isInstantMultiplayer().then((isInstant) => { + if (isInstant) { + console.log( + `CrazyGames: joining instant multiplayer lobby from CrazyGames`, + ); + this.hostModal.open(); + } + }); } const strip = () => diff --git a/src/client/SinglePlayerModal.ts b/src/client/SinglePlayerModal.ts index 6e8e04096..9b7b62c57 100644 --- a/src/client/SinglePlayerModal.ts +++ b/src/client/SinglePlayerModal.ts @@ -23,6 +23,7 @@ import "./components/baseComponents/Modal"; import "./components/Difficulties"; import "./components/Maps"; import { fetchCosmetics } from "./Cosmetics"; +import { crazyGamesSDK } from "./CrazyGamesSDK"; import { FlagInput } from "./FlagInput"; import { JoinLobbyEvent } from "./Main"; import { UsernameInput } from "./UsernameInput"; @@ -547,6 +548,8 @@ export class SinglePlayerModal extends LitElement { const selectedColor = this.userSettings.getSelectedColor(); + await crazyGamesSDK.requestMidgameAd(); + this.dispatchEvent( new CustomEvent("join-lobby", { detail: { diff --git a/src/client/UsernameInput.ts b/src/client/UsernameInput.ts index 37e26dc00..ec6a067c4 100644 --- a/src/client/UsernameInput.ts +++ b/src/client/UsernameInput.ts @@ -6,6 +6,7 @@ import { MAX_USERNAME_LENGTH, validateUsername, } from "../core/validations/username"; +import { crazyGamesSDK } from "./CrazyGamesSDK"; const usernameKey: string = "username"; @@ -28,7 +29,7 @@ export class UsernameInput extends LitElement { connectedCallback() { super.connectedCallback(); - this.username = this.getStoredUsername(); + this.username = this.getUsername(); } render() { @@ -66,7 +67,14 @@ export class UsernameInput extends LitElement { } } - private getStoredUsername(): string { + private getUsername(): string { + crazyGamesSDK.getUsername().then((username) => { + if (username) { + this.username = username; + this.requestUpdate(); + } + return null; + }); const storedUsername = localStorage.getItem(usernameKey); if (storedUsername) { return storedUsername; diff --git a/src/client/graphics/layers/GameRightSidebar.ts b/src/client/graphics/layers/GameRightSidebar.ts index d73c47360..d79766ff0 100644 --- a/src/client/graphics/layers/GameRightSidebar.ts +++ b/src/client/graphics/layers/GameRightSidebar.ts @@ -96,10 +96,15 @@ export class GameRightSidebar extends LitElement implements Layer { private onPauseButtonClick() { this.isPaused = !this.isPaused; + if (this.isPaused) { + crazyGamesSDK.gameplayStop(); + } else { + crazyGamesSDK.gameplayStart(); + } this.eventBus.emit(new PauseGameEvent(this.isPaused)); } - private onExitButtonClick() { + private async onExitButtonClick() { const isAlive = this.game.myPlayer()?.isAlive(); if (isAlive) { const isConfirmed = confirm( @@ -107,10 +112,10 @@ export class GameRightSidebar extends LitElement implements Layer { ); if (!isConfirmed) return; } - crazyGamesSDK.gameplayStop().then(() => { - // redirect to the home page - window.location.href = "/"; - }); + await crazyGamesSDK.requestMidgameAd(); + await crazyGamesSDK.gameplayStop(); + // redirect to the home page + window.location.href = "/"; } private onSettingsButtonClick() { diff --git a/src/client/graphics/layers/SettingsModal.ts b/src/client/graphics/layers/SettingsModal.ts index 23ee9f5e7..148cb38bb 100644 --- a/src/client/graphics/layers/SettingsModal.ts +++ b/src/client/graphics/layers/SettingsModal.ts @@ -13,6 +13,7 @@ import treeIcon from "../../../../resources/images/TreeIconWhite.svg"; import musicIcon from "../../../../resources/images/music.svg"; import { EventBus } from "../../../core/EventBus"; import { UserSettings } from "../../../core/game/UserSettings"; +import { crazyGamesSDK } from "../../CrazyGamesSDK"; import { AlternateViewEvent, RefreshGraphicsEvent } from "../../InputHandler"; import { PauseGameEvent } from "../../Transport"; import { translateText } from "../../Utils"; @@ -106,8 +107,14 @@ export class SettingsModal extends LitElement implements Layer { } private pauseGame(pause: boolean) { - if (this.shouldPause && !this.wasPausedWhenOpened) + if (this.shouldPause && !this.wasPausedWhenOpened) { + if (pause) { + crazyGamesSDK.gameplayStop(); + } else { + crazyGamesSDK.gameplayStart(); + } this.eventBus.emit(new PauseGameEvent(pause)); + } } private onTerrainButtonClick() { diff --git a/src/client/graphics/layers/UnitDisplay.ts b/src/client/graphics/layers/UnitDisplay.ts index 55b7d7ccb..72eb9e37c 100644 --- a/src/client/graphics/layers/UnitDisplay.ts +++ b/src/client/graphics/layers/UnitDisplay.ts @@ -130,7 +130,7 @@ export class UnitDisplay extends LitElement implements Layer { return html`