@hyve-sdk/js 2.14.0-canary.3 → 2.14.1-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -244,6 +244,19 @@ await hyve.gameplayStop();
244
244
  await hyve.happytime();
245
245
  ```
246
246
 
247
+ Gameplay start/stop tracking is handled automatically where the SDK has the
248
+ context to do so:
249
+
250
+ - `gameplayStart()` and `gameplayEnd()` lifecycle telemetry calls forward to
251
+ the CrazyGames SDK, so games emitting the required Hyve lifecycle events get
252
+ CrazyGames gameplay tracking for free.
253
+ - Ads shown via `showAd()` automatically stop gameplay tracking for the
254
+ duration of the ad and resume it afterwards (only if gameplay was active
255
+ when the ad was requested).
256
+ - Calls are idempotent — duplicate starts or stops are not forwarded.
257
+ - Focus/tab changes are intentionally not tracked: CrazyGames handles those on
258
+ their side and instructs games not to signal them.
259
+
247
260
  ### Playgama
248
261
 
249
262
  When running on Playgama, the SDK auto-initializes the Playgama Bridge and routes ads through it. No additional setup required.
package/dist/index.d.mts CHANGED
@@ -823,6 +823,8 @@ declare class HyveClient {
823
823
  configureAds(config: AdConfig): void;
824
824
  /**
825
825
  * Show an ad
826
+ * On CrazyGames, gameplay tracking is automatically stopped for the duration
827
+ * of the ad and resumed afterwards if it was active.
826
828
  * @param type Type of ad to show ('rewarded', 'interstitial', or 'preroll')
827
829
  * @param placement Optional placement key, forwarded to the native AdMob path
828
830
  * to resolve a per-placement unit ID. Ignored on the web (H5/Playgama) path.
@@ -1033,6 +1035,13 @@ declare global {
1033
1035
  declare class CrazyGamesService {
1034
1036
  private initialized;
1035
1037
  private environment;
1038
+ /**
1039
+ * Whether gameplay is currently marked active on the CrazyGames side.
1040
+ * Makes gameplayStart/gameplayStop idempotent and lets ad flows restore the
1041
+ * pre-ad state. Focus/tab changes are intentionally NOT tracked — CrazyGames
1042
+ * handles those on their side (per docs.crazygames.com/sdk/game).
1043
+ */
1044
+ private gameplayActive;
1036
1045
  /**
1037
1046
  * Detects if the game is running on the CrazyGames platform.
1038
1047
  * Games on CrazyGames run inside an iframe, so we check document.referrer
@@ -1065,11 +1074,14 @@ declare class CrazyGamesService {
1065
1074
  /**
1066
1075
  * Notifies CrazyGames that gameplay has started.
1067
1076
  * Call when the player actively begins or resumes playing.
1077
+ * Idempotent — repeated calls while gameplay is already active are no-ops.
1068
1078
  */
1069
1079
  gameplayStart(): void;
1070
1080
  /**
1071
1081
  * Notifies CrazyGames that gameplay has stopped.
1072
- * Call during menu access, level completion, or pausing.
1082
+ * Call during menu access, level completion, or pausing. Do NOT call on
1083
+ * focus/tab changes — CrazyGames handles those on their side.
1084
+ * Idempotent — repeated calls while gameplay is already stopped are no-ops.
1073
1085
  */
1074
1086
  gameplayStop(): void;
1075
1087
  /**
package/dist/index.d.ts CHANGED
@@ -823,6 +823,8 @@ declare class HyveClient {
823
823
  configureAds(config: AdConfig): void;
824
824
  /**
825
825
  * Show an ad
826
+ * On CrazyGames, gameplay tracking is automatically stopped for the duration
827
+ * of the ad and resumed afterwards if it was active.
826
828
  * @param type Type of ad to show ('rewarded', 'interstitial', or 'preroll')
827
829
  * @param placement Optional placement key, forwarded to the native AdMob path
828
830
  * to resolve a per-placement unit ID. Ignored on the web (H5/Playgama) path.
@@ -1033,6 +1035,13 @@ declare global {
1033
1035
  declare class CrazyGamesService {
1034
1036
  private initialized;
1035
1037
  private environment;
1038
+ /**
1039
+ * Whether gameplay is currently marked active on the CrazyGames side.
1040
+ * Makes gameplayStart/gameplayStop idempotent and lets ad flows restore the
1041
+ * pre-ad state. Focus/tab changes are intentionally NOT tracked — CrazyGames
1042
+ * handles those on their side (per docs.crazygames.com/sdk/game).
1043
+ */
1044
+ private gameplayActive;
1036
1045
  /**
1037
1046
  * Detects if the game is running on the CrazyGames platform.
1038
1047
  * Games on CrazyGames run inside an iframe, so we check document.referrer
@@ -1065,11 +1074,14 @@ declare class CrazyGamesService {
1065
1074
  /**
1066
1075
  * Notifies CrazyGames that gameplay has started.
1067
1076
  * Call when the player actively begins or resumes playing.
1077
+ * Idempotent — repeated calls while gameplay is already active are no-ops.
1068
1078
  */
1069
1079
  gameplayStart(): void;
1070
1080
  /**
1071
1081
  * Notifies CrazyGames that gameplay has stopped.
1072
- * Call during menu access, level completion, or pausing.
1082
+ * Call during menu access, level completion, or pausing. Do NOT call on
1083
+ * focus/tab changes — CrazyGames handles those on their side.
1084
+ * Idempotent — repeated calls while gameplay is already stopped are no-ops.
1073
1085
  */
1074
1086
  gameplayStop(): void;
1075
1087
  /**
package/dist/index.js CHANGED
@@ -1243,6 +1243,13 @@ var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v3.js";
1243
1243
  var CrazyGamesService = class {
1244
1244
  initialized = false;
1245
1245
  environment = null;
1246
+ /**
1247
+ * Whether gameplay is currently marked active on the CrazyGames side.
1248
+ * Makes gameplayStart/gameplayStop idempotent and lets ad flows restore the
1249
+ * pre-ad state. Focus/tab changes are intentionally NOT tracked — CrazyGames
1250
+ * handles those on their side (per docs.crazygames.com/sdk/game).
1251
+ */
1252
+ gameplayActive = false;
1246
1253
  /**
1247
1254
  * Detects if the game is running on the CrazyGames platform.
1248
1255
  * Games on CrazyGames run inside an iframe, so we check document.referrer
@@ -1317,6 +1324,8 @@ var CrazyGamesService = class {
1317
1324
  completedAt: Date.now()
1318
1325
  };
1319
1326
  }
1327
+ const resumeAfterAd = this.gameplayActive;
1328
+ this.gameplayStop();
1320
1329
  return new Promise((resolve) => {
1321
1330
  sdk.ad.requestAd("midgame", {
1322
1331
  adStarted: () => {
@@ -1324,10 +1333,12 @@ var CrazyGamesService = class {
1324
1333
  },
1325
1334
  adFinished: () => {
1326
1335
  callbacks?.onAfterAd?.();
1336
+ if (resumeAfterAd) this.gameplayStart();
1327
1337
  resolve({ success: true, type: "interstitial", requestedAt, completedAt: Date.now() });
1328
1338
  },
1329
1339
  adError: (error) => {
1330
1340
  callbacks?.onAfterAd?.();
1341
+ if (resumeAfterAd) this.gameplayStart();
1331
1342
  resolve({
1332
1343
  success: false,
1333
1344
  type: "interstitial",
@@ -1355,6 +1366,8 @@ var CrazyGamesService = class {
1355
1366
  completedAt: Date.now()
1356
1367
  };
1357
1368
  }
1369
+ const resumeAfterAd = this.gameplayActive;
1370
+ this.gameplayStop();
1358
1371
  return new Promise((resolve) => {
1359
1372
  sdk.ad.requestAd("rewarded", {
1360
1373
  adStarted: () => {
@@ -1363,10 +1376,12 @@ var CrazyGamesService = class {
1363
1376
  adFinished: () => {
1364
1377
  callbacks?.onRewardEarned?.();
1365
1378
  callbacks?.onAfterAd?.();
1379
+ if (resumeAfterAd) this.gameplayStart();
1366
1380
  resolve({ success: true, type: "rewarded", requestedAt, completedAt: Date.now() });
1367
1381
  },
1368
1382
  adError: (error) => {
1369
1383
  callbacks?.onAfterAd?.();
1384
+ if (resumeAfterAd) this.gameplayStart();
1370
1385
  resolve({
1371
1386
  success: false,
1372
1387
  type: "rewarded",
@@ -1381,17 +1396,22 @@ var CrazyGamesService = class {
1381
1396
  /**
1382
1397
  * Notifies CrazyGames that gameplay has started.
1383
1398
  * Call when the player actively begins or resumes playing.
1399
+ * Idempotent — repeated calls while gameplay is already active are no-ops.
1384
1400
  */
1385
1401
  gameplayStart() {
1386
- if (!this.initialized) return;
1402
+ if (!this.initialized || this.gameplayActive) return;
1403
+ this.gameplayActive = true;
1387
1404
  window.CrazyGames?.SDK.game.gameplayStart();
1388
1405
  }
1389
1406
  /**
1390
1407
  * Notifies CrazyGames that gameplay has stopped.
1391
- * Call during menu access, level completion, or pausing.
1408
+ * Call during menu access, level completion, or pausing. Do NOT call on
1409
+ * focus/tab changes — CrazyGames handles those on their side.
1410
+ * Idempotent — repeated calls while gameplay is already stopped are no-ops.
1392
1411
  */
1393
1412
  gameplayStop() {
1394
- if (!this.initialized) return;
1413
+ if (!this.initialized || !this.gameplayActive) return;
1414
+ this.gameplayActive = false;
1395
1415
  window.CrazyGames?.SDK.game.gameplayStop();
1396
1416
  }
1397
1417
  /**
@@ -3080,6 +3100,8 @@ var HyveClient = class {
3080
3100
  }
3081
3101
  /**
3082
3102
  * Show an ad
3103
+ * On CrazyGames, gameplay tracking is automatically stopped for the duration
3104
+ * of the ad and resumed afterwards if it was active.
3083
3105
  * @param type Type of ad to show ('rewarded', 'interstitial', or 'preroll')
3084
3106
  * @param placement Optional placement key, forwarded to the native AdMob path
3085
3107
  * to resolve a per-placement unit ID. Ignored on the web (H5/Playgama) path.
package/dist/index.mjs CHANGED
@@ -1202,6 +1202,13 @@ var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v3.js";
1202
1202
  var CrazyGamesService = class {
1203
1203
  initialized = false;
1204
1204
  environment = null;
1205
+ /**
1206
+ * Whether gameplay is currently marked active on the CrazyGames side.
1207
+ * Makes gameplayStart/gameplayStop idempotent and lets ad flows restore the
1208
+ * pre-ad state. Focus/tab changes are intentionally NOT tracked — CrazyGames
1209
+ * handles those on their side (per docs.crazygames.com/sdk/game).
1210
+ */
1211
+ gameplayActive = false;
1205
1212
  /**
1206
1213
  * Detects if the game is running on the CrazyGames platform.
1207
1214
  * Games on CrazyGames run inside an iframe, so we check document.referrer
@@ -1276,6 +1283,8 @@ var CrazyGamesService = class {
1276
1283
  completedAt: Date.now()
1277
1284
  };
1278
1285
  }
1286
+ const resumeAfterAd = this.gameplayActive;
1287
+ this.gameplayStop();
1279
1288
  return new Promise((resolve) => {
1280
1289
  sdk.ad.requestAd("midgame", {
1281
1290
  adStarted: () => {
@@ -1283,10 +1292,12 @@ var CrazyGamesService = class {
1283
1292
  },
1284
1293
  adFinished: () => {
1285
1294
  callbacks?.onAfterAd?.();
1295
+ if (resumeAfterAd) this.gameplayStart();
1286
1296
  resolve({ success: true, type: "interstitial", requestedAt, completedAt: Date.now() });
1287
1297
  },
1288
1298
  adError: (error) => {
1289
1299
  callbacks?.onAfterAd?.();
1300
+ if (resumeAfterAd) this.gameplayStart();
1290
1301
  resolve({
1291
1302
  success: false,
1292
1303
  type: "interstitial",
@@ -1314,6 +1325,8 @@ var CrazyGamesService = class {
1314
1325
  completedAt: Date.now()
1315
1326
  };
1316
1327
  }
1328
+ const resumeAfterAd = this.gameplayActive;
1329
+ this.gameplayStop();
1317
1330
  return new Promise((resolve) => {
1318
1331
  sdk.ad.requestAd("rewarded", {
1319
1332
  adStarted: () => {
@@ -1322,10 +1335,12 @@ var CrazyGamesService = class {
1322
1335
  adFinished: () => {
1323
1336
  callbacks?.onRewardEarned?.();
1324
1337
  callbacks?.onAfterAd?.();
1338
+ if (resumeAfterAd) this.gameplayStart();
1325
1339
  resolve({ success: true, type: "rewarded", requestedAt, completedAt: Date.now() });
1326
1340
  },
1327
1341
  adError: (error) => {
1328
1342
  callbacks?.onAfterAd?.();
1343
+ if (resumeAfterAd) this.gameplayStart();
1329
1344
  resolve({
1330
1345
  success: false,
1331
1346
  type: "rewarded",
@@ -1340,17 +1355,22 @@ var CrazyGamesService = class {
1340
1355
  /**
1341
1356
  * Notifies CrazyGames that gameplay has started.
1342
1357
  * Call when the player actively begins or resumes playing.
1358
+ * Idempotent — repeated calls while gameplay is already active are no-ops.
1343
1359
  */
1344
1360
  gameplayStart() {
1345
- if (!this.initialized) return;
1361
+ if (!this.initialized || this.gameplayActive) return;
1362
+ this.gameplayActive = true;
1346
1363
  window.CrazyGames?.SDK.game.gameplayStart();
1347
1364
  }
1348
1365
  /**
1349
1366
  * Notifies CrazyGames that gameplay has stopped.
1350
- * Call during menu access, level completion, or pausing.
1367
+ * Call during menu access, level completion, or pausing. Do NOT call on
1368
+ * focus/tab changes — CrazyGames handles those on their side.
1369
+ * Idempotent — repeated calls while gameplay is already stopped are no-ops.
1351
1370
  */
1352
1371
  gameplayStop() {
1353
- if (!this.initialized) return;
1372
+ if (!this.initialized || !this.gameplayActive) return;
1373
+ this.gameplayActive = false;
1354
1374
  window.CrazyGames?.SDK.game.gameplayStop();
1355
1375
  }
1356
1376
  /**
@@ -3039,6 +3059,8 @@ var HyveClient = class {
3039
3059
  }
3040
3060
  /**
3041
3061
  * Show an ad
3062
+ * On CrazyGames, gameplay tracking is automatically stopped for the duration
3063
+ * of the ad and resumed afterwards if it was active.
3042
3064
  * @param type Type of ad to show ('rewarded', 'interstitial', or 'preroll')
3043
3065
  * @param placement Optional placement key, forwarded to the native AdMob path
3044
3066
  * to resolve a per-placement unit ID. Ignored on the web (H5/Playgama) path.
package/dist/react.d.mts CHANGED
@@ -554,6 +554,8 @@ declare class HyveClient {
554
554
  configureAds(config: AdConfig): void;
555
555
  /**
556
556
  * Show an ad
557
+ * On CrazyGames, gameplay tracking is automatically stopped for the duration
558
+ * of the ad and resumed afterwards if it was active.
557
559
  * @param type Type of ad to show ('rewarded', 'interstitial', or 'preroll')
558
560
  * @param placement Optional placement key, forwarded to the native AdMob path
559
561
  * to resolve a per-placement unit ID. Ignored on the web (H5/Playgama) path.
package/dist/react.d.ts CHANGED
@@ -554,6 +554,8 @@ declare class HyveClient {
554
554
  configureAds(config: AdConfig): void;
555
555
  /**
556
556
  * Show an ad
557
+ * On CrazyGames, gameplay tracking is automatically stopped for the duration
558
+ * of the ad and resumed afterwards if it was active.
557
559
  * @param type Type of ad to show ('rewarded', 'interstitial', or 'preroll')
558
560
  * @param placement Optional placement key, forwarded to the native AdMob path
559
561
  * to resolve a per-placement unit ID. Ignored on the web (H5/Playgama) path.
package/dist/react.js CHANGED
@@ -1200,6 +1200,13 @@ var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v3.js";
1200
1200
  var CrazyGamesService = class {
1201
1201
  initialized = false;
1202
1202
  environment = null;
1203
+ /**
1204
+ * Whether gameplay is currently marked active on the CrazyGames side.
1205
+ * Makes gameplayStart/gameplayStop idempotent and lets ad flows restore the
1206
+ * pre-ad state. Focus/tab changes are intentionally NOT tracked — CrazyGames
1207
+ * handles those on their side (per docs.crazygames.com/sdk/game).
1208
+ */
1209
+ gameplayActive = false;
1203
1210
  /**
1204
1211
  * Detects if the game is running on the CrazyGames platform.
1205
1212
  * Games on CrazyGames run inside an iframe, so we check document.referrer
@@ -1274,6 +1281,8 @@ var CrazyGamesService = class {
1274
1281
  completedAt: Date.now()
1275
1282
  };
1276
1283
  }
1284
+ const resumeAfterAd = this.gameplayActive;
1285
+ this.gameplayStop();
1277
1286
  return new Promise((resolve) => {
1278
1287
  sdk.ad.requestAd("midgame", {
1279
1288
  adStarted: () => {
@@ -1281,10 +1290,12 @@ var CrazyGamesService = class {
1281
1290
  },
1282
1291
  adFinished: () => {
1283
1292
  callbacks?.onAfterAd?.();
1293
+ if (resumeAfterAd) this.gameplayStart();
1284
1294
  resolve({ success: true, type: "interstitial", requestedAt, completedAt: Date.now() });
1285
1295
  },
1286
1296
  adError: (error) => {
1287
1297
  callbacks?.onAfterAd?.();
1298
+ if (resumeAfterAd) this.gameplayStart();
1288
1299
  resolve({
1289
1300
  success: false,
1290
1301
  type: "interstitial",
@@ -1312,6 +1323,8 @@ var CrazyGamesService = class {
1312
1323
  completedAt: Date.now()
1313
1324
  };
1314
1325
  }
1326
+ const resumeAfterAd = this.gameplayActive;
1327
+ this.gameplayStop();
1315
1328
  return new Promise((resolve) => {
1316
1329
  sdk.ad.requestAd("rewarded", {
1317
1330
  adStarted: () => {
@@ -1320,10 +1333,12 @@ var CrazyGamesService = class {
1320
1333
  adFinished: () => {
1321
1334
  callbacks?.onRewardEarned?.();
1322
1335
  callbacks?.onAfterAd?.();
1336
+ if (resumeAfterAd) this.gameplayStart();
1323
1337
  resolve({ success: true, type: "rewarded", requestedAt, completedAt: Date.now() });
1324
1338
  },
1325
1339
  adError: (error) => {
1326
1340
  callbacks?.onAfterAd?.();
1341
+ if (resumeAfterAd) this.gameplayStart();
1327
1342
  resolve({
1328
1343
  success: false,
1329
1344
  type: "rewarded",
@@ -1338,17 +1353,22 @@ var CrazyGamesService = class {
1338
1353
  /**
1339
1354
  * Notifies CrazyGames that gameplay has started.
1340
1355
  * Call when the player actively begins or resumes playing.
1356
+ * Idempotent — repeated calls while gameplay is already active are no-ops.
1341
1357
  */
1342
1358
  gameplayStart() {
1343
- if (!this.initialized) return;
1359
+ if (!this.initialized || this.gameplayActive) return;
1360
+ this.gameplayActive = true;
1344
1361
  window.CrazyGames?.SDK.game.gameplayStart();
1345
1362
  }
1346
1363
  /**
1347
1364
  * Notifies CrazyGames that gameplay has stopped.
1348
- * Call during menu access, level completion, or pausing.
1365
+ * Call during menu access, level completion, or pausing. Do NOT call on
1366
+ * focus/tab changes — CrazyGames handles those on their side.
1367
+ * Idempotent — repeated calls while gameplay is already stopped are no-ops.
1349
1368
  */
1350
1369
  gameplayStop() {
1351
- if (!this.initialized) return;
1370
+ if (!this.initialized || !this.gameplayActive) return;
1371
+ this.gameplayActive = false;
1352
1372
  window.CrazyGames?.SDK.game.gameplayStop();
1353
1373
  }
1354
1374
  /**
@@ -3031,6 +3051,8 @@ var HyveClient = class {
3031
3051
  }
3032
3052
  /**
3033
3053
  * Show an ad
3054
+ * On CrazyGames, gameplay tracking is automatically stopped for the duration
3055
+ * of the ad and resumed afterwards if it was active.
3034
3056
  * @param type Type of ad to show ('rewarded', 'interstitial', or 'preroll')
3035
3057
  * @param placement Optional placement key, forwarded to the native AdMob path
3036
3058
  * to resolve a per-placement unit ID. Ignored on the web (H5/Playgama) path.
package/dist/react.mjs CHANGED
@@ -1178,6 +1178,13 @@ var CRAZYGAMES_SDK_CDN = "https://sdk.crazygames.com/crazygames-sdk-v3.js";
1178
1178
  var CrazyGamesService = class {
1179
1179
  initialized = false;
1180
1180
  environment = null;
1181
+ /**
1182
+ * Whether gameplay is currently marked active on the CrazyGames side.
1183
+ * Makes gameplayStart/gameplayStop idempotent and lets ad flows restore the
1184
+ * pre-ad state. Focus/tab changes are intentionally NOT tracked — CrazyGames
1185
+ * handles those on their side (per docs.crazygames.com/sdk/game).
1186
+ */
1187
+ gameplayActive = false;
1181
1188
  /**
1182
1189
  * Detects if the game is running on the CrazyGames platform.
1183
1190
  * Games on CrazyGames run inside an iframe, so we check document.referrer
@@ -1252,6 +1259,8 @@ var CrazyGamesService = class {
1252
1259
  completedAt: Date.now()
1253
1260
  };
1254
1261
  }
1262
+ const resumeAfterAd = this.gameplayActive;
1263
+ this.gameplayStop();
1255
1264
  return new Promise((resolve) => {
1256
1265
  sdk.ad.requestAd("midgame", {
1257
1266
  adStarted: () => {
@@ -1259,10 +1268,12 @@ var CrazyGamesService = class {
1259
1268
  },
1260
1269
  adFinished: () => {
1261
1270
  callbacks?.onAfterAd?.();
1271
+ if (resumeAfterAd) this.gameplayStart();
1262
1272
  resolve({ success: true, type: "interstitial", requestedAt, completedAt: Date.now() });
1263
1273
  },
1264
1274
  adError: (error) => {
1265
1275
  callbacks?.onAfterAd?.();
1276
+ if (resumeAfterAd) this.gameplayStart();
1266
1277
  resolve({
1267
1278
  success: false,
1268
1279
  type: "interstitial",
@@ -1290,6 +1301,8 @@ var CrazyGamesService = class {
1290
1301
  completedAt: Date.now()
1291
1302
  };
1292
1303
  }
1304
+ const resumeAfterAd = this.gameplayActive;
1305
+ this.gameplayStop();
1293
1306
  return new Promise((resolve) => {
1294
1307
  sdk.ad.requestAd("rewarded", {
1295
1308
  adStarted: () => {
@@ -1298,10 +1311,12 @@ var CrazyGamesService = class {
1298
1311
  adFinished: () => {
1299
1312
  callbacks?.onRewardEarned?.();
1300
1313
  callbacks?.onAfterAd?.();
1314
+ if (resumeAfterAd) this.gameplayStart();
1301
1315
  resolve({ success: true, type: "rewarded", requestedAt, completedAt: Date.now() });
1302
1316
  },
1303
1317
  adError: (error) => {
1304
1318
  callbacks?.onAfterAd?.();
1319
+ if (resumeAfterAd) this.gameplayStart();
1305
1320
  resolve({
1306
1321
  success: false,
1307
1322
  type: "rewarded",
@@ -1316,17 +1331,22 @@ var CrazyGamesService = class {
1316
1331
  /**
1317
1332
  * Notifies CrazyGames that gameplay has started.
1318
1333
  * Call when the player actively begins or resumes playing.
1334
+ * Idempotent — repeated calls while gameplay is already active are no-ops.
1319
1335
  */
1320
1336
  gameplayStart() {
1321
- if (!this.initialized) return;
1337
+ if (!this.initialized || this.gameplayActive) return;
1338
+ this.gameplayActive = true;
1322
1339
  window.CrazyGames?.SDK.game.gameplayStart();
1323
1340
  }
1324
1341
  /**
1325
1342
  * Notifies CrazyGames that gameplay has stopped.
1326
- * Call during menu access, level completion, or pausing.
1343
+ * Call during menu access, level completion, or pausing. Do NOT call on
1344
+ * focus/tab changes — CrazyGames handles those on their side.
1345
+ * Idempotent — repeated calls while gameplay is already stopped are no-ops.
1327
1346
  */
1328
1347
  gameplayStop() {
1329
- if (!this.initialized) return;
1348
+ if (!this.initialized || !this.gameplayActive) return;
1349
+ this.gameplayActive = false;
1330
1350
  window.CrazyGames?.SDK.game.gameplayStop();
1331
1351
  }
1332
1352
  /**
@@ -3009,6 +3029,8 @@ var HyveClient = class {
3009
3029
  }
3010
3030
  /**
3011
3031
  * Show an ad
3032
+ * On CrazyGames, gameplay tracking is automatically stopped for the duration
3033
+ * of the ad and resumed afterwards if it was active.
3012
3034
  * @param type Type of ad to show ('rewarded', 'interstitial', or 'preroll')
3013
3035
  * @param placement Optional placement key, forwarded to the native AdMob path
3014
3036
  * to resolve a per-placement unit ID. Ignored on the web (H5/Playgama) path.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyve-sdk/js",
3
- "version": "2.14.0-canary.3",
3
+ "version": "2.14.1-canary.0",
4
4
  "description": "Hyve SDK - TypeScript wrapper for Hyve game server integration",
5
5
  "private": false,
6
6
  "publishConfig": {