Skip to content

Commit 01b65e2

Browse files
committed
crazygames
1 parent e1d31ef commit 01b65e2

16 files changed

+293
-101
lines changed

src/client/CrazyGamesSDK.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,28 @@ declare global {
33
CrazyGames?: {
44
SDK: {
55
init: () => Promise<void>;
6+
user: {
7+
getUser(): Promise<{
8+
username: string;
9+
} | null>;
10+
addAuthListener: (
11+
listener: (
12+
user: {
13+
username: string;
14+
} | null,
15+
) => void,
16+
) => void;
17+
};
18+
ad: {
19+
requestAd: (
20+
adType: string,
21+
callbacks: {
22+
adStarted: () => void;
23+
adFinished: () => void;
24+
adError: (error: any) => void;
25+
},
26+
) => void;
27+
};
628
game: {
729
gameplayStart: () => Promise<void>;
830
gameplayStop: () => Promise<void>;
@@ -14,7 +36,9 @@ declare global {
1436
[key: string]: string | number;
1537
}) => string;
1638
hideInviteButton: () => void;
39+
inviteLink: (params: { [key: string]: string | number }) => string;
1740
getInviteParam: (paramName: string) => string | null;
41+
isInstantMultiplayer?: boolean;
1842
};
1943
};
2044
};
@@ -24,6 +48,24 @@ declare global {
2448
export class CrazyGamesSDK {
2549
private initialized = false;
2650
private isGameplayActive = false;
51+
private readyPromise: Promise<void>;
52+
private resolveReady!: () => void;
53+
54+
constructor() {
55+
this.readyPromise = new Promise((resolve) => {
56+
this.resolveReady = resolve;
57+
});
58+
}
59+
60+
async ready(): Promise<boolean> {
61+
const timeout = new Promise<boolean>((resolve) => {
62+
setTimeout(() => resolve(false), 3000);
63+
});
64+
65+
const ready = this.readyPromise.then(() => true);
66+
67+
return Promise.race([ready, timeout]);
68+
}
2769

2870
isOnCrazyGames(): boolean {
2971
try {
@@ -70,12 +112,58 @@ export class CrazyGamesSDK {
70112
try {
71113
await window.CrazyGames.SDK.init();
72114
this.initialized = true;
115+
this.resolveReady();
73116
console.log("CrazyGames SDK initialized");
74117
} catch (error) {
75118
console.error("Failed to initialize CrazyGames SDK:", error);
76119
}
77120
}
78121

122+
async getUsername(): Promise<string | null> {
123+
const isReady = await this.ready();
124+
if (!isReady) {
125+
return null;
126+
}
127+
try {
128+
return (await window.CrazyGames!.SDK.user.getUser())?.username ?? null;
129+
} catch (e) {
130+
console.log("error getting CrazyGames username: ", e);
131+
return null;
132+
}
133+
}
134+
135+
async addAuthListener(
136+
listener: (
137+
user: {
138+
username: string;
139+
} | null,
140+
) => void,
141+
): Promise<void> {
142+
if (!(await this.ready())) {
143+
return;
144+
}
145+
146+
try {
147+
console.log("registering CrazyGames auth listener");
148+
window.CrazyGames!.SDK.user.addAuthListener(listener);
149+
} catch (error) {
150+
console.error("Failed to add auth listener:", error);
151+
}
152+
}
153+
154+
async isInstantMultiplayer(): Promise<boolean> {
155+
const isReady = await this.ready();
156+
if (!isReady) {
157+
return false;
158+
}
159+
try {
160+
return window.CrazyGames!.SDK.game.isInstantMultiplayer ?? false;
161+
} catch (e) {
162+
console.log("Error getting instant multiplayer: ", e);
163+
return false;
164+
}
165+
}
166+
79167
async gameplayStart(): Promise<void> {
80168
if (!this.isReady()) {
81169
return;
@@ -186,6 +274,22 @@ export class CrazyGamesSDK {
186274
}
187275
}
188276

277+
createInviteLink(gameId: string): string | null {
278+
if (!this.isReady()) {
279+
console.warn("CrazyGames SDK not ready, cannot create invite link");
280+
return null;
281+
}
282+
283+
try {
284+
const link = window.CrazyGames!.SDK.game.inviteLink({ gameId });
285+
console.log("CrazyGames: created invite link:", link);
286+
return link;
287+
} catch (error) {
288+
console.error("Failed to create invite link:", error);
289+
return null;
290+
}
291+
}
292+
189293
getInviteGameId(): string | null {
190294
if (!this.isReady()) {
191295
return null;
@@ -200,6 +304,33 @@ export class CrazyGamesSDK {
200304
return null;
201305
}
202306
}
307+
308+
requestMidgameAd(): Promise<void> {
309+
return new Promise((resolve) => {
310+
if (!this.isReady()) {
311+
resolve();
312+
return;
313+
}
314+
315+
try {
316+
const callbacks = {
317+
adFinished: () => {
318+
console.log("End midgame ad");
319+
resolve();
320+
},
321+
adError: (error: any) => {
322+
console.log("Error midgame ad", error);
323+
resolve();
324+
},
325+
adStarted: () => console.log("Start midgame ad"),
326+
};
327+
window.CrazyGames!.SDK.ad.requestAd("midgame", callbacks);
328+
} catch (error) {
329+
console.error("Failed to request midgame ad:", error);
330+
resolve();
331+
}
332+
});
333+
}
203334
}
204335

205336
export const crazyGamesSDK = new CrazyGamesSDK();

src/client/HostLobbyModal.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ export class HostLobbyModal extends BaseModal {
113113
}
114114

115115
private async buildLobbyUrl(): Promise<string> {
116+
if (crazyGamesSDK.isOnCrazyGames()) {
117+
const link = crazyGamesSDK.createInviteLink(this.lobbyId);
118+
if (link !== null) {
119+
return link;
120+
}
121+
}
116122
const config = await getServerConfigFromClient();
117123
return `${window.location.origin}/${config.workerPath(this.lobbyId)}/game/${this.lobbyId}?lobby&s=${encodeURIComponent(this.lobbyUrlSuffix)}`;
118124
}
@@ -123,7 +129,9 @@ export class HostLobbyModal extends BaseModal {
123129
}
124130

125131
private updateHistory(url: string): void {
126-
history.replaceState(null, "", url);
132+
if (!crazyGamesSDK.isOnCrazyGames()) {
133+
history.replaceState(null, "", url);
134+
}
127135
}
128136

129137
render() {

src/client/LangSelector.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ export class LangSelector extends LitElement {
228228
"stats-modal",
229229
"flag-input-modal",
230230
"flag-input",
231+
"matchmaking-button",
231232
];
232233

233234
document.title = this.translateText("main.title") ?? document.title;

src/client/Main.ts

Lines changed: 27 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ class Client {
214214
private usernameInput: UsernameInput | null = null;
215215
private flagInput: FlagInput | null = null;
216216

217+
private hostModal: HostPrivateLobbyModal;
217218
private joinModal: JoinPrivateLobbyModal;
218219
private publicLobby: PublicLobby;
219220
private userSettings: UserSettings = new UserSettings();
@@ -431,56 +432,8 @@ class Client {
431432
) {
432433
console.warn("Matchmaking modal element not found");
433434
}
434-
const matchmakingButton = document.getElementById("matchmaking-button");
435-
const matchmakingButtonLoggedOut = document.getElementById(
436-
"matchmaking-button-logged-out",
437-
);
438-
439-
const updateMatchmakingButton = (loggedIn: boolean) => {
440-
if (!loggedIn) {
441-
matchmakingButton?.classList.add("hidden");
442-
matchmakingButtonLoggedOut?.classList.remove("hidden");
443-
} else {
444-
matchmakingButton?.classList.remove("hidden");
445-
matchmakingButtonLoggedOut?.classList.add("hidden");
446-
}
447-
};
448-
449-
if (matchmakingButton) {
450-
matchmakingButton.addEventListener("click", () => {
451-
if (this.usernameInput?.isValid()) {
452-
window.showPage?.("page-matchmaking");
453-
this.publicLobby.leaveLobby();
454-
} else {
455-
window.dispatchEvent(
456-
new CustomEvent("show-message", {
457-
detail: {
458-
message: this.usernameInput?.validationError,
459-
color: "red",
460-
duration: 3000,
461-
},
462-
}),
463-
);
464-
}
465-
});
466-
}
467-
468-
if (matchmakingButtonLoggedOut) {
469-
matchmakingButtonLoggedOut.addEventListener("click", () => {
470-
window.showPage?.("page-account");
471-
});
472-
}
473435

474436
const onUserMe = async (userMeResponse: UserMeResponse | false) => {
475-
// Check if user has actual authentication (discord or email), not just a publicId
476-
const loggedIn =
477-
userMeResponse !== false &&
478-
userMeResponse !== null &&
479-
typeof userMeResponse === "object" &&
480-
userMeResponse.user &&
481-
(userMeResponse.user.discord !== undefined ||
482-
userMeResponse.user.email !== undefined);
483-
updateMatchmakingButton(loggedIn);
484437
updateAccountNavButton(userMeResponse);
485438
document.dispatchEvent(
486439
new CustomEvent("userMeResponse", {
@@ -522,10 +475,10 @@ class Client {
522475
}
523476
});
524477

525-
const hostModal = document.querySelector(
478+
this.hostModal = document.querySelector(
526479
"host-lobby-modal",
527480
) as HostPrivateLobbyModal;
528-
if (!hostModal || !(hostModal instanceof HostPrivateLobbyModal)) {
481+
if (!this.hostModal || !(this.hostModal instanceof HostPrivateLobbyModal)) {
529482
console.warn("Host private lobby modal element not found");
530483
}
531484
const hostLobbyButton = document.getElementById("host-lobby-button");
@@ -641,6 +594,14 @@ class Client {
641594
return;
642595
}
643596
}
597+
crazyGamesSDK.isInstantMultiplayer().then((isInstant) => {
598+
if (isInstant) {
599+
console.log(
600+
`CrazyGames: joining instant multiplayer lobby from CrazyGames`,
601+
);
602+
this.hostModal.open();
603+
}
604+
});
644605

645606
const strip = () =>
646607
history.replaceState(
@@ -951,11 +912,27 @@ class Client {
951912
}
952913
}
953914

915+
// Hide elements with no-crazygames class if on CrazyGames
916+
const hideCrazyGamesElements = () => {
917+
if (crazyGamesSDK.isOnCrazyGames()) {
918+
document.querySelectorAll(".no-crazygames").forEach((el) => {
919+
(el as HTMLElement).style.display = "none";
920+
});
921+
}
922+
};
923+
954924
// Initialize the client when the DOM is loaded
955925
const bootstrap = () => {
956926
initLayout();
957927
new Client().initialize();
958928
initNavigation();
929+
930+
// Hide elements immediately
931+
hideCrazyGamesElements();
932+
933+
// Also hide elements after a short delay to catch late-rendered components
934+
setTimeout(hideCrazyGamesElements, 100);
935+
setTimeout(hideCrazyGamesElements, 500);
959936
};
960937

961938
if (document.readyState === "loading") {

0 commit comments

Comments
 (0)