@oasiz/sdk 1.5.6 → 1.6.1
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 +94 -4
- package/dist/index.cjs +489 -0
- package/dist/index.d.cts +44 -1
- package/dist/index.d.ts +44 -1
- package/dist/index.js +487 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Both talk to the same host bridges (`window.submitScore`, `__oasizLeaveGame`, la
|
|
|
13
13
|
|
|
14
14
|
## HTML5 / TypeScript (`@oasiz/sdk`)
|
|
15
15
|
|
|
16
|
-
Typed SDK for integrating browser games with the Oasiz platform: score, haptics, cross-session state, multiplayer hooks, layout (safe area, leaderboard visibility), navigation (back / leave), and lifecycle events.
|
|
16
|
+
Typed SDK for integrating browser games with the Oasiz platform: score, haptics, cross-session state, multiplayer hooks, layout (safe area, leaderboard visibility), graphics performance, navigation (back / leave), and lifecycle events.
|
|
17
17
|
|
|
18
18
|
### Install
|
|
19
19
|
|
|
@@ -44,13 +44,17 @@ document.documentElement.style.setProperty(
|
|
|
44
44
|
`${oasiz.safeAreaTop}vh`,
|
|
45
45
|
);
|
|
46
46
|
|
|
47
|
-
// 5.
|
|
47
|
+
// 5. Pick graphics settings for this device
|
|
48
|
+
const graphics = oasiz.getGraphicsPerformance();
|
|
49
|
+
renderer.setPixelRatio(graphics.tier === "high" ? 1.5 : graphics.tier === "medium" ? 1.25 : 1);
|
|
50
|
+
|
|
51
|
+
// 6. Submit score when the game ends
|
|
48
52
|
oasiz.submitScore(score);
|
|
49
53
|
|
|
50
|
-
//
|
|
54
|
+
// 7. Optionally hide the leaderboard while a custom overlay is open
|
|
51
55
|
oasiz.setLeaderboardVisible(false);
|
|
52
56
|
|
|
53
|
-
//
|
|
57
|
+
// 8. Optionally surface console logs in-game while debugging
|
|
54
58
|
oasiz.enableLogOverlay({
|
|
55
59
|
enabled: new URLSearchParams(window.location.search).has("oasizLogs"),
|
|
56
60
|
collapsed: true,
|
|
@@ -264,6 +268,40 @@ function closeCustomOverlay(): void {
|
|
|
264
268
|
|
|
265
269
|
Unsupported hosts safely no-op.
|
|
266
270
|
|
|
271
|
+
### Graphics performance
|
|
272
|
+
|
|
273
|
+
#### `oasiz.getGraphicsPerformance(): GraphicsPerformanceMetric`
|
|
274
|
+
|
|
275
|
+
Returns a recommended FPS target and suggested rendering tier:
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
const graphics = oasiz.getGraphicsPerformance();
|
|
279
|
+
|
|
280
|
+
switch (graphics.tier) {
|
|
281
|
+
case "high":
|
|
282
|
+
enablePostProcessing();
|
|
283
|
+
renderer.setPixelRatio(1.5);
|
|
284
|
+
break;
|
|
285
|
+
case "medium":
|
|
286
|
+
renderer.setPixelRatio(1.25);
|
|
287
|
+
break;
|
|
288
|
+
case "low":
|
|
289
|
+
disableHeavyParticles();
|
|
290
|
+
renderer.setPixelRatio(1);
|
|
291
|
+
break;
|
|
292
|
+
case "minimal":
|
|
293
|
+
disableOptionalEffects();
|
|
294
|
+
renderer.setPixelRatio(0.75);
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
The returned object is `{ fps, tier }`, where `fps` is the recommended render target and `tier` is `"minimal"`, `"low"`, `"medium"`, or `"high"`. Hosts can inject measured values with `window.getGraphicsPerformance()` or `window.__OASIZ_GRAPHICS_PERFORMANCE__`; otherwise the SDK estimates from browser, device, and WebGL capability signals.
|
|
300
|
+
|
|
301
|
+
#### `oasiz.graphicsPerformance`
|
|
302
|
+
|
|
303
|
+
Getter alias for `getGraphicsPerformance()`.
|
|
304
|
+
|
|
267
305
|
### Lifecycle
|
|
268
306
|
|
|
269
307
|
The platform dispatches lifecycle events when the app goes to the background or returns to the foreground. Subscribe to pause game loops and audio accordingly.
|
|
@@ -315,6 +353,29 @@ const offBack = oasiz.onBackButton(() => {
|
|
|
315
353
|
offBack();
|
|
316
354
|
```
|
|
317
355
|
|
|
356
|
+
#### `oasiz.enableBackButtonTesting(options?: BackButtonTestingOptions): BackButtonTestingHandle`
|
|
357
|
+
|
|
358
|
+
Opt-in local web helper for testing back override behavior without the Oasiz app bridge. Call it before registering `onBackButton` in local development:
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
if (import.meta.env.DEV) {
|
|
362
|
+
oasiz.enableBackButtonTesting();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const offBack = oasiz.onBackButton(() => {
|
|
366
|
+
closePauseMenuOrOpenIt();
|
|
367
|
+
});
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
While a back listener is active, the helper maps Escape to the same `oasiz:back` event the app sends. By default it also traps one browser-history entry so the browser Back button dispatches `oasiz:back` instead of leaving the page.
|
|
371
|
+
|
|
372
|
+
```ts
|
|
373
|
+
const backTest = oasiz.enableBackButtonTesting({ browserHistory: false });
|
|
374
|
+
testBackButton.onclick = () => backTest.triggerBack();
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
The returned handle also exposes `triggerLeave()` and `destroy()`. This helper is for local/dev web testing; in the app, the real bridge still owns back behavior.
|
|
378
|
+
|
|
318
379
|
#### `oasiz.leaveGame(): void`
|
|
319
380
|
|
|
320
381
|
Programmatically request the host to close the current game (for example, from a Quit button inside your game UI).
|
|
@@ -421,6 +482,8 @@ import {
|
|
|
421
482
|
shareRoomCode,
|
|
422
483
|
openInviteModal,
|
|
423
484
|
enableLogOverlay,
|
|
485
|
+
enableBackButtonTesting,
|
|
486
|
+
getGraphicsPerformance,
|
|
424
487
|
getSafeAreaTop,
|
|
425
488
|
getViewportInsets,
|
|
426
489
|
setLeaderboardVisible,
|
|
@@ -440,7 +503,11 @@ import {
|
|
|
440
503
|
|
|
441
504
|
```ts
|
|
442
505
|
import type {
|
|
506
|
+
BackButtonTestingHandle,
|
|
507
|
+
BackButtonTestingOptions,
|
|
443
508
|
GameState,
|
|
509
|
+
GraphicsPerformanceMetric,
|
|
510
|
+
GraphicsPerformanceTier,
|
|
444
511
|
HapticType,
|
|
445
512
|
LogOverlayEntry,
|
|
446
513
|
LogOverlayHandle,
|
|
@@ -486,6 +553,10 @@ public class GameManager : MonoBehaviour
|
|
|
486
553
|
float safeTopPx = safeTopPct / 100f * Screen.height;
|
|
487
554
|
Debug.Log($"Safe area top: {safeTopPx}px ({safeTopPct}% of height)");
|
|
488
555
|
|
|
556
|
+
// Pick visual settings for the current device
|
|
557
|
+
GraphicsPerformanceMetric graphics = OasizSDK.GetGraphicsPerformance();
|
|
558
|
+
Debug.Log($"Graphics tier: {graphics.tier} ({graphics.fps} FPS target)");
|
|
559
|
+
|
|
489
560
|
// Emit score normalization anchors
|
|
490
561
|
OasizSDK.EmitScoreConfig(new ScoreConfig(
|
|
491
562
|
new ScoreAnchor(10, 100),
|
|
@@ -530,8 +601,10 @@ public class GameManager : MonoBehaviour
|
|
|
530
601
|
| `oasiz.getViewportInsets()` / `viewportInsets` | `OasizSDK.GetViewportInsets()` (`ViewportInsets`, each side 0–100, % of matching viewport axis) |
|
|
531
602
|
| `oasiz.getSafeAreaTop()` / `safeAreaTop` | `OasizSDK.GetSafeAreaTop()` / `OasizSDK.SafeAreaTop` (`float`, 0–100, % of viewport height; legacy alias for top viewport inset) |
|
|
532
603
|
| `oasiz.setLeaderboardVisible(v)` | `OasizSDK.SetLeaderboardVisible(bool)` |
|
|
604
|
+
| `oasiz.getGraphicsPerformance()` / `graphicsPerformance` | `OasizSDK.GetGraphicsPerformance()` / `OasizSDK.GraphicsPerformance` (`GraphicsPerformanceMetric`, recommended FPS plus `minimal` / `low` / `medium` / `high`) |
|
|
533
605
|
| `oasiz.onPause` / `onResume` | `OasizSDK.OnPause` / `OnResume` static events |
|
|
534
606
|
| `oasiz.onBackButton` | `OasizSDK.OnBackButton` or `SubscribeBackButton(Action)` (reference-counts `__oasizSetBackOverride`) |
|
|
607
|
+
| `oasiz.enableBackButtonTesting()` | `OasizSDK.EnableBackButtonTesting()` (WebGL local/dev helper for Escape/browser Back testing) |
|
|
535
608
|
| `oasiz.onLeaveGame` | `OasizSDK.OnLeaveGame` |
|
|
536
609
|
| `oasiz.leaveGame()` | `OasizSDK.LeaveGame()` |
|
|
537
610
|
| `oasiz.share(request)` | `OasizSDK.Share(ShareRequest)` |
|
|
@@ -542,6 +615,23 @@ public class GameManager : MonoBehaviour
|
|
|
542
615
|
| `oasiz.enableLogOverlay` | `OasizSDK.EnableLogOverlay(LogOverlayOptions)` (see note below) |
|
|
543
616
|
| -- | `OasizSDK.AppendLogOverlay(level, message, stackTrace)` (see note below) |
|
|
544
617
|
|
|
618
|
+
### Local back-button testing (Unity WebGL)
|
|
619
|
+
|
|
620
|
+
For local WebGL builds outside the Oasiz app, install the dev bridge before testing your subscribed back handler:
|
|
621
|
+
|
|
622
|
+
```csharp
|
|
623
|
+
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
|
624
|
+
OasizSDK.EnableBackButtonTesting();
|
|
625
|
+
#endif
|
|
626
|
+
|
|
627
|
+
Action unsubscribeBack = OasizSDK.SubscribeBackButton(() =>
|
|
628
|
+
{
|
|
629
|
+
TogglePauseMenu();
|
|
630
|
+
});
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
While the override is active, Escape and the browser Back button dispatch the same `oasiz:back` event that the app bridge sends. You can disable either input with `OasizSDK.EnableBackButtonTesting(keyboard: false)` or `OasizSDK.EnableBackButtonTesting(browserHistory: false)`.
|
|
634
|
+
|
|
545
635
|
### Share (Unity)
|
|
546
636
|
|
|
547
637
|
HTML5 **`oasiz.share`** returns a **Promise** you can `await`. Unity **`OasizSDK.Share(ShareRequest)`** returns **`void`**: C# validation throws **`ArgumentException`** with the same rules as TypeScript (at least one of text, score, or image; non-negative integer score; `http(s)` or `data:image/...;base64,...` image). The call forwards JSON to **`window.__oasizShareRequest`**. If the host promise rejects, the **WebGL `.jslib` logs the error** to the browser console.
|
package/dist/index.cjs
CHANGED
|
@@ -21,9 +21,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
addScore: () => addScore,
|
|
24
|
+
enableBackButtonTesting: () => enableBackButtonTesting,
|
|
24
25
|
enableLogOverlay: () => enableLogOverlay,
|
|
25
26
|
flushGameState: () => flushGameState,
|
|
26
27
|
getGameId: () => getGameId,
|
|
28
|
+
getGraphicsPerformance: () => getGraphicsPerformance,
|
|
27
29
|
getPlayerAvatar: () => getPlayerAvatar,
|
|
28
30
|
getPlayerCharacter: () => getPlayerCharacter,
|
|
29
31
|
getPlayerId: () => getPlayerId,
|
|
@@ -1628,7 +1630,9 @@ function setLeaderboardVisible(visible) {
|
|
|
1628
1630
|
}
|
|
1629
1631
|
|
|
1630
1632
|
// src/navigation.ts
|
|
1633
|
+
var BACK_BUTTON_TEST_STATE_KEY = "__oasizBackButtonTest";
|
|
1631
1634
|
var activeBackListeners = 0;
|
|
1635
|
+
var activeBackButtonTestingHandle;
|
|
1632
1636
|
function isDevelopment10() {
|
|
1633
1637
|
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
1634
1638
|
return nodeEnv !== "production";
|
|
@@ -1654,6 +1658,16 @@ function normalizeNavigationError(error) {
|
|
|
1654
1658
|
typeof error === "string" ? error : "Back button callback failed."
|
|
1655
1659
|
);
|
|
1656
1660
|
}
|
|
1661
|
+
function isRecord2(value) {
|
|
1662
|
+
return typeof value === "object" && value !== null;
|
|
1663
|
+
}
|
|
1664
|
+
function dispatchNavigationEvent(eventName) {
|
|
1665
|
+
const bridge = getBridgeWindow9();
|
|
1666
|
+
if (!bridge || typeof bridge.dispatchEvent !== "function") {
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
bridge.dispatchEvent(new Event(eventName));
|
|
1670
|
+
}
|
|
1657
1671
|
function addNavigationListener(eventName, callback) {
|
|
1658
1672
|
if (typeof window === "undefined") {
|
|
1659
1673
|
if (isDevelopment10()) {
|
|
@@ -1668,6 +1682,151 @@ function addNavigationListener(eventName, callback) {
|
|
|
1668
1682
|
window.addEventListener(eventName, handler);
|
|
1669
1683
|
return () => window.removeEventListener(eventName, handler);
|
|
1670
1684
|
}
|
|
1685
|
+
function enableBackButtonTesting(options = {}) {
|
|
1686
|
+
activeBackButtonTestingHandle?.destroy();
|
|
1687
|
+
const bridge = getBridgeWindow9();
|
|
1688
|
+
if (!bridge) {
|
|
1689
|
+
if (isDevelopment10()) {
|
|
1690
|
+
console.warn(
|
|
1691
|
+
"[oasiz/sdk] enableBackButtonTesting requires a browser window."
|
|
1692
|
+
);
|
|
1693
|
+
}
|
|
1694
|
+
return {
|
|
1695
|
+
destroy: () => {
|
|
1696
|
+
},
|
|
1697
|
+
isBackOverrideActive: () => false,
|
|
1698
|
+
triggerBack: () => {
|
|
1699
|
+
},
|
|
1700
|
+
triggerLeave: () => {
|
|
1701
|
+
}
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
const bridgeWindow = bridge;
|
|
1705
|
+
const keyboard = options.keyboard ?? true;
|
|
1706
|
+
const browserHistory = options.browserHistory ?? true;
|
|
1707
|
+
const log = options.log === true;
|
|
1708
|
+
const previousSetBackOverride = bridgeWindow.__oasizSetBackOverride;
|
|
1709
|
+
const previousLeaveGame = bridgeWindow.__oasizLeaveGame;
|
|
1710
|
+
let destroyed = false;
|
|
1711
|
+
let backOverrideActive = false;
|
|
1712
|
+
let historyTrapArmed = false;
|
|
1713
|
+
function maybeLog(message) {
|
|
1714
|
+
if (log) {
|
|
1715
|
+
console.info("[oasiz/sdk] " + message);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
function canUseHistoryTrap() {
|
|
1719
|
+
return browserHistory && typeof bridgeWindow.history?.pushState === "function" && typeof bridgeWindow.history?.replaceState === "function" && typeof bridgeWindow.location?.href === "string";
|
|
1720
|
+
}
|
|
1721
|
+
function ensureHistoryTrap() {
|
|
1722
|
+
if (!backOverrideActive || historyTrapArmed || !canUseHistoryTrap()) {
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
try {
|
|
1726
|
+
const currentState = isRecord2(bridgeWindow.history.state) ? bridgeWindow.history.state : {};
|
|
1727
|
+
bridgeWindow.history.replaceState(
|
|
1728
|
+
{ ...currentState, [BACK_BUTTON_TEST_STATE_KEY]: "base" },
|
|
1729
|
+
"",
|
|
1730
|
+
bridgeWindow.location.href
|
|
1731
|
+
);
|
|
1732
|
+
bridgeWindow.history.pushState(
|
|
1733
|
+
{ [BACK_BUTTON_TEST_STATE_KEY]: "trap" },
|
|
1734
|
+
"",
|
|
1735
|
+
bridgeWindow.location.href
|
|
1736
|
+
);
|
|
1737
|
+
historyTrapArmed = true;
|
|
1738
|
+
maybeLog("Local browser Back testing is armed.");
|
|
1739
|
+
} catch (error) {
|
|
1740
|
+
historyTrapArmed = false;
|
|
1741
|
+
if (log) {
|
|
1742
|
+
console.warn("[oasiz/sdk] Failed to arm browser Back testing:", error);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
function triggerBack() {
|
|
1747
|
+
dispatchNavigationEvent("oasiz:back");
|
|
1748
|
+
}
|
|
1749
|
+
function triggerLeave() {
|
|
1750
|
+
dispatchNavigationEvent("oasiz:leave");
|
|
1751
|
+
}
|
|
1752
|
+
function setBackOverride(active) {
|
|
1753
|
+
backOverrideActive = active;
|
|
1754
|
+
if (active) {
|
|
1755
|
+
ensureHistoryTrap();
|
|
1756
|
+
}
|
|
1757
|
+
if (typeof previousSetBackOverride === "function") {
|
|
1758
|
+
previousSetBackOverride(active);
|
|
1759
|
+
}
|
|
1760
|
+
maybeLog("Back override " + (active ? "enabled" : "disabled") + ".");
|
|
1761
|
+
}
|
|
1762
|
+
function stopBackEvent(event) {
|
|
1763
|
+
event.preventDefault();
|
|
1764
|
+
event.stopPropagation();
|
|
1765
|
+
event.stopImmediatePropagation?.();
|
|
1766
|
+
}
|
|
1767
|
+
const handleKeyDown = (event) => {
|
|
1768
|
+
if (!backOverrideActive || event.key !== "Escape") {
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1771
|
+
stopBackEvent(event);
|
|
1772
|
+
triggerBack();
|
|
1773
|
+
};
|
|
1774
|
+
const handlePopState = (event) => {
|
|
1775
|
+
if (!backOverrideActive) {
|
|
1776
|
+
historyTrapArmed = false;
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
stopBackEvent(event);
|
|
1780
|
+
triggerBack();
|
|
1781
|
+
historyTrapArmed = false;
|
|
1782
|
+
ensureHistoryTrap();
|
|
1783
|
+
};
|
|
1784
|
+
const testLeaveGame = () => {
|
|
1785
|
+
triggerLeave();
|
|
1786
|
+
if (typeof previousLeaveGame === "function") {
|
|
1787
|
+
previousLeaveGame();
|
|
1788
|
+
}
|
|
1789
|
+
};
|
|
1790
|
+
bridgeWindow.__oasizSetBackOverride = setBackOverride;
|
|
1791
|
+
bridgeWindow.__oasizLeaveGame = testLeaveGame;
|
|
1792
|
+
if (keyboard) {
|
|
1793
|
+
bridgeWindow.addEventListener("keydown", handleKeyDown);
|
|
1794
|
+
}
|
|
1795
|
+
if (browserHistory) {
|
|
1796
|
+
bridgeWindow.addEventListener("popstate", handlePopState);
|
|
1797
|
+
}
|
|
1798
|
+
if (activeBackListeners > 0) {
|
|
1799
|
+
setBackOverride(true);
|
|
1800
|
+
}
|
|
1801
|
+
maybeLog("Back button testing bridge installed.");
|
|
1802
|
+
const handle = {
|
|
1803
|
+
destroy: () => {
|
|
1804
|
+
if (destroyed) return;
|
|
1805
|
+
destroyed = true;
|
|
1806
|
+
if (keyboard) {
|
|
1807
|
+
bridgeWindow.removeEventListener("keydown", handleKeyDown);
|
|
1808
|
+
}
|
|
1809
|
+
if (browserHistory) {
|
|
1810
|
+
bridgeWindow.removeEventListener("popstate", handlePopState);
|
|
1811
|
+
}
|
|
1812
|
+
if (bridgeWindow.__oasizSetBackOverride === setBackOverride) {
|
|
1813
|
+
bridgeWindow.__oasizSetBackOverride = previousSetBackOverride;
|
|
1814
|
+
}
|
|
1815
|
+
if (bridgeWindow.__oasizLeaveGame === testLeaveGame) {
|
|
1816
|
+
bridgeWindow.__oasizLeaveGame = previousLeaveGame;
|
|
1817
|
+
}
|
|
1818
|
+
if (activeBackButtonTestingHandle === handle) {
|
|
1819
|
+
activeBackButtonTestingHandle = void 0;
|
|
1820
|
+
}
|
|
1821
|
+
maybeLog("Back button testing bridge removed.");
|
|
1822
|
+
},
|
|
1823
|
+
isBackOverrideActive: () => backOverrideActive,
|
|
1824
|
+
triggerBack,
|
|
1825
|
+
triggerLeave
|
|
1826
|
+
};
|
|
1827
|
+
activeBackButtonTestingHandle = handle;
|
|
1828
|
+
return handle;
|
|
1829
|
+
}
|
|
1671
1830
|
function onBackButton(callback) {
|
|
1672
1831
|
const off = addNavigationListener("oasiz:back", () => {
|
|
1673
1832
|
try {
|
|
@@ -1711,6 +1870,329 @@ function leaveGame() {
|
|
|
1711
1870
|
warnMissingBridge7("__oasizLeaveGame");
|
|
1712
1871
|
}
|
|
1713
1872
|
|
|
1873
|
+
// src/performance.ts
|
|
1874
|
+
var DEFAULT_GRAPHICS_PERFORMANCE = {
|
|
1875
|
+
fps: 45,
|
|
1876
|
+
tier: "medium"
|
|
1877
|
+
};
|
|
1878
|
+
var TIER_DEFAULT_FPS = {
|
|
1879
|
+
minimal: 24,
|
|
1880
|
+
low: 30,
|
|
1881
|
+
medium: 45,
|
|
1882
|
+
high: 60
|
|
1883
|
+
};
|
|
1884
|
+
var cachedEstimatedGraphicsPerformance;
|
|
1885
|
+
function getBridgeWindow10() {
|
|
1886
|
+
if (typeof window === "undefined") {
|
|
1887
|
+
return void 0;
|
|
1888
|
+
}
|
|
1889
|
+
return window;
|
|
1890
|
+
}
|
|
1891
|
+
function isRecord3(value) {
|
|
1892
|
+
return typeof value === "object" && value !== null;
|
|
1893
|
+
}
|
|
1894
|
+
function toFiniteNumber2(value) {
|
|
1895
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1896
|
+
return value;
|
|
1897
|
+
}
|
|
1898
|
+
if (typeof value === "string") {
|
|
1899
|
+
const parsed = Number.parseFloat(value.trim());
|
|
1900
|
+
if (Number.isFinite(parsed)) {
|
|
1901
|
+
return parsed;
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
return void 0;
|
|
1905
|
+
}
|
|
1906
|
+
function clampScore(value) {
|
|
1907
|
+
return Math.min(100, Math.max(0, Math.round(value)));
|
|
1908
|
+
}
|
|
1909
|
+
function clampFps(value) {
|
|
1910
|
+
return Math.min(240, Math.max(1, Math.round(value)));
|
|
1911
|
+
}
|
|
1912
|
+
function tierFromScore(score) {
|
|
1913
|
+
if (score < 25) return "minimal";
|
|
1914
|
+
if (score < 40) return "low";
|
|
1915
|
+
if (score < 70) return "medium";
|
|
1916
|
+
return "high";
|
|
1917
|
+
}
|
|
1918
|
+
function tierFromFps(fps) {
|
|
1919
|
+
if (fps < 30) return "minimal";
|
|
1920
|
+
if (fps < 45) return "low";
|
|
1921
|
+
if (fps < 58) return "medium";
|
|
1922
|
+
return "high";
|
|
1923
|
+
}
|
|
1924
|
+
function fpsFromScore(score) {
|
|
1925
|
+
return TIER_DEFAULT_FPS[tierFromScore(score)];
|
|
1926
|
+
}
|
|
1927
|
+
function normalizeTier(value) {
|
|
1928
|
+
if (typeof value !== "string") {
|
|
1929
|
+
return void 0;
|
|
1930
|
+
}
|
|
1931
|
+
const normalized = value.trim().toLowerCase();
|
|
1932
|
+
if (normalized === "minimal" || normalized === "safe") return "minimal";
|
|
1933
|
+
if (normalized === "high") return "high";
|
|
1934
|
+
if (normalized === "medium") return "medium";
|
|
1935
|
+
if (normalized === "low") return "low";
|
|
1936
|
+
return void 0;
|
|
1937
|
+
}
|
|
1938
|
+
function firstDefined2(...values) {
|
|
1939
|
+
return values.find((value) => typeof value !== "undefined");
|
|
1940
|
+
}
|
|
1941
|
+
function normalizeMetric(value) {
|
|
1942
|
+
if (typeof value === "undefined" || value === null) {
|
|
1943
|
+
return void 0;
|
|
1944
|
+
}
|
|
1945
|
+
if (typeof value === "number" || typeof value === "string") {
|
|
1946
|
+
const numeric = toFiniteNumber2(value);
|
|
1947
|
+
if (typeof numeric !== "undefined") {
|
|
1948
|
+
const fps2 = clampFps(numeric);
|
|
1949
|
+
return { fps: fps2, tier: tierFromFps(fps2) };
|
|
1950
|
+
}
|
|
1951
|
+
const tier2 = normalizeTier(value);
|
|
1952
|
+
if (tier2) {
|
|
1953
|
+
return { fps: TIER_DEFAULT_FPS[tier2], tier: tier2 };
|
|
1954
|
+
}
|
|
1955
|
+
return void 0;
|
|
1956
|
+
}
|
|
1957
|
+
if (!isRecord3(value)) {
|
|
1958
|
+
return void 0;
|
|
1959
|
+
}
|
|
1960
|
+
const fpsCandidate = firstDefined2(
|
|
1961
|
+
value.fps,
|
|
1962
|
+
value.targetFps,
|
|
1963
|
+
value.frameRate,
|
|
1964
|
+
value.framesPerSecond
|
|
1965
|
+
);
|
|
1966
|
+
const legacyScoreCandidate = firstDefined2(
|
|
1967
|
+
value.score,
|
|
1968
|
+
value.metric,
|
|
1969
|
+
value.value,
|
|
1970
|
+
value.performance,
|
|
1971
|
+
value.performanceScore,
|
|
1972
|
+
value.graphicsScore
|
|
1973
|
+
);
|
|
1974
|
+
const tierCandidate = firstDefined2(
|
|
1975
|
+
value.tier,
|
|
1976
|
+
value.graphicsTier,
|
|
1977
|
+
value.performanceTier,
|
|
1978
|
+
value.qualityTier,
|
|
1979
|
+
value.recommendedTier
|
|
1980
|
+
);
|
|
1981
|
+
const tier = normalizeTier(tierCandidate);
|
|
1982
|
+
const fpsNumeric = toFiniteNumber2(fpsCandidate);
|
|
1983
|
+
const scoreNumeric = toFiniteNumber2(legacyScoreCandidate);
|
|
1984
|
+
if (typeof fpsNumeric === "undefined" && typeof scoreNumeric === "undefined" && !tier) {
|
|
1985
|
+
return void 0;
|
|
1986
|
+
}
|
|
1987
|
+
const fps = clampFps(
|
|
1988
|
+
typeof fpsNumeric !== "undefined" ? fpsNumeric : typeof scoreNumeric !== "undefined" ? fpsFromScore(clampScore(scoreNumeric)) : TIER_DEFAULT_FPS[tier]
|
|
1989
|
+
);
|
|
1990
|
+
return {
|
|
1991
|
+
fps,
|
|
1992
|
+
tier: tier ?? tierFromFps(fps)
|
|
1993
|
+
};
|
|
1994
|
+
}
|
|
1995
|
+
function callBridgeMetric(bridge, name) {
|
|
1996
|
+
const fn = bridge[name];
|
|
1997
|
+
if (typeof fn !== "function") {
|
|
1998
|
+
return void 0;
|
|
1999
|
+
}
|
|
2000
|
+
try {
|
|
2001
|
+
return fn.call(bridge);
|
|
2002
|
+
} catch (error) {
|
|
2003
|
+
console.error("[oasiz/sdk] " + String(name) + " failed:", error);
|
|
2004
|
+
return void 0;
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
function readHostGraphicsPerformance(bridge) {
|
|
2008
|
+
const candidates = [
|
|
2009
|
+
callBridgeMetric(bridge, "getGraphicsPerformance"),
|
|
2010
|
+
callBridgeMetric(bridge, "getGraphicsPerformanceMetric"),
|
|
2011
|
+
callBridgeMetric(bridge, "getPerformanceMetric"),
|
|
2012
|
+
bridge.__OASIZ_GRAPHICS_PERFORMANCE__,
|
|
2013
|
+
bridge.__OASIZ_PERFORMANCE_METRIC__
|
|
2014
|
+
];
|
|
2015
|
+
for (const candidate of candidates) {
|
|
2016
|
+
const metric = normalizeMetric(candidate);
|
|
2017
|
+
if (metric) {
|
|
2018
|
+
return metric;
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
return void 0;
|
|
2022
|
+
}
|
|
2023
|
+
function getDevicePixelRatio2(bridge) {
|
|
2024
|
+
const dpr = bridge.devicePixelRatio;
|
|
2025
|
+
return typeof dpr === "number" && Number.isFinite(dpr) && dpr > 0 ? dpr : 1;
|
|
2026
|
+
}
|
|
2027
|
+
function getNavigatorValue(bridge) {
|
|
2028
|
+
return bridge.navigator;
|
|
2029
|
+
}
|
|
2030
|
+
function parseIosMajorVersion(userAgent) {
|
|
2031
|
+
const match = /\bOS (\d+)_/i.exec(userAgent);
|
|
2032
|
+
if (!match) {
|
|
2033
|
+
return void 0;
|
|
2034
|
+
}
|
|
2035
|
+
const parsed = Number.parseInt(match[1] ?? "", 10);
|
|
2036
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
2037
|
+
}
|
|
2038
|
+
function isAppleMobileDevice(bridge) {
|
|
2039
|
+
const userAgent = bridge.navigator?.userAgent ?? "";
|
|
2040
|
+
return /iP(hone|ad|od)/i.test(userAgent) || /Macintosh/i.test(userAgent) && (bridge.navigator?.maxTouchPoints ?? 0) > 1;
|
|
2041
|
+
}
|
|
2042
|
+
function getLongestScreenEdge(bridge) {
|
|
2043
|
+
const screenWidth = bridge.screen?.width;
|
|
2044
|
+
const screenHeight = bridge.screen?.height;
|
|
2045
|
+
const innerWidth = bridge.innerWidth;
|
|
2046
|
+
const innerHeight = bridge.innerHeight;
|
|
2047
|
+
return Math.max(
|
|
2048
|
+
typeof screenWidth === "number" ? screenWidth : 0,
|
|
2049
|
+
typeof screenHeight === "number" ? screenHeight : 0,
|
|
2050
|
+
typeof innerWidth === "number" ? innerWidth : 0,
|
|
2051
|
+
typeof innerHeight === "number" ? innerHeight : 0
|
|
2052
|
+
);
|
|
2053
|
+
}
|
|
2054
|
+
function isCoarsePointer(bridge) {
|
|
2055
|
+
try {
|
|
2056
|
+
return bridge.matchMedia?.("(pointer: coarse)").matches === true;
|
|
2057
|
+
} catch {
|
|
2058
|
+
return false;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
function getWebGlInfo(bridge) {
|
|
2062
|
+
const canvas = bridge.document?.createElement?.("canvas");
|
|
2063
|
+
if (!canvas || typeof canvas.getContext !== "function") {
|
|
2064
|
+
return { webglVersion: 0 };
|
|
2065
|
+
}
|
|
2066
|
+
const gl2 = canvas.getContext("webgl2");
|
|
2067
|
+
const gl = gl2 ?? canvas.getContext("webgl") ?? canvas.getContext("experimental-webgl");
|
|
2068
|
+
if (!gl) {
|
|
2069
|
+
return { webglVersion: 0 };
|
|
2070
|
+
}
|
|
2071
|
+
const context = gl;
|
|
2072
|
+
let maxTextureSize;
|
|
2073
|
+
let renderer;
|
|
2074
|
+
try {
|
|
2075
|
+
const value = context.getParameter(context.MAX_TEXTURE_SIZE);
|
|
2076
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2077
|
+
maxTextureSize = value;
|
|
2078
|
+
}
|
|
2079
|
+
} catch {
|
|
2080
|
+
}
|
|
2081
|
+
try {
|
|
2082
|
+
const debugInfo = context.getExtension("WEBGL_debug_renderer_info");
|
|
2083
|
+
const rendererParam = debugInfo?.UNMASKED_RENDERER_WEBGL ?? context.RENDERER;
|
|
2084
|
+
const value = context.getParameter(rendererParam);
|
|
2085
|
+
if (typeof value === "string") {
|
|
2086
|
+
renderer = value;
|
|
2087
|
+
}
|
|
2088
|
+
} catch {
|
|
2089
|
+
}
|
|
2090
|
+
return {
|
|
2091
|
+
maxTextureSize,
|
|
2092
|
+
renderer,
|
|
2093
|
+
webglVersion: gl2 ? 2 : 1
|
|
2094
|
+
};
|
|
2095
|
+
}
|
|
2096
|
+
function applyMemoryScore(score, memory) {
|
|
2097
|
+
if (typeof memory !== "number" || !Number.isFinite(memory) || memory <= 0) {
|
|
2098
|
+
return score;
|
|
2099
|
+
}
|
|
2100
|
+
if (memory <= 1) return score - 18;
|
|
2101
|
+
if (memory <= 2) return score - 10;
|
|
2102
|
+
if (memory >= 8) return score + 16;
|
|
2103
|
+
if (memory >= 6) return score + 10;
|
|
2104
|
+
return score;
|
|
2105
|
+
}
|
|
2106
|
+
function applyCoreScore(score, cores) {
|
|
2107
|
+
if (typeof cores !== "number" || !Number.isFinite(cores) || cores <= 0) {
|
|
2108
|
+
return score;
|
|
2109
|
+
}
|
|
2110
|
+
if (cores <= 2) return score - 12;
|
|
2111
|
+
if (cores <= 4) return score;
|
|
2112
|
+
if (cores >= 8) return score + 12;
|
|
2113
|
+
return score + 8;
|
|
2114
|
+
}
|
|
2115
|
+
function applyWebGlScore(score, info) {
|
|
2116
|
+
let next = score;
|
|
2117
|
+
if (info.webglVersion === 2) {
|
|
2118
|
+
next += 16;
|
|
2119
|
+
} else if (info.webglVersion === 1) {
|
|
2120
|
+
next += 6;
|
|
2121
|
+
} else {
|
|
2122
|
+
next -= 25;
|
|
2123
|
+
}
|
|
2124
|
+
if (typeof info.maxTextureSize === "number") {
|
|
2125
|
+
if (info.maxTextureSize >= 8192) next += 10;
|
|
2126
|
+
else if (info.maxTextureSize >= 4096) next += 3;
|
|
2127
|
+
else next -= 8;
|
|
2128
|
+
}
|
|
2129
|
+
const renderer = info.renderer?.toLowerCase() ?? "";
|
|
2130
|
+
if (/swiftshader|llvmpipe|software/.test(renderer)) {
|
|
2131
|
+
next = Math.min(next, 35);
|
|
2132
|
+
} else if (/\bm[1-9]\b|apple gpu|a1[6-9]|rtx|radeon|adreno 7|adreno 8/.test(renderer)) {
|
|
2133
|
+
next += 8;
|
|
2134
|
+
}
|
|
2135
|
+
return next;
|
|
2136
|
+
}
|
|
2137
|
+
function applyMobileCostScore(score, bridge) {
|
|
2138
|
+
if (!isCoarsePointer(bridge)) {
|
|
2139
|
+
return score;
|
|
2140
|
+
}
|
|
2141
|
+
const dpr = getDevicePixelRatio2(bridge);
|
|
2142
|
+
const width = bridge.screen?.width ?? bridge.innerWidth ?? 0;
|
|
2143
|
+
const height = bridge.screen?.height ?? bridge.innerHeight ?? 0;
|
|
2144
|
+
const physicalPixels = width * height * dpr * dpr;
|
|
2145
|
+
if (physicalPixels > 4e6) {
|
|
2146
|
+
return score - 5;
|
|
2147
|
+
}
|
|
2148
|
+
return score;
|
|
2149
|
+
}
|
|
2150
|
+
function applyAppleMobileRules(score, bridge) {
|
|
2151
|
+
if (!isAppleMobileDevice(bridge)) {
|
|
2152
|
+
return score;
|
|
2153
|
+
}
|
|
2154
|
+
const userAgent = bridge.navigator?.userAgent ?? "";
|
|
2155
|
+
const iosMajorVersion = parseIosMajorVersion(userAgent);
|
|
2156
|
+
const highDensityDisplay = getDevicePixelRatio2(bridge) >= 3;
|
|
2157
|
+
const longestScreenEdge = getLongestScreenEdge(bridge);
|
|
2158
|
+
if (typeof iosMajorVersion === "number" && iosMajorVersion < 15) {
|
|
2159
|
+
return Math.min(score, 35);
|
|
2160
|
+
}
|
|
2161
|
+
if (highDensityDisplay && longestScreenEdge >= 430 && (typeof iosMajorVersion === "undefined" || iosMajorVersion >= 17)) {
|
|
2162
|
+
return Math.max(score, 78);
|
|
2163
|
+
}
|
|
2164
|
+
if (highDensityDisplay && longestScreenEdge >= 414 && (typeof iosMajorVersion === "undefined" || iosMajorVersion >= 16)) {
|
|
2165
|
+
return Math.max(score, 62);
|
|
2166
|
+
}
|
|
2167
|
+
return Math.min(score, 48);
|
|
2168
|
+
}
|
|
2169
|
+
function estimateGraphicsPerformance(bridge) {
|
|
2170
|
+
const navigatorValue = getNavigatorValue(bridge);
|
|
2171
|
+
let score = 45;
|
|
2172
|
+
score = applyMemoryScore(score, navigatorValue?.deviceMemory);
|
|
2173
|
+
score = applyCoreScore(score, navigatorValue?.hardwareConcurrency);
|
|
2174
|
+
score = applyWebGlScore(score, getWebGlInfo(bridge));
|
|
2175
|
+
score = applyMobileCostScore(score, bridge);
|
|
2176
|
+
score = applyAppleMobileRules(score, bridge);
|
|
2177
|
+
const normalizedScore = clampScore(score);
|
|
2178
|
+
return {
|
|
2179
|
+
fps: fpsFromScore(normalizedScore),
|
|
2180
|
+
tier: tierFromScore(normalizedScore)
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2183
|
+
function getGraphicsPerformance() {
|
|
2184
|
+
const bridge = getBridgeWindow10();
|
|
2185
|
+
if (!bridge) {
|
|
2186
|
+
return { ...DEFAULT_GRAPHICS_PERFORMANCE };
|
|
2187
|
+
}
|
|
2188
|
+
const hostMetric = readHostGraphicsPerformance(bridge);
|
|
2189
|
+
if (hostMetric) {
|
|
2190
|
+
return hostMetric;
|
|
2191
|
+
}
|
|
2192
|
+
cachedEstimatedGraphicsPerformance ??= estimateGraphicsPerformance(bridge);
|
|
2193
|
+
return { ...cachedEstimatedGraphicsPerformance };
|
|
2194
|
+
}
|
|
2195
|
+
|
|
1714
2196
|
// src/index.ts
|
|
1715
2197
|
var oasiz = {
|
|
1716
2198
|
submitScore,
|
|
@@ -1730,6 +2212,8 @@ var oasiz = {
|
|
|
1730
2212
|
getSafeAreaTop,
|
|
1731
2213
|
getViewportInsets,
|
|
1732
2214
|
setLeaderboardVisible,
|
|
2215
|
+
getGraphicsPerformance,
|
|
2216
|
+
enableBackButtonTesting,
|
|
1733
2217
|
onBackButton,
|
|
1734
2218
|
onLeaveGame,
|
|
1735
2219
|
leaveGame,
|
|
@@ -1753,14 +2237,19 @@ var oasiz = {
|
|
|
1753
2237
|
},
|
|
1754
2238
|
get viewportInsets() {
|
|
1755
2239
|
return getViewportInsets();
|
|
2240
|
+
},
|
|
2241
|
+
get graphicsPerformance() {
|
|
2242
|
+
return getGraphicsPerformance();
|
|
1756
2243
|
}
|
|
1757
2244
|
};
|
|
1758
2245
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1759
2246
|
0 && (module.exports = {
|
|
1760
2247
|
addScore,
|
|
2248
|
+
enableBackButtonTesting,
|
|
1761
2249
|
enableLogOverlay,
|
|
1762
2250
|
flushGameState,
|
|
1763
2251
|
getGameId,
|
|
2252
|
+
getGraphicsPerformance,
|
|
1764
2253
|
getPlayerAvatar,
|
|
1765
2254
|
getPlayerCharacter,
|
|
1766
2255
|
getPlayerId,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
type GraphicsPerformanceTier = "minimal" | "low" | "medium" | "high";
|
|
2
|
+
interface GraphicsPerformanceMetric {
|
|
3
|
+
/** Recommended graphics/rendering frame-rate target for the current device. */
|
|
4
|
+
fps: number;
|
|
5
|
+
/** Suggested graphics/rendering tier for the current device. */
|
|
6
|
+
tier: GraphicsPerformanceTier;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Return the current device's recommended graphics performance profile.
|
|
10
|
+
*
|
|
11
|
+
* Hosts can provide an FPS/tier recommendation through
|
|
12
|
+
* `window.getGraphicsPerformance()` or `window.__OASIZ_GRAPHICS_PERFORMANCE__`.
|
|
13
|
+
* When no host value exists, the SDK estimates from browser/device/WebGL
|
|
14
|
+
* capability signals so games still get a usable recommendation in local
|
|
15
|
+
* development and unsupported hosts.
|
|
16
|
+
*/
|
|
17
|
+
declare function getGraphicsPerformance(): GraphicsPerformanceMetric;
|
|
18
|
+
|
|
1
19
|
type ViewportInsetSide = "top" | "right" | "bottom" | "left";
|
|
2
20
|
interface ViewportInsetEdges {
|
|
3
21
|
top: number;
|
|
@@ -221,6 +239,28 @@ type Unsubscribe = () => void;
|
|
|
221
239
|
declare function onPause(callback: () => void): Unsubscribe;
|
|
222
240
|
declare function onResume(callback: () => void): Unsubscribe;
|
|
223
241
|
|
|
242
|
+
interface BackButtonTestingOptions {
|
|
243
|
+
/**
|
|
244
|
+
* When true, pressing Escape dispatches the same event the host app sends for
|
|
245
|
+
* back actions. Defaults to true.
|
|
246
|
+
*/
|
|
247
|
+
keyboard?: boolean;
|
|
248
|
+
/**
|
|
249
|
+
* When true, the SDK traps one browser-history entry while back override is
|
|
250
|
+
* active, so the browser Back button can be used for local testing. Defaults
|
|
251
|
+
* to true.
|
|
252
|
+
*/
|
|
253
|
+
browserHistory?: boolean;
|
|
254
|
+
/** Log setup details to the console. Defaults to false. */
|
|
255
|
+
log?: boolean;
|
|
256
|
+
}
|
|
257
|
+
interface BackButtonTestingHandle {
|
|
258
|
+
destroy: () => void;
|
|
259
|
+
isBackOverrideActive: () => boolean;
|
|
260
|
+
triggerBack: () => void;
|
|
261
|
+
triggerLeave: () => void;
|
|
262
|
+
}
|
|
263
|
+
declare function enableBackButtonTesting(options?: BackButtonTestingOptions): BackButtonTestingHandle;
|
|
224
264
|
declare function onBackButton(callback: () => void): Unsubscribe;
|
|
225
265
|
declare function onLeaveGame(callback: () => void): Unsubscribe;
|
|
226
266
|
declare function leaveGame(): void;
|
|
@@ -243,6 +283,8 @@ declare const oasiz: {
|
|
|
243
283
|
getSafeAreaTop: typeof getSafeAreaTop;
|
|
244
284
|
getViewportInsets: typeof getViewportInsets;
|
|
245
285
|
setLeaderboardVisible: typeof setLeaderboardVisible;
|
|
286
|
+
getGraphicsPerformance: typeof getGraphicsPerformance;
|
|
287
|
+
enableBackButtonTesting: typeof enableBackButtonTesting;
|
|
246
288
|
onBackButton: typeof onBackButton;
|
|
247
289
|
onLeaveGame: typeof onLeaveGame;
|
|
248
290
|
leaveGame: typeof leaveGame;
|
|
@@ -253,6 +295,7 @@ declare const oasiz: {
|
|
|
253
295
|
readonly playerAvatar: string | undefined;
|
|
254
296
|
readonly safeAreaTop: number;
|
|
255
297
|
readonly viewportInsets: ViewportInsets;
|
|
298
|
+
readonly graphicsPerformance: GraphicsPerformanceMetric;
|
|
256
299
|
};
|
|
257
300
|
|
|
258
|
-
export { type FacingFrameMap, type GameState, type HapticType, type LogOverlayEntry, type LogOverlayHandle, type LogOverlayLevel, type LogOverlayOptions, type PlayerCharacter, type ScoreEditResult, type ShareRequest, type ShareRoomCodeOptions, type TextureAtlas, type TextureAtlasAnimation, type TextureAtlasFrame, type Unsubscribe, type ViewportInsetEdges, type ViewportInsetSide, type ViewportInsets, addScore, enableLogOverlay, flushGameState, getGameId, getPlayerAvatar, getPlayerCharacter, getPlayerId, getPlayerName, getRoomCode, getSafeAreaTop, getViewportInsets, leaveGame, loadGameState, oasiz, onBackButton, onLeaveGame, onPause, onResume, openInviteModal, saveGameState, setLeaderboardVisible, setScore, share, shareRoomCode, submitScore, triggerHaptic };
|
|
301
|
+
export { type BackButtonTestingHandle, type BackButtonTestingOptions, type FacingFrameMap, type GameState, type GraphicsPerformanceMetric, type GraphicsPerformanceTier, type HapticType, type LogOverlayEntry, type LogOverlayHandle, type LogOverlayLevel, type LogOverlayOptions, type PlayerCharacter, type ScoreEditResult, type ShareRequest, type ShareRoomCodeOptions, type TextureAtlas, type TextureAtlasAnimation, type TextureAtlasFrame, type Unsubscribe, type ViewportInsetEdges, type ViewportInsetSide, type ViewportInsets, addScore, enableBackButtonTesting, enableLogOverlay, flushGameState, getGameId, getGraphicsPerformance, getPlayerAvatar, getPlayerCharacter, getPlayerId, getPlayerName, getRoomCode, getSafeAreaTop, getViewportInsets, leaveGame, loadGameState, oasiz, onBackButton, onLeaveGame, onPause, onResume, openInviteModal, saveGameState, setLeaderboardVisible, setScore, share, shareRoomCode, submitScore, triggerHaptic };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
type GraphicsPerformanceTier = "minimal" | "low" | "medium" | "high";
|
|
2
|
+
interface GraphicsPerformanceMetric {
|
|
3
|
+
/** Recommended graphics/rendering frame-rate target for the current device. */
|
|
4
|
+
fps: number;
|
|
5
|
+
/** Suggested graphics/rendering tier for the current device. */
|
|
6
|
+
tier: GraphicsPerformanceTier;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Return the current device's recommended graphics performance profile.
|
|
10
|
+
*
|
|
11
|
+
* Hosts can provide an FPS/tier recommendation through
|
|
12
|
+
* `window.getGraphicsPerformance()` or `window.__OASIZ_GRAPHICS_PERFORMANCE__`.
|
|
13
|
+
* When no host value exists, the SDK estimates from browser/device/WebGL
|
|
14
|
+
* capability signals so games still get a usable recommendation in local
|
|
15
|
+
* development and unsupported hosts.
|
|
16
|
+
*/
|
|
17
|
+
declare function getGraphicsPerformance(): GraphicsPerformanceMetric;
|
|
18
|
+
|
|
1
19
|
type ViewportInsetSide = "top" | "right" | "bottom" | "left";
|
|
2
20
|
interface ViewportInsetEdges {
|
|
3
21
|
top: number;
|
|
@@ -221,6 +239,28 @@ type Unsubscribe = () => void;
|
|
|
221
239
|
declare function onPause(callback: () => void): Unsubscribe;
|
|
222
240
|
declare function onResume(callback: () => void): Unsubscribe;
|
|
223
241
|
|
|
242
|
+
interface BackButtonTestingOptions {
|
|
243
|
+
/**
|
|
244
|
+
* When true, pressing Escape dispatches the same event the host app sends for
|
|
245
|
+
* back actions. Defaults to true.
|
|
246
|
+
*/
|
|
247
|
+
keyboard?: boolean;
|
|
248
|
+
/**
|
|
249
|
+
* When true, the SDK traps one browser-history entry while back override is
|
|
250
|
+
* active, so the browser Back button can be used for local testing. Defaults
|
|
251
|
+
* to true.
|
|
252
|
+
*/
|
|
253
|
+
browserHistory?: boolean;
|
|
254
|
+
/** Log setup details to the console. Defaults to false. */
|
|
255
|
+
log?: boolean;
|
|
256
|
+
}
|
|
257
|
+
interface BackButtonTestingHandle {
|
|
258
|
+
destroy: () => void;
|
|
259
|
+
isBackOverrideActive: () => boolean;
|
|
260
|
+
triggerBack: () => void;
|
|
261
|
+
triggerLeave: () => void;
|
|
262
|
+
}
|
|
263
|
+
declare function enableBackButtonTesting(options?: BackButtonTestingOptions): BackButtonTestingHandle;
|
|
224
264
|
declare function onBackButton(callback: () => void): Unsubscribe;
|
|
225
265
|
declare function onLeaveGame(callback: () => void): Unsubscribe;
|
|
226
266
|
declare function leaveGame(): void;
|
|
@@ -243,6 +283,8 @@ declare const oasiz: {
|
|
|
243
283
|
getSafeAreaTop: typeof getSafeAreaTop;
|
|
244
284
|
getViewportInsets: typeof getViewportInsets;
|
|
245
285
|
setLeaderboardVisible: typeof setLeaderboardVisible;
|
|
286
|
+
getGraphicsPerformance: typeof getGraphicsPerformance;
|
|
287
|
+
enableBackButtonTesting: typeof enableBackButtonTesting;
|
|
246
288
|
onBackButton: typeof onBackButton;
|
|
247
289
|
onLeaveGame: typeof onLeaveGame;
|
|
248
290
|
leaveGame: typeof leaveGame;
|
|
@@ -253,6 +295,7 @@ declare const oasiz: {
|
|
|
253
295
|
readonly playerAvatar: string | undefined;
|
|
254
296
|
readonly safeAreaTop: number;
|
|
255
297
|
readonly viewportInsets: ViewportInsets;
|
|
298
|
+
readonly graphicsPerformance: GraphicsPerformanceMetric;
|
|
256
299
|
};
|
|
257
300
|
|
|
258
|
-
export { type FacingFrameMap, type GameState, type HapticType, type LogOverlayEntry, type LogOverlayHandle, type LogOverlayLevel, type LogOverlayOptions, type PlayerCharacter, type ScoreEditResult, type ShareRequest, type ShareRoomCodeOptions, type TextureAtlas, type TextureAtlasAnimation, type TextureAtlasFrame, type Unsubscribe, type ViewportInsetEdges, type ViewportInsetSide, type ViewportInsets, addScore, enableLogOverlay, flushGameState, getGameId, getPlayerAvatar, getPlayerCharacter, getPlayerId, getPlayerName, getRoomCode, getSafeAreaTop, getViewportInsets, leaveGame, loadGameState, oasiz, onBackButton, onLeaveGame, onPause, onResume, openInviteModal, saveGameState, setLeaderboardVisible, setScore, share, shareRoomCode, submitScore, triggerHaptic };
|
|
301
|
+
export { type BackButtonTestingHandle, type BackButtonTestingOptions, type FacingFrameMap, type GameState, type GraphicsPerformanceMetric, type GraphicsPerformanceTier, type HapticType, type LogOverlayEntry, type LogOverlayHandle, type LogOverlayLevel, type LogOverlayOptions, type PlayerCharacter, type ScoreEditResult, type ShareRequest, type ShareRoomCodeOptions, type TextureAtlas, type TextureAtlasAnimation, type TextureAtlasFrame, type Unsubscribe, type ViewportInsetEdges, type ViewportInsetSide, type ViewportInsets, addScore, enableBackButtonTesting, enableLogOverlay, flushGameState, getGameId, getGraphicsPerformance, getPlayerAvatar, getPlayerCharacter, getPlayerId, getPlayerName, getRoomCode, getSafeAreaTop, getViewportInsets, leaveGame, loadGameState, oasiz, onBackButton, onLeaveGame, onPause, onResume, openInviteModal, saveGameState, setLeaderboardVisible, setScore, share, shareRoomCode, submitScore, triggerHaptic };
|
package/dist/index.js
CHANGED
|
@@ -1577,7 +1577,9 @@ function setLeaderboardVisible(visible) {
|
|
|
1577
1577
|
}
|
|
1578
1578
|
|
|
1579
1579
|
// src/navigation.ts
|
|
1580
|
+
var BACK_BUTTON_TEST_STATE_KEY = "__oasizBackButtonTest";
|
|
1580
1581
|
var activeBackListeners = 0;
|
|
1582
|
+
var activeBackButtonTestingHandle;
|
|
1581
1583
|
function isDevelopment10() {
|
|
1582
1584
|
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
1583
1585
|
return nodeEnv !== "production";
|
|
@@ -1603,6 +1605,16 @@ function normalizeNavigationError(error) {
|
|
|
1603
1605
|
typeof error === "string" ? error : "Back button callback failed."
|
|
1604
1606
|
);
|
|
1605
1607
|
}
|
|
1608
|
+
function isRecord2(value) {
|
|
1609
|
+
return typeof value === "object" && value !== null;
|
|
1610
|
+
}
|
|
1611
|
+
function dispatchNavigationEvent(eventName) {
|
|
1612
|
+
const bridge = getBridgeWindow9();
|
|
1613
|
+
if (!bridge || typeof bridge.dispatchEvent !== "function") {
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1616
|
+
bridge.dispatchEvent(new Event(eventName));
|
|
1617
|
+
}
|
|
1606
1618
|
function addNavigationListener(eventName, callback) {
|
|
1607
1619
|
if (typeof window === "undefined") {
|
|
1608
1620
|
if (isDevelopment10()) {
|
|
@@ -1617,6 +1629,151 @@ function addNavigationListener(eventName, callback) {
|
|
|
1617
1629
|
window.addEventListener(eventName, handler);
|
|
1618
1630
|
return () => window.removeEventListener(eventName, handler);
|
|
1619
1631
|
}
|
|
1632
|
+
function enableBackButtonTesting(options = {}) {
|
|
1633
|
+
activeBackButtonTestingHandle?.destroy();
|
|
1634
|
+
const bridge = getBridgeWindow9();
|
|
1635
|
+
if (!bridge) {
|
|
1636
|
+
if (isDevelopment10()) {
|
|
1637
|
+
console.warn(
|
|
1638
|
+
"[oasiz/sdk] enableBackButtonTesting requires a browser window."
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1641
|
+
return {
|
|
1642
|
+
destroy: () => {
|
|
1643
|
+
},
|
|
1644
|
+
isBackOverrideActive: () => false,
|
|
1645
|
+
triggerBack: () => {
|
|
1646
|
+
},
|
|
1647
|
+
triggerLeave: () => {
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
const bridgeWindow = bridge;
|
|
1652
|
+
const keyboard = options.keyboard ?? true;
|
|
1653
|
+
const browserHistory = options.browserHistory ?? true;
|
|
1654
|
+
const log = options.log === true;
|
|
1655
|
+
const previousSetBackOverride = bridgeWindow.__oasizSetBackOverride;
|
|
1656
|
+
const previousLeaveGame = bridgeWindow.__oasizLeaveGame;
|
|
1657
|
+
let destroyed = false;
|
|
1658
|
+
let backOverrideActive = false;
|
|
1659
|
+
let historyTrapArmed = false;
|
|
1660
|
+
function maybeLog(message) {
|
|
1661
|
+
if (log) {
|
|
1662
|
+
console.info("[oasiz/sdk] " + message);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
function canUseHistoryTrap() {
|
|
1666
|
+
return browserHistory && typeof bridgeWindow.history?.pushState === "function" && typeof bridgeWindow.history?.replaceState === "function" && typeof bridgeWindow.location?.href === "string";
|
|
1667
|
+
}
|
|
1668
|
+
function ensureHistoryTrap() {
|
|
1669
|
+
if (!backOverrideActive || historyTrapArmed || !canUseHistoryTrap()) {
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
try {
|
|
1673
|
+
const currentState = isRecord2(bridgeWindow.history.state) ? bridgeWindow.history.state : {};
|
|
1674
|
+
bridgeWindow.history.replaceState(
|
|
1675
|
+
{ ...currentState, [BACK_BUTTON_TEST_STATE_KEY]: "base" },
|
|
1676
|
+
"",
|
|
1677
|
+
bridgeWindow.location.href
|
|
1678
|
+
);
|
|
1679
|
+
bridgeWindow.history.pushState(
|
|
1680
|
+
{ [BACK_BUTTON_TEST_STATE_KEY]: "trap" },
|
|
1681
|
+
"",
|
|
1682
|
+
bridgeWindow.location.href
|
|
1683
|
+
);
|
|
1684
|
+
historyTrapArmed = true;
|
|
1685
|
+
maybeLog("Local browser Back testing is armed.");
|
|
1686
|
+
} catch (error) {
|
|
1687
|
+
historyTrapArmed = false;
|
|
1688
|
+
if (log) {
|
|
1689
|
+
console.warn("[oasiz/sdk] Failed to arm browser Back testing:", error);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
function triggerBack() {
|
|
1694
|
+
dispatchNavigationEvent("oasiz:back");
|
|
1695
|
+
}
|
|
1696
|
+
function triggerLeave() {
|
|
1697
|
+
dispatchNavigationEvent("oasiz:leave");
|
|
1698
|
+
}
|
|
1699
|
+
function setBackOverride(active) {
|
|
1700
|
+
backOverrideActive = active;
|
|
1701
|
+
if (active) {
|
|
1702
|
+
ensureHistoryTrap();
|
|
1703
|
+
}
|
|
1704
|
+
if (typeof previousSetBackOverride === "function") {
|
|
1705
|
+
previousSetBackOverride(active);
|
|
1706
|
+
}
|
|
1707
|
+
maybeLog("Back override " + (active ? "enabled" : "disabled") + ".");
|
|
1708
|
+
}
|
|
1709
|
+
function stopBackEvent(event) {
|
|
1710
|
+
event.preventDefault();
|
|
1711
|
+
event.stopPropagation();
|
|
1712
|
+
event.stopImmediatePropagation?.();
|
|
1713
|
+
}
|
|
1714
|
+
const handleKeyDown = (event) => {
|
|
1715
|
+
if (!backOverrideActive || event.key !== "Escape") {
|
|
1716
|
+
return;
|
|
1717
|
+
}
|
|
1718
|
+
stopBackEvent(event);
|
|
1719
|
+
triggerBack();
|
|
1720
|
+
};
|
|
1721
|
+
const handlePopState = (event) => {
|
|
1722
|
+
if (!backOverrideActive) {
|
|
1723
|
+
historyTrapArmed = false;
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1726
|
+
stopBackEvent(event);
|
|
1727
|
+
triggerBack();
|
|
1728
|
+
historyTrapArmed = false;
|
|
1729
|
+
ensureHistoryTrap();
|
|
1730
|
+
};
|
|
1731
|
+
const testLeaveGame = () => {
|
|
1732
|
+
triggerLeave();
|
|
1733
|
+
if (typeof previousLeaveGame === "function") {
|
|
1734
|
+
previousLeaveGame();
|
|
1735
|
+
}
|
|
1736
|
+
};
|
|
1737
|
+
bridgeWindow.__oasizSetBackOverride = setBackOverride;
|
|
1738
|
+
bridgeWindow.__oasizLeaveGame = testLeaveGame;
|
|
1739
|
+
if (keyboard) {
|
|
1740
|
+
bridgeWindow.addEventListener("keydown", handleKeyDown);
|
|
1741
|
+
}
|
|
1742
|
+
if (browserHistory) {
|
|
1743
|
+
bridgeWindow.addEventListener("popstate", handlePopState);
|
|
1744
|
+
}
|
|
1745
|
+
if (activeBackListeners > 0) {
|
|
1746
|
+
setBackOverride(true);
|
|
1747
|
+
}
|
|
1748
|
+
maybeLog("Back button testing bridge installed.");
|
|
1749
|
+
const handle = {
|
|
1750
|
+
destroy: () => {
|
|
1751
|
+
if (destroyed) return;
|
|
1752
|
+
destroyed = true;
|
|
1753
|
+
if (keyboard) {
|
|
1754
|
+
bridgeWindow.removeEventListener("keydown", handleKeyDown);
|
|
1755
|
+
}
|
|
1756
|
+
if (browserHistory) {
|
|
1757
|
+
bridgeWindow.removeEventListener("popstate", handlePopState);
|
|
1758
|
+
}
|
|
1759
|
+
if (bridgeWindow.__oasizSetBackOverride === setBackOverride) {
|
|
1760
|
+
bridgeWindow.__oasizSetBackOverride = previousSetBackOverride;
|
|
1761
|
+
}
|
|
1762
|
+
if (bridgeWindow.__oasizLeaveGame === testLeaveGame) {
|
|
1763
|
+
bridgeWindow.__oasizLeaveGame = previousLeaveGame;
|
|
1764
|
+
}
|
|
1765
|
+
if (activeBackButtonTestingHandle === handle) {
|
|
1766
|
+
activeBackButtonTestingHandle = void 0;
|
|
1767
|
+
}
|
|
1768
|
+
maybeLog("Back button testing bridge removed.");
|
|
1769
|
+
},
|
|
1770
|
+
isBackOverrideActive: () => backOverrideActive,
|
|
1771
|
+
triggerBack,
|
|
1772
|
+
triggerLeave
|
|
1773
|
+
};
|
|
1774
|
+
activeBackButtonTestingHandle = handle;
|
|
1775
|
+
return handle;
|
|
1776
|
+
}
|
|
1620
1777
|
function onBackButton(callback) {
|
|
1621
1778
|
const off = addNavigationListener("oasiz:back", () => {
|
|
1622
1779
|
try {
|
|
@@ -1660,6 +1817,329 @@ function leaveGame() {
|
|
|
1660
1817
|
warnMissingBridge7("__oasizLeaveGame");
|
|
1661
1818
|
}
|
|
1662
1819
|
|
|
1820
|
+
// src/performance.ts
|
|
1821
|
+
var DEFAULT_GRAPHICS_PERFORMANCE = {
|
|
1822
|
+
fps: 45,
|
|
1823
|
+
tier: "medium"
|
|
1824
|
+
};
|
|
1825
|
+
var TIER_DEFAULT_FPS = {
|
|
1826
|
+
minimal: 24,
|
|
1827
|
+
low: 30,
|
|
1828
|
+
medium: 45,
|
|
1829
|
+
high: 60
|
|
1830
|
+
};
|
|
1831
|
+
var cachedEstimatedGraphicsPerformance;
|
|
1832
|
+
function getBridgeWindow10() {
|
|
1833
|
+
if (typeof window === "undefined") {
|
|
1834
|
+
return void 0;
|
|
1835
|
+
}
|
|
1836
|
+
return window;
|
|
1837
|
+
}
|
|
1838
|
+
function isRecord3(value) {
|
|
1839
|
+
return typeof value === "object" && value !== null;
|
|
1840
|
+
}
|
|
1841
|
+
function toFiniteNumber2(value) {
|
|
1842
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1843
|
+
return value;
|
|
1844
|
+
}
|
|
1845
|
+
if (typeof value === "string") {
|
|
1846
|
+
const parsed = Number.parseFloat(value.trim());
|
|
1847
|
+
if (Number.isFinite(parsed)) {
|
|
1848
|
+
return parsed;
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
return void 0;
|
|
1852
|
+
}
|
|
1853
|
+
function clampScore(value) {
|
|
1854
|
+
return Math.min(100, Math.max(0, Math.round(value)));
|
|
1855
|
+
}
|
|
1856
|
+
function clampFps(value) {
|
|
1857
|
+
return Math.min(240, Math.max(1, Math.round(value)));
|
|
1858
|
+
}
|
|
1859
|
+
function tierFromScore(score) {
|
|
1860
|
+
if (score < 25) return "minimal";
|
|
1861
|
+
if (score < 40) return "low";
|
|
1862
|
+
if (score < 70) return "medium";
|
|
1863
|
+
return "high";
|
|
1864
|
+
}
|
|
1865
|
+
function tierFromFps(fps) {
|
|
1866
|
+
if (fps < 30) return "minimal";
|
|
1867
|
+
if (fps < 45) return "low";
|
|
1868
|
+
if (fps < 58) return "medium";
|
|
1869
|
+
return "high";
|
|
1870
|
+
}
|
|
1871
|
+
function fpsFromScore(score) {
|
|
1872
|
+
return TIER_DEFAULT_FPS[tierFromScore(score)];
|
|
1873
|
+
}
|
|
1874
|
+
function normalizeTier(value) {
|
|
1875
|
+
if (typeof value !== "string") {
|
|
1876
|
+
return void 0;
|
|
1877
|
+
}
|
|
1878
|
+
const normalized = value.trim().toLowerCase();
|
|
1879
|
+
if (normalized === "minimal" || normalized === "safe") return "minimal";
|
|
1880
|
+
if (normalized === "high") return "high";
|
|
1881
|
+
if (normalized === "medium") return "medium";
|
|
1882
|
+
if (normalized === "low") return "low";
|
|
1883
|
+
return void 0;
|
|
1884
|
+
}
|
|
1885
|
+
function firstDefined2(...values) {
|
|
1886
|
+
return values.find((value) => typeof value !== "undefined");
|
|
1887
|
+
}
|
|
1888
|
+
function normalizeMetric(value) {
|
|
1889
|
+
if (typeof value === "undefined" || value === null) {
|
|
1890
|
+
return void 0;
|
|
1891
|
+
}
|
|
1892
|
+
if (typeof value === "number" || typeof value === "string") {
|
|
1893
|
+
const numeric = toFiniteNumber2(value);
|
|
1894
|
+
if (typeof numeric !== "undefined") {
|
|
1895
|
+
const fps2 = clampFps(numeric);
|
|
1896
|
+
return { fps: fps2, tier: tierFromFps(fps2) };
|
|
1897
|
+
}
|
|
1898
|
+
const tier2 = normalizeTier(value);
|
|
1899
|
+
if (tier2) {
|
|
1900
|
+
return { fps: TIER_DEFAULT_FPS[tier2], tier: tier2 };
|
|
1901
|
+
}
|
|
1902
|
+
return void 0;
|
|
1903
|
+
}
|
|
1904
|
+
if (!isRecord3(value)) {
|
|
1905
|
+
return void 0;
|
|
1906
|
+
}
|
|
1907
|
+
const fpsCandidate = firstDefined2(
|
|
1908
|
+
value.fps,
|
|
1909
|
+
value.targetFps,
|
|
1910
|
+
value.frameRate,
|
|
1911
|
+
value.framesPerSecond
|
|
1912
|
+
);
|
|
1913
|
+
const legacyScoreCandidate = firstDefined2(
|
|
1914
|
+
value.score,
|
|
1915
|
+
value.metric,
|
|
1916
|
+
value.value,
|
|
1917
|
+
value.performance,
|
|
1918
|
+
value.performanceScore,
|
|
1919
|
+
value.graphicsScore
|
|
1920
|
+
);
|
|
1921
|
+
const tierCandidate = firstDefined2(
|
|
1922
|
+
value.tier,
|
|
1923
|
+
value.graphicsTier,
|
|
1924
|
+
value.performanceTier,
|
|
1925
|
+
value.qualityTier,
|
|
1926
|
+
value.recommendedTier
|
|
1927
|
+
);
|
|
1928
|
+
const tier = normalizeTier(tierCandidate);
|
|
1929
|
+
const fpsNumeric = toFiniteNumber2(fpsCandidate);
|
|
1930
|
+
const scoreNumeric = toFiniteNumber2(legacyScoreCandidate);
|
|
1931
|
+
if (typeof fpsNumeric === "undefined" && typeof scoreNumeric === "undefined" && !tier) {
|
|
1932
|
+
return void 0;
|
|
1933
|
+
}
|
|
1934
|
+
const fps = clampFps(
|
|
1935
|
+
typeof fpsNumeric !== "undefined" ? fpsNumeric : typeof scoreNumeric !== "undefined" ? fpsFromScore(clampScore(scoreNumeric)) : TIER_DEFAULT_FPS[tier]
|
|
1936
|
+
);
|
|
1937
|
+
return {
|
|
1938
|
+
fps,
|
|
1939
|
+
tier: tier ?? tierFromFps(fps)
|
|
1940
|
+
};
|
|
1941
|
+
}
|
|
1942
|
+
function callBridgeMetric(bridge, name) {
|
|
1943
|
+
const fn = bridge[name];
|
|
1944
|
+
if (typeof fn !== "function") {
|
|
1945
|
+
return void 0;
|
|
1946
|
+
}
|
|
1947
|
+
try {
|
|
1948
|
+
return fn.call(bridge);
|
|
1949
|
+
} catch (error) {
|
|
1950
|
+
console.error("[oasiz/sdk] " + String(name) + " failed:", error);
|
|
1951
|
+
return void 0;
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
function readHostGraphicsPerformance(bridge) {
|
|
1955
|
+
const candidates = [
|
|
1956
|
+
callBridgeMetric(bridge, "getGraphicsPerformance"),
|
|
1957
|
+
callBridgeMetric(bridge, "getGraphicsPerformanceMetric"),
|
|
1958
|
+
callBridgeMetric(bridge, "getPerformanceMetric"),
|
|
1959
|
+
bridge.__OASIZ_GRAPHICS_PERFORMANCE__,
|
|
1960
|
+
bridge.__OASIZ_PERFORMANCE_METRIC__
|
|
1961
|
+
];
|
|
1962
|
+
for (const candidate of candidates) {
|
|
1963
|
+
const metric = normalizeMetric(candidate);
|
|
1964
|
+
if (metric) {
|
|
1965
|
+
return metric;
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
return void 0;
|
|
1969
|
+
}
|
|
1970
|
+
function getDevicePixelRatio2(bridge) {
|
|
1971
|
+
const dpr = bridge.devicePixelRatio;
|
|
1972
|
+
return typeof dpr === "number" && Number.isFinite(dpr) && dpr > 0 ? dpr : 1;
|
|
1973
|
+
}
|
|
1974
|
+
function getNavigatorValue(bridge) {
|
|
1975
|
+
return bridge.navigator;
|
|
1976
|
+
}
|
|
1977
|
+
function parseIosMajorVersion(userAgent) {
|
|
1978
|
+
const match = /\bOS (\d+)_/i.exec(userAgent);
|
|
1979
|
+
if (!match) {
|
|
1980
|
+
return void 0;
|
|
1981
|
+
}
|
|
1982
|
+
const parsed = Number.parseInt(match[1] ?? "", 10);
|
|
1983
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
1984
|
+
}
|
|
1985
|
+
function isAppleMobileDevice(bridge) {
|
|
1986
|
+
const userAgent = bridge.navigator?.userAgent ?? "";
|
|
1987
|
+
return /iP(hone|ad|od)/i.test(userAgent) || /Macintosh/i.test(userAgent) && (bridge.navigator?.maxTouchPoints ?? 0) > 1;
|
|
1988
|
+
}
|
|
1989
|
+
function getLongestScreenEdge(bridge) {
|
|
1990
|
+
const screenWidth = bridge.screen?.width;
|
|
1991
|
+
const screenHeight = bridge.screen?.height;
|
|
1992
|
+
const innerWidth = bridge.innerWidth;
|
|
1993
|
+
const innerHeight = bridge.innerHeight;
|
|
1994
|
+
return Math.max(
|
|
1995
|
+
typeof screenWidth === "number" ? screenWidth : 0,
|
|
1996
|
+
typeof screenHeight === "number" ? screenHeight : 0,
|
|
1997
|
+
typeof innerWidth === "number" ? innerWidth : 0,
|
|
1998
|
+
typeof innerHeight === "number" ? innerHeight : 0
|
|
1999
|
+
);
|
|
2000
|
+
}
|
|
2001
|
+
function isCoarsePointer(bridge) {
|
|
2002
|
+
try {
|
|
2003
|
+
return bridge.matchMedia?.("(pointer: coarse)").matches === true;
|
|
2004
|
+
} catch {
|
|
2005
|
+
return false;
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
function getWebGlInfo(bridge) {
|
|
2009
|
+
const canvas = bridge.document?.createElement?.("canvas");
|
|
2010
|
+
if (!canvas || typeof canvas.getContext !== "function") {
|
|
2011
|
+
return { webglVersion: 0 };
|
|
2012
|
+
}
|
|
2013
|
+
const gl2 = canvas.getContext("webgl2");
|
|
2014
|
+
const gl = gl2 ?? canvas.getContext("webgl") ?? canvas.getContext("experimental-webgl");
|
|
2015
|
+
if (!gl) {
|
|
2016
|
+
return { webglVersion: 0 };
|
|
2017
|
+
}
|
|
2018
|
+
const context = gl;
|
|
2019
|
+
let maxTextureSize;
|
|
2020
|
+
let renderer;
|
|
2021
|
+
try {
|
|
2022
|
+
const value = context.getParameter(context.MAX_TEXTURE_SIZE);
|
|
2023
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2024
|
+
maxTextureSize = value;
|
|
2025
|
+
}
|
|
2026
|
+
} catch {
|
|
2027
|
+
}
|
|
2028
|
+
try {
|
|
2029
|
+
const debugInfo = context.getExtension("WEBGL_debug_renderer_info");
|
|
2030
|
+
const rendererParam = debugInfo?.UNMASKED_RENDERER_WEBGL ?? context.RENDERER;
|
|
2031
|
+
const value = context.getParameter(rendererParam);
|
|
2032
|
+
if (typeof value === "string") {
|
|
2033
|
+
renderer = value;
|
|
2034
|
+
}
|
|
2035
|
+
} catch {
|
|
2036
|
+
}
|
|
2037
|
+
return {
|
|
2038
|
+
maxTextureSize,
|
|
2039
|
+
renderer,
|
|
2040
|
+
webglVersion: gl2 ? 2 : 1
|
|
2041
|
+
};
|
|
2042
|
+
}
|
|
2043
|
+
function applyMemoryScore(score, memory) {
|
|
2044
|
+
if (typeof memory !== "number" || !Number.isFinite(memory) || memory <= 0) {
|
|
2045
|
+
return score;
|
|
2046
|
+
}
|
|
2047
|
+
if (memory <= 1) return score - 18;
|
|
2048
|
+
if (memory <= 2) return score - 10;
|
|
2049
|
+
if (memory >= 8) return score + 16;
|
|
2050
|
+
if (memory >= 6) return score + 10;
|
|
2051
|
+
return score;
|
|
2052
|
+
}
|
|
2053
|
+
function applyCoreScore(score, cores) {
|
|
2054
|
+
if (typeof cores !== "number" || !Number.isFinite(cores) || cores <= 0) {
|
|
2055
|
+
return score;
|
|
2056
|
+
}
|
|
2057
|
+
if (cores <= 2) return score - 12;
|
|
2058
|
+
if (cores <= 4) return score;
|
|
2059
|
+
if (cores >= 8) return score + 12;
|
|
2060
|
+
return score + 8;
|
|
2061
|
+
}
|
|
2062
|
+
function applyWebGlScore(score, info) {
|
|
2063
|
+
let next = score;
|
|
2064
|
+
if (info.webglVersion === 2) {
|
|
2065
|
+
next += 16;
|
|
2066
|
+
} else if (info.webglVersion === 1) {
|
|
2067
|
+
next += 6;
|
|
2068
|
+
} else {
|
|
2069
|
+
next -= 25;
|
|
2070
|
+
}
|
|
2071
|
+
if (typeof info.maxTextureSize === "number") {
|
|
2072
|
+
if (info.maxTextureSize >= 8192) next += 10;
|
|
2073
|
+
else if (info.maxTextureSize >= 4096) next += 3;
|
|
2074
|
+
else next -= 8;
|
|
2075
|
+
}
|
|
2076
|
+
const renderer = info.renderer?.toLowerCase() ?? "";
|
|
2077
|
+
if (/swiftshader|llvmpipe|software/.test(renderer)) {
|
|
2078
|
+
next = Math.min(next, 35);
|
|
2079
|
+
} else if (/\bm[1-9]\b|apple gpu|a1[6-9]|rtx|radeon|adreno 7|adreno 8/.test(renderer)) {
|
|
2080
|
+
next += 8;
|
|
2081
|
+
}
|
|
2082
|
+
return next;
|
|
2083
|
+
}
|
|
2084
|
+
function applyMobileCostScore(score, bridge) {
|
|
2085
|
+
if (!isCoarsePointer(bridge)) {
|
|
2086
|
+
return score;
|
|
2087
|
+
}
|
|
2088
|
+
const dpr = getDevicePixelRatio2(bridge);
|
|
2089
|
+
const width = bridge.screen?.width ?? bridge.innerWidth ?? 0;
|
|
2090
|
+
const height = bridge.screen?.height ?? bridge.innerHeight ?? 0;
|
|
2091
|
+
const physicalPixels = width * height * dpr * dpr;
|
|
2092
|
+
if (physicalPixels > 4e6) {
|
|
2093
|
+
return score - 5;
|
|
2094
|
+
}
|
|
2095
|
+
return score;
|
|
2096
|
+
}
|
|
2097
|
+
function applyAppleMobileRules(score, bridge) {
|
|
2098
|
+
if (!isAppleMobileDevice(bridge)) {
|
|
2099
|
+
return score;
|
|
2100
|
+
}
|
|
2101
|
+
const userAgent = bridge.navigator?.userAgent ?? "";
|
|
2102
|
+
const iosMajorVersion = parseIosMajorVersion(userAgent);
|
|
2103
|
+
const highDensityDisplay = getDevicePixelRatio2(bridge) >= 3;
|
|
2104
|
+
const longestScreenEdge = getLongestScreenEdge(bridge);
|
|
2105
|
+
if (typeof iosMajorVersion === "number" && iosMajorVersion < 15) {
|
|
2106
|
+
return Math.min(score, 35);
|
|
2107
|
+
}
|
|
2108
|
+
if (highDensityDisplay && longestScreenEdge >= 430 && (typeof iosMajorVersion === "undefined" || iosMajorVersion >= 17)) {
|
|
2109
|
+
return Math.max(score, 78);
|
|
2110
|
+
}
|
|
2111
|
+
if (highDensityDisplay && longestScreenEdge >= 414 && (typeof iosMajorVersion === "undefined" || iosMajorVersion >= 16)) {
|
|
2112
|
+
return Math.max(score, 62);
|
|
2113
|
+
}
|
|
2114
|
+
return Math.min(score, 48);
|
|
2115
|
+
}
|
|
2116
|
+
function estimateGraphicsPerformance(bridge) {
|
|
2117
|
+
const navigatorValue = getNavigatorValue(bridge);
|
|
2118
|
+
let score = 45;
|
|
2119
|
+
score = applyMemoryScore(score, navigatorValue?.deviceMemory);
|
|
2120
|
+
score = applyCoreScore(score, navigatorValue?.hardwareConcurrency);
|
|
2121
|
+
score = applyWebGlScore(score, getWebGlInfo(bridge));
|
|
2122
|
+
score = applyMobileCostScore(score, bridge);
|
|
2123
|
+
score = applyAppleMobileRules(score, bridge);
|
|
2124
|
+
const normalizedScore = clampScore(score);
|
|
2125
|
+
return {
|
|
2126
|
+
fps: fpsFromScore(normalizedScore),
|
|
2127
|
+
tier: tierFromScore(normalizedScore)
|
|
2128
|
+
};
|
|
2129
|
+
}
|
|
2130
|
+
function getGraphicsPerformance() {
|
|
2131
|
+
const bridge = getBridgeWindow10();
|
|
2132
|
+
if (!bridge) {
|
|
2133
|
+
return { ...DEFAULT_GRAPHICS_PERFORMANCE };
|
|
2134
|
+
}
|
|
2135
|
+
const hostMetric = readHostGraphicsPerformance(bridge);
|
|
2136
|
+
if (hostMetric) {
|
|
2137
|
+
return hostMetric;
|
|
2138
|
+
}
|
|
2139
|
+
cachedEstimatedGraphicsPerformance ??= estimateGraphicsPerformance(bridge);
|
|
2140
|
+
return { ...cachedEstimatedGraphicsPerformance };
|
|
2141
|
+
}
|
|
2142
|
+
|
|
1663
2143
|
// src/index.ts
|
|
1664
2144
|
var oasiz = {
|
|
1665
2145
|
submitScore,
|
|
@@ -1679,6 +2159,8 @@ var oasiz = {
|
|
|
1679
2159
|
getSafeAreaTop,
|
|
1680
2160
|
getViewportInsets,
|
|
1681
2161
|
setLeaderboardVisible,
|
|
2162
|
+
getGraphicsPerformance,
|
|
2163
|
+
enableBackButtonTesting,
|
|
1682
2164
|
onBackButton,
|
|
1683
2165
|
onLeaveGame,
|
|
1684
2166
|
leaveGame,
|
|
@@ -1702,13 +2184,18 @@ var oasiz = {
|
|
|
1702
2184
|
},
|
|
1703
2185
|
get viewportInsets() {
|
|
1704
2186
|
return getViewportInsets();
|
|
2187
|
+
},
|
|
2188
|
+
get graphicsPerformance() {
|
|
2189
|
+
return getGraphicsPerformance();
|
|
1705
2190
|
}
|
|
1706
2191
|
};
|
|
1707
2192
|
export {
|
|
1708
2193
|
addScore,
|
|
2194
|
+
enableBackButtonTesting,
|
|
1709
2195
|
enableLogOverlay,
|
|
1710
2196
|
flushGameState,
|
|
1711
2197
|
getGameId,
|
|
2198
|
+
getGraphicsPerformance,
|
|
1712
2199
|
getPlayerAvatar,
|
|
1713
2200
|
getPlayerCharacter,
|
|
1714
2201
|
getPlayerId,
|