-
Notifications
You must be signed in to change notification settings - Fork 785
crazy games integrations #2675
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
crazy games integrations #2675
Conversation
WalkthroughThis PR integrates CrazyGames SDK across the client application. It introduces a new CrazyGamesSDK wrapper class for platform detection and lifecycle management, updates key components to report gameplay events, and replaces modal event-based closing with callback-based closing. Changes
Sequence DiagramssequenceDiagram
participant App as Client App
participant SDK as CrazyGamesSDK
participant Platform as CrazyGames Platform
rect rgba(76, 175, 80, 0.1)
Note over App,Platform: Initialization Flow
App->>SDK: maybeInit()
SDK->>SDK: isOnCrazyGames()?
alt On CrazyGames
SDK->>SDK: await delay
SDK->>Platform: window.CrazyGames.SDK.init()
Platform-->>SDK: init complete
SDK->>SDK: initialized = true
else Not on CrazyGames
SDK->>SDK: skip initialization
end
SDK-->>App: ready
end
sequenceDiagram
participant User as Player
participant App as Client App
participant SDK as CrazyGamesSDK
participant Platform as CrazyGames Platform
rect rgba(33, 150, 243, 0.1)
Note over User,Platform: Gameplay Lifecycle
User->>App: Start Game
App->>SDK: gameplayStart()
SDK->>Platform: window.CrazyGames.SDK.gameplayStart()
Platform-->>SDK: acknowledged
Note over User,Platform: ...gameplay in progress...
User->>App: Win Condition
App->>SDK: happytime()
SDK->>Platform: window.CrazyGames.SDK.happytime()
Platform-->>SDK: acknowledged
end
rect rgba(244, 67, 54, 0.1)
Note over User,Platform: Exit Flow
User->>App: Exit Game
App->>SDK: gameplayStop()
SDK->>Platform: window.CrazyGames.SDK.gameplayStop()
Platform-->>SDK: acknowledged
App->>App: Navigate away
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks✅ Passed checks (3 passed)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
3756357 to
7000231
Compare
7000231 to
226cf0c
Compare
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.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/client/components/baseComponents/Modal.ts (1)
11-11: Consider using@state()or a plain class property for the callback.
@property({ type: Function })works but thetype: Functionconverter doesn't do much for callbacks. A simpler pattern is to use a plain public property or@state()since this callback is set programmatically, not via HTML attribute.🔎 Suggested simplification
- @property({ type: Function }) onClose?: () => void; + public onClose?: () => void;
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/client/CrazyGamesSDK.tssrc/client/HostLobbyModal.tssrc/client/Main.tssrc/client/components/baseComponents/Modal.tssrc/client/graphics/layers/GameRightSidebar.tssrc/client/graphics/layers/WinModal.tssrc/client/index.html
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-06-09T02:20:43.637Z
Learnt from: VariableVince
Repo: openfrontio/OpenFrontIO PR: 1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.
Applied to files:
src/client/graphics/layers/WinModal.tssrc/client/Main.tssrc/client/HostLobbyModal.tssrc/client/graphics/layers/GameRightSidebar.ts
📚 Learning: 2025-10-18T11:00:57.142Z
Learnt from: NewYearNewPhil
Repo: openfrontio/OpenFrontIO PR: 2230
File: src/client/graphics/GameRenderer.ts:269-277
Timestamp: 2025-10-18T11:00:57.142Z
Learning: In src/client/graphics/GameRenderer.ts, the GameRecapCapture implementation does not use setCaptureRenderEnabled on layers. Instead, it uses RecapCaptureSurface.capture() to render capture layers (TerrainLayer, TerritoryLayer, RailroadLayer, StructureIconsLayer, UnitLayer) directly to an off-screen canvas without requiring layer-level capture mode methods.
Applied to files:
src/client/graphics/layers/WinModal.tssrc/client/graphics/layers/GameRightSidebar.ts
📚 Learning: 2025-10-08T17:14:49.369Z
Learnt from: Foorack
Repo: openfrontio/OpenFrontIO PR: 2141
File: src/client/ClientGameRunner.ts:228-234
Timestamp: 2025-10-08T17:14:49.369Z
Learning: In `ClientGameRunner.ts`, the `myPlayer` field is always set when `shouldPreventWindowClose()` is called, so the null check in that method is sufficient without needing to fetch it again from `gameView.playerByClientID()`.
Applied to files:
src/client/Main.ts
📚 Learning: 2025-10-21T20:06:04.823Z
Learnt from: Saphereye
Repo: openfrontio/OpenFrontIO PR: 2233
File: src/client/HostLobbyModal.ts:891-891
Timestamp: 2025-10-21T20:06:04.823Z
Learning: For the HumansVsNations game mode in `src/client/HostLobbyModal.ts` and related files, the implementation strategy is to generate all nations and adjust their strength for balancing, rather than limiting lobby size based on the number of available nations on the map.
Applied to files:
src/client/HostLobbyModal.ts
🧬 Code graph analysis (4)
src/client/graphics/layers/WinModal.ts (1)
src/client/CrazyGamesSDK.ts (1)
crazyGamesSDK(205-205)
src/client/Main.ts (2)
src/client/CrazyGamesSDK.ts (1)
crazyGamesSDK(205-205)src/core/Schemas.ts (1)
ID(205-208)
src/client/HostLobbyModal.ts (1)
src/client/CrazyGamesSDK.ts (1)
crazyGamesSDK(205-205)
src/client/graphics/layers/GameRightSidebar.ts (1)
src/client/CrazyGamesSDK.ts (1)
crazyGamesSDK(205-205)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Deploy to openfront.dev
🔇 Additional comments (22)
src/client/components/baseComponents/Modal.ts (1)
78-83: Good guard against double-close.The check prevents redundant callback invocations and state toggles. Implementation is clean.
src/client/index.html (1)
486-489: Formatting-only change — no concerns.The onclick handler behavior is unchanged.
src/client/graphics/layers/WinModal.ts (2)
19-19: Import looks good.Singleton import aligns with the SDK module pattern used across the codebase.
297-297: Happy-time triggers on win — good integration.Both team and individual win paths call
happytime(). The fire-and-forget pattern is fine since errors are handled internally by the SDK wrapper.Also applies to: 320-320
src/client/Main.ts (6)
119-119:maybeInit()is async but not awaited.The SDK might not be initialized when early code paths try to use it. This is likely acceptable since
isReady()guards all SDK methods, but consider awaiting or documenting the intent.
166-171:asyncinbeforeunloadhandler — browser may not wait.Browsers typically ignore async work in
beforeunload. Theawait crazyGamesSDK.gameplayStop()may not complete before the page unloads. This is a known limitation and acceptable here since the SDK handles it gracefully, but be aware it's best-effort.
386-395: CrazyGames invite flow looks correct.Early-exit when invite param is valid prevents fallthrough to hash-based join. The
ID.safeParsevalidation aligns with the schema atsrc/core/Schemas.ts(8-char alphanumeric).
563-564: Loading start reported — good.Matches the expected lifecycle: loading → gameplay.
584-588: Loading stop and gameplay start with error handling — good.Errors are logged but don't block game start.
605-610: Gameplay stop on leave with error handling — good.Same pattern as start, consistent error handling.
src/client/HostLobbyModal.ts (5)
36-40: Type extension for modal element looks good.Adding
onCloseto the type matches the new Modal.ts contract.
612-614: Invite button shown after lobby creation — correct placement.The check with
isOnCrazyGames()ensures the button only appears on the CrazyGames platform.
629-631: onClose callback wiring — clean pattern.Ensures
close()is called regardless of how the modal is dismissed.
637-638: Invite button hidden on close — good cleanup.The SDK method guards against being called when not on CrazyGames.
67-67: Good debounce for bots slider.Prevents excessive API calls while user drags the slider. The 300ms delay is reasonable. Timer cleanup in
close()(lines 646-649) prevents stale calls.Also applies to: 682-690
src/client/CrazyGamesSDK.ts (7)
1-22: Clean global type augmentation.The
declare globalblock properly extends the Window interface with the CrazyGames SDK surface. Types match the SDK v3 API.
28-41: Platform detection is reasonable.The iframe check with cross-origin fallback to
document.referrerhandles the common cases. Note that referrer can be spoofed, but for feature-gating (not security), this is acceptable.
47-77: Polling for SDK availability is pragmatic.100 attempts at 100ms intervals gives a 10-second window for the script to load. The error handling and early returns are clean.
79-114: Gameplay lifecycle methods are well-guarded.Both
gameplayStartandgameplayStopcheckisReady()and trackisGameplayActiveto prevent duplicate calls. Error handling logs but doesn't throw, allowing callers to fire-and-forget.
155-174:showInviteButtonreturns the invite link — good.The return value can be useful for logging or display, and null is returned on error.
189-202:getInviteGameIdretrieves the invite param correctly.Uses the SDK's
getInviteParammethod. Null handling is consistent with other methods.
204-205: Singleton export is the right pattern here.Single shared instance ensures consistent state tracking across the app.
226cf0c to
f9b7273
Compare
099b6d5 to
dae46d7
Compare
dae46d7 to
f7e069d
Compare
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.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/client/Main.ts (1)
602-603: Consider awaiting gameplayStop for consistency.
gameplayStop()is called withoutawait, meaning it fires and forgets. If this is intentional for performance, consider adding a comment. Otherwise, makehandleLeaveLobbyasync and await the call.🔎 Optional: Make handleLeaveLobby async
- private async handleLeaveLobby(/* event: CustomEvent */) { + private async handleLeaveLobby(/* event: CustomEvent */) { if (this.gameStop === null) { return; } console.log("leaving lobby, cancelling game"); this.gameStop(); this.gameStop = null; - crazyGamesSDK.gameplayStop(); + await crazyGamesSDK.gameplayStop(); this.gutterAds.hide();src/client/HostLobbyModal.ts (1)
627-629: Circular callback pattern works but could be clearer.The
onClosecallback invokesthis.close(), which then callsmodalEl.close()again. The guard inModal.ts(line 79) prevents infinite recursion by checkingisModalOpen, but this flow is subtle and could confuse future maintainers.Consider refactoring to avoid the circular call:
🔎 Alternative: Separate cleanup logic
public open() { // ... existing code ... this.modalEl?.open(); - this.modalEl.onClose = () => { - this.close(); - }; + this.modalEl.onClose = () => { + this.cleanup(); + }; this.playersInterval = setInterval(() => this.pollPlayers(), 1000); this.loadNationCount(); } public close() { - console.log("Closing host lobby modal"); - crazyGamesSDK.hideInviteButton(); this.modalEl?.close(); + this.cleanup(); + } + + private cleanup() { + console.log("Closing host lobby modal"); + crazyGamesSDK.hideInviteButton(); - this.modalEl?.close(); this.copySuccess = false; if (this.playersInterval) { clearInterval(this.playersInterval); this.playersInterval = null; } // Clear any pending bot updates if (this.botsUpdateTimer !== null) { clearTimeout(this.botsUpdateTimer); this.botsUpdateTimer = null; } }This separates cleanup logic from modal state management, making the flow more explicit.
Also applies to: 634-637
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
README.mdsrc/client/CrazyGamesSDK.tssrc/client/HostLobbyModal.tssrc/client/Main.tssrc/client/components/baseComponents/Modal.tssrc/client/graphics/layers/GameRightSidebar.tssrc/client/graphics/layers/WinModal.tssrc/client/index.html
💤 Files with no reviewable changes (1)
- README.md
🚧 Files skipped from review as they are similar to previous changes (3)
- src/client/graphics/layers/WinModal.ts
- src/client/graphics/layers/GameRightSidebar.ts
- src/client/CrazyGamesSDK.ts
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: VariableVince
Repo: openfrontio/OpenFrontIO PR: 1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.
📚 Learning: 2025-06-09T02:20:43.637Z
Learnt from: VariableVince
Repo: openfrontio/OpenFrontIO PR: 1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.
Applied to files:
src/client/Main.tssrc/client/HostLobbyModal.ts
📚 Learning: 2025-10-08T17:14:49.369Z
Learnt from: Foorack
Repo: openfrontio/OpenFrontIO PR: 2141
File: src/client/ClientGameRunner.ts:228-234
Timestamp: 2025-10-08T17:14:49.369Z
Learning: In `ClientGameRunner.ts`, the `myPlayer` field is always set when `shouldPreventWindowClose()` is called, so the null check in that method is sufficient without needing to fetch it again from `gameView.playerByClientID()`.
Applied to files:
src/client/Main.ts
📚 Learning: 2025-10-21T20:06:04.823Z
Learnt from: Saphereye
Repo: openfrontio/OpenFrontIO PR: 2233
File: src/client/HostLobbyModal.ts:891-891
Timestamp: 2025-10-21T20:06:04.823Z
Learning: For the HumansVsNations game mode in `src/client/HostLobbyModal.ts` and related files, the implementation strategy is to generate all nations and adjust their strength for balancing, rather than limiting lobby size based on the number of available nations on the map.
Applied to files:
src/client/HostLobbyModal.ts
🧬 Code graph analysis (2)
src/client/Main.ts (2)
src/client/CrazyGamesSDK.ts (1)
crazyGamesSDK(205-205)src/core/Schemas.ts (1)
ID(205-208)
src/client/HostLobbyModal.ts (1)
src/client/CrazyGamesSDK.ts (1)
crazyGamesSDK(205-205)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Deploy to openfront.dev
🔇 Additional comments (9)
src/client/index.html (1)
133-137: LGTM! Async attribute added as suggested.The CrazyGames SDK script now loads asynchronously, preventing page parsing blockage. This addresses the previous review comment and aligns with the SDK initialization pattern in
CrazyGamesSDK.tsthat polls for readiness.src/client/components/baseComponents/Modal.ts (2)
11-11: Clean callback property addition.The optional
onClosecallback provides a straightforward hook for parent components to react to modal closure.
78-83: Guard prevents duplicate calls.The
isModalOpenguard ensuresonCloseis only invoked once per close action, preventing duplicate cleanup when the modal is already closed.src/client/Main.ts (4)
119-119: SDK initialized early.Calling
maybeInit()during client initialization ensures the SDK is ready before any lobby operations occur.
166-172: Best-effort cleanup on page unload.The
awaitinbeforeunloadprovides best-effort cleanup but may not complete if the browser terminates the page quickly. This is acceptable for this use case.
386-395: CrazyGames invites take priority.The URL handling correctly prioritizes CrazyGames invite parameters over hash-based joins, with proper ID validation using the
IDschema fromSchemas.ts.
563-563: Lifecycle reporting integrated.The SDK lifecycle calls (
loadingStart,loadingStop,gameplayStart) are placed at appropriate points in the join flow, coordinating with UI transitions.Also applies to: 583-584
src/client/HostLobbyModal.ts (2)
612-612: Invite button shown after lobby creation.
showInviteButtonis correctly called afterlobbyIdis set, ensuring the SDK has the necessary context.
680-688: Debouncing reduces server load.The 300ms debounce on bot count changes prevents excessive
putGameConfigcalls while the user drags the slider. This is a good optimization.
f7e069d to
2af124f
Compare
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.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/client/components/baseComponents/Modal.ts (1)
11-11: Consider using@state()for internal callback instead of@property().The
onClosecallback is set programmatically by parent components, not via HTML attributes. Using@property({ type: Function })attempts attribute reflection which doesn't work well for functions. Consider using@state()or a plain class property instead.🔎 Suggested change
- @property({ type: Function }) onClose?: () => void; + public onClose?: () => void;src/client/HostLobbyModal.ts (1)
609-629: Potential issue:showInviteButtoncalled before modal is opened.The
showInviteButton(this.lobbyId)is called in the first.then()block, butmodalEl.open()is called after both.then()blocks. Consider reordering so the modal is visible when the invite button appears.Also, the chained
.then()blocks run in sequence but don't depend on each other - they could be combined for clarity.🔎 Suggested refactor
createLobby(this.lobbyCreatorClientID) .then((lobby) => { this.lobbyId = lobby.gameID; crazyGamesSDK.showInviteButton(this.lobbyId); - }) - .then(() => { this.dispatchEvent( new CustomEvent("join-lobby", { detail: { gameID: this.lobbyId, clientID: this.lobbyCreatorClientID, } as JoinLobbyEvent, bubbles: true, composed: true, }), ); }); - this.modalEl?.open(); + this.modalEl.open();src/client/CrazyGamesSDK.ts (1)
129-153: Consider tracking loading state to prevent mismatched start/stop.Unlike gameplay, loading calls have no state tracking. If
loadingStart()is called twice, orloadingStop()is called without a prior start, the SDK behavior is unknown.🔎 Suggested improvement
private initialized = false; private isGameplayActive = false; + private isLoading = false; loadingStart(): void { if (!this.isReady()) { return; } + + if (this.isLoading) { + console.warn("Loading already started"); + return; + } try { window.CrazyGames!.SDK.game.loadingStart(); + this.isLoading = true; console.log("CrazyGames: loading started"); } catch (error) { console.error("Failed to report loading start:", error); } } loadingStop(): void { if (!this.isReady()) { return; } + + if (!this.isLoading) { + return; + } try { window.CrazyGames!.SDK.game.loadingStop(); + this.isLoading = false; console.log("CrazyGames: loading stopped"); } catch (error) { console.error("Failed to report loading stop:", error); } }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/client/CrazyGamesSDK.tssrc/client/HostLobbyModal.tssrc/client/Main.tssrc/client/components/baseComponents/Modal.tssrc/client/graphics/layers/GameRightSidebar.tssrc/client/graphics/layers/WinModal.tssrc/client/index.html
🚧 Files skipped from review as they are similar to previous changes (2)
- src/client/graphics/layers/GameRightSidebar.ts
- src/client/index.html
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-06-09T02:20:43.637Z
Learnt from: VariableVince
Repo: openfrontio/OpenFrontIO PR: 1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.
Applied to files:
src/client/Main.tssrc/client/HostLobbyModal.tssrc/client/graphics/layers/WinModal.ts
📚 Learning: 2025-10-08T17:14:49.369Z
Learnt from: Foorack
Repo: openfrontio/OpenFrontIO PR: 2141
File: src/client/ClientGameRunner.ts:228-234
Timestamp: 2025-10-08T17:14:49.369Z
Learning: In `ClientGameRunner.ts`, the `myPlayer` field is always set when `shouldPreventWindowClose()` is called, so the null check in that method is sufficient without needing to fetch it again from `gameView.playerByClientID()`.
Applied to files:
src/client/Main.ts
📚 Learning: 2025-10-21T20:06:04.823Z
Learnt from: Saphereye
Repo: openfrontio/OpenFrontIO PR: 2233
File: src/client/HostLobbyModal.ts:891-891
Timestamp: 2025-10-21T20:06:04.823Z
Learning: For the HumansVsNations game mode in `src/client/HostLobbyModal.ts` and related files, the implementation strategy is to generate all nations and adjust their strength for balancing, rather than limiting lobby size based on the number of available nations on the map.
Applied to files:
src/client/HostLobbyModal.ts
📚 Learning: 2025-10-18T11:00:57.142Z
Learnt from: NewYearNewPhil
Repo: openfrontio/OpenFrontIO PR: 2230
File: src/client/graphics/GameRenderer.ts:269-277
Timestamp: 2025-10-18T11:00:57.142Z
Learning: In src/client/graphics/GameRenderer.ts, the GameRecapCapture implementation does not use setCaptureRenderEnabled on layers. Instead, it uses RecapCaptureSurface.capture() to render capture layers (TerrainLayer, TerritoryLayer, RailroadLayer, StructureIconsLayer, UnitLayer) directly to an off-screen canvas without requiring layer-level capture mode methods.
Applied to files:
src/client/graphics/layers/WinModal.ts
🔇 Additional comments (18)
src/client/components/baseComponents/Modal.ts (1)
78-83: LGTM!The close guard prevents redundant callbacks and the callback-based approach is cleaner than event dispatch for this use case.
src/client/graphics/layers/WinModal.ts (2)
19-19: LGTM!Import of
crazyGamesSDKis correctly placed with other imports.
292-320: LGTM!The
happytime()calls are correctly placed in both win paths (team win and individual player win). The SDK wrapper handles platform detection internally.src/client/Main.ts (4)
386-395: LGTM!CrazyGames invite handling is correctly prioritized before hash-based join logic. The early return is appropriate since CrazyGames environments don't use URL hashes for invites.
563-564: LGTM!The
loadingStart()call is correctly placed before showing the game starting modal.
583-585: LGTM!Correct sequence:
loadingStop()followed bygameplayStart()when the game actually begins.
602-604: LGTM!The
gameplayStop()call is correctly placed when leaving the lobby.src/client/HostLobbyModal.ts (4)
30-30: LGTM!Import placed correctly with other imports.
36-40: LGTM!Type augmentation for
modalElcorrectly includes the optionalonClosecallback.
670-689: LGTM!Good debounce implementation for the bots slider. The 300ms delay reduces unnecessary API calls during rapid slider adjustments, and cleanup on modal close prevents stale updates.
634-648: LGTM!The
close()method correctly cleans up the invite button, polling interval, and pending bots update timer.src/client/CrazyGamesSDK.ts (7)
1-22: LGTM!Clean global type declaration for the CrazyGames SDK. The interface structure matches the expected SDK API.
47-77: LGTM!The initialization pattern with polling (up to 10 seconds) is consistent with other SDK loading in the codebase. The guard against double-initialization is correct.
79-114: LGTM!The gameplay state tracking prevents duplicate SDK calls. The
isGameplayActiveflag ensures proper pairing of start/stop events.
116-127: LGTM!Simple wrapper for
happytime()with proper readiness check and error handling.
155-202: LGTM!Invite button methods are well-structured with proper error handling and return values.
205-205: LGTM!Singleton pattern is appropriate here since the SDK instance should be shared across the application.
28-41: Use CrazyGames.SDK.environment or CrazyGames.SDK.platform instead of custom iframe detection.The current implementation relies on custom heuristics (iframe check + parent URL + referrer fallback), but CrazyGames official guidance recommends using the SDK's built-in platform detection APIs. The document.referrer fallback is especially problematic—it can be stripped or empty due to Referrer-Policy headers, iframe referrerPolicy attributes, or sandboxing. Use postMessage for reliable cross-origin parent identification, or better yet, rely on CrazyGames.SDK.platform where available.
| window.addEventListener("beforeunload", async () => { | ||
| console.log("Browser is closing"); | ||
| if (this.gameStop !== null) { | ||
| this.gameStop(); | ||
| await crazyGamesSDK.gameplayStop(); | ||
| } | ||
| }); |
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.
Async callback in beforeunload may not complete.
The await crazyGamesSDK.gameplayStop() inside beforeunload handler is unlikely to complete before the browser closes the page. The beforeunload event does not wait for async operations.
🔎 Suggested fix
- window.addEventListener("beforeunload", async () => {
+ window.addEventListener("beforeunload", () => {
console.log("Browser is closing");
if (this.gameStop !== null) {
this.gameStop();
- await crazyGamesSDK.gameplayStop();
+ // Fire-and-forget - beforeunload doesn't wait for async
+ crazyGamesSDK.gameplayStop();
}
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| window.addEventListener("beforeunload", async () => { | |
| console.log("Browser is closing"); | |
| if (this.gameStop !== null) { | |
| this.gameStop(); | |
| await crazyGamesSDK.gameplayStop(); | |
| } | |
| }); | |
| window.addEventListener("beforeunload", () => { | |
| console.log("Browser is closing"); | |
| if (this.gameStop !== null) { | |
| this.gameStop(); | |
| // Fire-and-forget - beforeunload doesn't wait for async | |
| crazyGamesSDK.gameplayStop(); | |
| } | |
| }); |
🤖 Prompt for AI Agents
In src/client/Main.ts around lines 166 to 172, the beforeunload handler uses an
async await (await crazyGamesSDK.gameplayStop()) which the browser will not wait
for; replace the async approach with a synchronous/guaranteed delivery method:
call crazyGamesSDK.gameplayStop() without awaiting and send any final telemetry
via navigator.sendBeacon or move the gameplayStop invocation to
pagehide/visibilitychange (or trigger it earlier when the user initiates exit)
so the stop signal is reliably delivered before unload.
Description:
Integrate with crazy games SDK
Please complete the following:
Please put your Discord username so you can be contacted if a bug or regression is found:
evan