@wvdsh/sdk-js 1.3.12 → 1.3.14
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/dist/client.d.ts +1 -1
- package/dist/index.d.ts +61 -6
- package/dist/index.js +260 -15
- package/package.json +3 -3
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WavedashSDK } from './index.js';
|
|
2
|
-
export { BackendConnectionPayload, EngineInstance, Friend, FullscreenChangedPayload, Leaderboard, LeaderboardDisplayType, LeaderboardEntries, LeaderboardSortOrder, ListUGCItemsArgs, Lobby, LobbyDataUpdatedPayload, LobbyInvite, LobbyInvitePayload, LobbyJoinResponse, LobbyJoinedPayload, LobbyKickedPayload, LobbyKickedReason, LobbyMessage, LobbyMessagePayload, LobbyUser, LobbyUserChangeType, LobbyUsersUpdatedPayload, LobbyVisibility, P2PConfig, P2PConnection, P2PConnectionEstablishedPayload, P2PConnectionFailedPayload, P2PMessage, P2PPacketDropReason, P2PPacketDroppedPayload, P2PPeer, P2PPeerDisconnectedPayload, P2PPeerReconnectedPayload, P2PPeerReconnectingPayload, PaginatedUGCItems, RemoteFileMetadata, StatsStoredPayload, UGCItem, UGCType, UGCVisibility, UpdateUGCItemArgs, UpsertedLeaderboardEntry, WavedashConfig, WavedashEvent, WavedashEventMap, WavedashResponse } from './index.js';
|
|
2
|
+
export { BackendConnectionPayload, EngineInstance, Friend, FullscreenChangedPayload, Leaderboard, LeaderboardDisplayType, LeaderboardEntries, LeaderboardSortOrder, ListUGCItemsArgs, Lobby, LobbyDataUpdatedPayload, LobbyInvite, LobbyInvitePayload, LobbyJoinResponse, LobbyJoinedPayload, LobbyKickedPayload, LobbyKickedReason, LobbyMessage, LobbyMessagePayload, LobbyUser, LobbyUserChangeType, LobbyUsersUpdatedPayload, LobbyVisibility, MuteChangedPayload, P2PConfig, P2PConnection, P2PConnectionEstablishedPayload, P2PConnectionFailedPayload, P2PMessage, P2PPacketDropReason, P2PPacketDroppedPayload, P2PPeer, P2PPeerDisconnectedPayload, P2PPeerReconnectedPayload, P2PPeerReconnectingPayload, PaginatedUGCItems, RemoteFileMetadata, StatsStoredPayload, UGCItem, UGCType, UGCVisibility, UpdateUGCItemArgs, UpsertedLeaderboardEntry, WavedashConfig, WavedashEvent, WavedashEventMap, WavedashResponse } from './index.js';
|
|
3
3
|
export { GameLaunchParams } from '@wvdsh/api';
|
|
4
4
|
export { GenericId as Id } from 'convex/values';
|
|
5
5
|
import 'convex/browser';
|
package/dist/index.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ declare const WavedashEvents: {
|
|
|
31
31
|
readonly BACKEND_DISCONNECTED: "BackendDisconnected";
|
|
32
32
|
readonly BACKEND_RECONNECTING: "BackendReconnecting";
|
|
33
33
|
readonly FULLSCREEN_CHANGED: "FullscreenChanged";
|
|
34
|
+
readonly MUTE_CHANGED: "MuteChanged";
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
/** Reasons why a user was kicked from a lobby */
|
|
@@ -214,6 +215,10 @@ interface BackendConnectionPayload {
|
|
|
214
215
|
interface FullscreenChangedPayload {
|
|
215
216
|
isFullscreen: boolean;
|
|
216
217
|
}
|
|
218
|
+
/** Payload for MuteChanged event - emitted when mute state flips */
|
|
219
|
+
interface MuteChangedPayload {
|
|
220
|
+
isMuted: boolean;
|
|
221
|
+
}
|
|
217
222
|
type WavedashEventMap = {
|
|
218
223
|
[WavedashEvents.LOBBY_MESSAGE]: LobbyMessagePayload;
|
|
219
224
|
[WavedashEvents.LOBBY_JOINED]: LobbyJoinedPayload;
|
|
@@ -232,6 +237,7 @@ type WavedashEventMap = {
|
|
|
232
237
|
[WavedashEvents.BACKEND_DISCONNECTED]: BackendConnectionPayload;
|
|
233
238
|
[WavedashEvents.BACKEND_RECONNECTING]: BackendConnectionPayload;
|
|
234
239
|
[WavedashEvents.FULLSCREEN_CHANGED]: FullscreenChangedPayload;
|
|
240
|
+
[WavedashEvents.MUTE_CHANGED]: MuteChangedPayload;
|
|
235
241
|
};
|
|
236
242
|
interface P2PPeer {
|
|
237
243
|
userId: GenericId<"users">;
|
|
@@ -627,6 +633,7 @@ declare class HeartbeatManager extends WavedashManager {
|
|
|
627
633
|
private isFirstTick;
|
|
628
634
|
private readonly TEST_CONNECTION_INTERVAL_MS;
|
|
629
635
|
private readonly DISCONNECTED_TIMEOUT_MS;
|
|
636
|
+
private cachedPresenceData;
|
|
630
637
|
constructor(sdk: WavedashSDK);
|
|
631
638
|
/** Start heartbeat and connection-check intervals */
|
|
632
639
|
start(): void;
|
|
@@ -638,11 +645,11 @@ declare class HeartbeatManager extends WavedashManager {
|
|
|
638
645
|
private tickHeartbeat;
|
|
639
646
|
private sendHeartbeat;
|
|
640
647
|
/**
|
|
641
|
-
* Updates user presence in the backend
|
|
648
|
+
* Updates user presence in the backend.
|
|
642
649
|
* @param data - Data to send to the backend
|
|
643
650
|
* @returns true if the presence was updated successfully
|
|
644
651
|
*/
|
|
645
|
-
updateUserPresence(data
|
|
652
|
+
updateUserPresence(data: Record<string, string | number | boolean | null>): Promise<boolean>;
|
|
646
653
|
/**
|
|
647
654
|
* Tests the connection to the backend
|
|
648
655
|
*/
|
|
@@ -713,6 +720,47 @@ declare class OverlayManager extends WavedashManager {
|
|
|
713
720
|
private handleKeyDown;
|
|
714
721
|
}
|
|
715
722
|
|
|
723
|
+
/**
|
|
724
|
+
* AudioManager
|
|
725
|
+
*
|
|
726
|
+
* Mutes & unmutes the game in response to MUTE_CHANGED iframe messages, without
|
|
727
|
+
* the game needing to know anything about it.
|
|
728
|
+
*
|
|
729
|
+
* Web Audio: subclass `AudioContext` so `ctx.destination` resolves to a master
|
|
730
|
+
* GainNode that we control. The master gain wires to the real native destination,
|
|
731
|
+
* so `node.connect(ctx.destination)` and any other game code is unaffected.
|
|
732
|
+
*
|
|
733
|
+
* HTML Media (`<audio>`/`<video>`): override `HTMLMediaElement.prototype.muted`
|
|
734
|
+
* to record the game's intended state, but write `true` to the underlying element
|
|
735
|
+
* whenever the SDK is muted. Tracked elements come from three sources:
|
|
736
|
+
* 1. Pre-existing DOM media (`querySelectorAll`)
|
|
737
|
+
* 2. `new Audio()` constructor shim (covers detached SFX)
|
|
738
|
+
* 3. MutationObserver for any media added to the DOM later (covers innerHTML,
|
|
739
|
+
* framework rendering, createElement + append, etc.)
|
|
740
|
+
*/
|
|
741
|
+
declare class AudioManager extends WavedashManager {
|
|
742
|
+
private _isMuted;
|
|
743
|
+
private contexts;
|
|
744
|
+
private elements;
|
|
745
|
+
private intendedMuted;
|
|
746
|
+
private originalAudioContext;
|
|
747
|
+
private originalWebKitAudioContext;
|
|
748
|
+
private originalAudio;
|
|
749
|
+
private originalMutedDescriptor;
|
|
750
|
+
private mutationObserver;
|
|
751
|
+
constructor(sdk: WavedashSDK);
|
|
752
|
+
isMuted(): boolean;
|
|
753
|
+
private handleMute;
|
|
754
|
+
/**
|
|
755
|
+
* Track a media element and (if SDK is currently muted) silence it.
|
|
756
|
+
* Idempotent — safe to call multiple times for the same element.
|
|
757
|
+
*/
|
|
758
|
+
private trackElement;
|
|
759
|
+
private installShims;
|
|
760
|
+
private shimAudioContextClass;
|
|
761
|
+
destroy(): void;
|
|
762
|
+
}
|
|
763
|
+
|
|
716
764
|
/**
|
|
717
765
|
* Friends service
|
|
718
766
|
*
|
|
@@ -831,6 +879,7 @@ declare class WavedashSDK extends EventTarget {
|
|
|
831
879
|
readonly BACKEND_DISCONNECTED: "BackendDisconnected";
|
|
832
880
|
readonly BACKEND_RECONNECTING: "BackendReconnecting";
|
|
833
881
|
readonly FULLSCREEN_CHANGED: "FullscreenChanged";
|
|
882
|
+
readonly MUTE_CHANGED: "MuteChanged";
|
|
834
883
|
};
|
|
835
884
|
LobbyVisibility: {
|
|
836
885
|
readonly PUBLIC: 0;
|
|
@@ -898,6 +947,7 @@ declare class WavedashSDK extends EventTarget {
|
|
|
898
947
|
p2pManager: P2PManager;
|
|
899
948
|
fullscreenManager: FullscreenManager;
|
|
900
949
|
overlayManager: OverlayManager;
|
|
950
|
+
audioManager: AudioManager;
|
|
901
951
|
private managers;
|
|
902
952
|
private gameplayJwt;
|
|
903
953
|
private gameplayJwtPromise;
|
|
@@ -1162,11 +1212,16 @@ declare class WavedashSDK extends EventTarget {
|
|
|
1162
1212
|
inviteUserToLobby(lobbyId: GenericId<"lobbies">, userId: GenericId<"users">): Promise<WavedashResponse<boolean>>;
|
|
1163
1213
|
getLobbyInviteLink(copyToClipboard?: boolean): Promise<WavedashResponse<string>>;
|
|
1164
1214
|
/**
|
|
1165
|
-
* Updates rich user presence so friends can see what the player is doing in game
|
|
1166
|
-
*
|
|
1215
|
+
* Updates rich user presence so friends can see what the player is doing in game.
|
|
1216
|
+
* Supported keys:
|
|
1217
|
+
* `status` — one-line activity shown as the primary line (e.g. "Traveling in a group")
|
|
1218
|
+
* `details` — secondary context shown beneath the status (e.g. current zone or mode)
|
|
1219
|
+
*
|
|
1220
|
+
* Pass an empty dictionary to clear all presence fields.
|
|
1221
|
+
* @param data Presence fields to update.
|
|
1167
1222
|
* @returns true if the presence was updated successfully
|
|
1168
1223
|
*/
|
|
1169
|
-
updateUserPresence(data
|
|
1224
|
+
updateUserPresence(data: Record<string, string | number | boolean | null>): Promise<WavedashResponse<boolean>>;
|
|
1170
1225
|
private isGodot;
|
|
1171
1226
|
private formatResponse;
|
|
1172
1227
|
private ensureInit;
|
|
@@ -1213,4 +1268,4 @@ declare global {
|
|
|
1213
1268
|
|
|
1214
1269
|
declare function setupWavedashSDK(): WavedashSDK;
|
|
1215
1270
|
|
|
1216
|
-
export { type BackendConnectionPayload, type EngineInstance, type Friend, type FullscreenChangedPayload, type Leaderboard, type LeaderboardDisplayType, type LeaderboardEntries, type LeaderboardSortOrder, type ListUGCItemsArgs, type Lobby, type LobbyDataUpdatedPayload, type LobbyInvite, type LobbyInvitePayload, type LobbyJoinResponse, type LobbyJoinedPayload, type LobbyKickedPayload, type LobbyKickedReason, type LobbyMessage, type LobbyMessagePayload, type LobbyUser, type LobbyUserChangeType, type LobbyUsersUpdatedPayload, type LobbyVisibility, type P2PConfig, type P2PConnection, type P2PConnectionEstablishedPayload, type P2PConnectionFailedPayload, type P2PMessage, type P2PPacketDropReason, type P2PPacketDroppedPayload, type P2PPeer, type P2PPeerDisconnectedPayload, type P2PPeerReconnectedPayload, type P2PPeerReconnectingPayload, type PaginatedUGCItems, type RemoteFileMetadata, type StatsStoredPayload, type UGCItem, type UGCType, type UGCVisibility, type UpdateUGCItemArgs, type UpsertedLeaderboardEntry, type WavedashConfig, type WavedashEvent, type WavedashEventMap, type WavedashResponse, WavedashSDK, setupWavedashSDK };
|
|
1271
|
+
export { type BackendConnectionPayload, type EngineInstance, type Friend, type FullscreenChangedPayload, type Leaderboard, type LeaderboardDisplayType, type LeaderboardEntries, type LeaderboardSortOrder, type ListUGCItemsArgs, type Lobby, type LobbyDataUpdatedPayload, type LobbyInvite, type LobbyInvitePayload, type LobbyJoinResponse, type LobbyJoinedPayload, type LobbyKickedPayload, type LobbyKickedReason, type LobbyMessage, type LobbyMessagePayload, type LobbyUser, type LobbyUserChangeType, type LobbyUsersUpdatedPayload, type LobbyVisibility, type MuteChangedPayload, type P2PConfig, type P2PConnection, type P2PConnectionEstablishedPayload, type P2PConnectionFailedPayload, type P2PMessage, type P2PPacketDropReason, type P2PPacketDroppedPayload, type P2PPeer, type P2PPeerDisconnectedPayload, type P2PPeerReconnectedPayload, type P2PPeerReconnectingPayload, type PaginatedUGCItems, type RemoteFileMetadata, type StatsStoredPayload, type UGCItem, type UGCType, type UGCVisibility, type UpdateUGCItemArgs, type UpsertedLeaderboardEntry, type WavedashConfig, type WavedashEvent, type WavedashEventMap, type WavedashResponse, WavedashSDK, setupWavedashSDK };
|
package/dist/index.js
CHANGED
|
@@ -75,8 +75,11 @@ var WavedashEvents = {
|
|
|
75
75
|
BACKEND_RECONNECTING: "BackendReconnecting",
|
|
76
76
|
// attempting to reconnect to backend
|
|
77
77
|
// Fullscreen events
|
|
78
|
-
FULLSCREEN_CHANGED: "FullscreenChanged"
|
|
78
|
+
FULLSCREEN_CHANGED: "FullscreenChanged",
|
|
79
79
|
// fullscreen state changed
|
|
80
|
+
// Audio events
|
|
81
|
+
MUTE_CHANGED: "MuteChanged"
|
|
82
|
+
// mute state changed
|
|
80
83
|
// TODO: Future events to implement
|
|
81
84
|
// P2P_CONNECTION_REQUESTED: 'P2PConnectionRequested', // for now we always connect all lobby members
|
|
82
85
|
};
|
|
@@ -2709,6 +2712,7 @@ var HeartbeatManager = class extends WavedashManager {
|
|
|
2709
2712
|
this.isFirstTick = true;
|
|
2710
2713
|
this.TEST_CONNECTION_INTERVAL_MS = 1e3;
|
|
2711
2714
|
this.DISCONNECTED_TIMEOUT_MS = 9e4;
|
|
2715
|
+
this.cachedPresenceData = {};
|
|
2712
2716
|
this.handleVisibilityChange = () => {
|
|
2713
2717
|
if (document.visibilityState === "visible") {
|
|
2714
2718
|
this.start();
|
|
@@ -2778,7 +2782,7 @@ var HeartbeatManager = class extends WavedashManager {
|
|
|
2778
2782
|
this.heartbeatInFlight = true;
|
|
2779
2783
|
this.sdk.convexClient.mutation(api7.sdk.presence.heartbeat, {
|
|
2780
2784
|
...reestablish ? {
|
|
2781
|
-
data:
|
|
2785
|
+
data: this.cachedPresenceData,
|
|
2782
2786
|
deviceFingerprint: this.deviceFingerprint
|
|
2783
2787
|
} : {}
|
|
2784
2788
|
}).then((accepted) => {
|
|
@@ -2792,15 +2796,15 @@ var HeartbeatManager = class extends WavedashManager {
|
|
|
2792
2796
|
});
|
|
2793
2797
|
}
|
|
2794
2798
|
/**
|
|
2795
|
-
* Updates user presence in the backend
|
|
2799
|
+
* Updates user presence in the backend.
|
|
2796
2800
|
* @param data - Data to send to the backend
|
|
2797
2801
|
* @returns true if the presence was updated successfully
|
|
2798
2802
|
*/
|
|
2799
2803
|
async updateUserPresence(data) {
|
|
2800
2804
|
try {
|
|
2801
|
-
|
|
2805
|
+
this.cachedPresenceData = data;
|
|
2802
2806
|
await this.sdk.convexClient.mutation(api7.sdk.presence.heartbeat, {
|
|
2803
|
-
data
|
|
2807
|
+
data,
|
|
2804
2808
|
deviceFingerprint: this.deviceFingerprint
|
|
2805
2809
|
});
|
|
2806
2810
|
return true;
|
|
@@ -3028,6 +3032,214 @@ var OverlayManager = class extends WavedashManager {
|
|
|
3028
3032
|
}
|
|
3029
3033
|
};
|
|
3030
3034
|
|
|
3035
|
+
// src/services/audio.ts
|
|
3036
|
+
import { IFRAME_MESSAGE_TYPE as IFRAME_MESSAGE_TYPE5 } from "@wvdsh/api";
|
|
3037
|
+
var WeakRefSet = class {
|
|
3038
|
+
constructor() {
|
|
3039
|
+
this.set = /* @__PURE__ */ new Set();
|
|
3040
|
+
}
|
|
3041
|
+
add(value) {
|
|
3042
|
+
for (const ref of this.set) {
|
|
3043
|
+
if (ref.deref() === value) return;
|
|
3044
|
+
}
|
|
3045
|
+
this.set.add(new WeakRef(value));
|
|
3046
|
+
}
|
|
3047
|
+
forEach(callback) {
|
|
3048
|
+
for (const ref of this.set) {
|
|
3049
|
+
const v = ref.deref();
|
|
3050
|
+
if (v === void 0) this.set.delete(ref);
|
|
3051
|
+
else callback(v);
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
clear() {
|
|
3055
|
+
this.set.clear();
|
|
3056
|
+
}
|
|
3057
|
+
};
|
|
3058
|
+
var AudioManager = class extends WavedashManager {
|
|
3059
|
+
constructor(sdk) {
|
|
3060
|
+
super(sdk);
|
|
3061
|
+
this._isMuted = false;
|
|
3062
|
+
// Web Audio contexts and their master gain nodes
|
|
3063
|
+
this.contexts = /* @__PURE__ */ new Map();
|
|
3064
|
+
// HTML media elements we know about + their game-intended muted state
|
|
3065
|
+
this.elements = new WeakRefSet();
|
|
3066
|
+
this.intendedMuted = /* @__PURE__ */ new WeakMap();
|
|
3067
|
+
// Originals (restored on destroy)
|
|
3068
|
+
this.originalAudioContext = null;
|
|
3069
|
+
this.originalWebKitAudioContext = null;
|
|
3070
|
+
this.originalAudio = null;
|
|
3071
|
+
this.originalMutedDescriptor = null;
|
|
3072
|
+
this.mutationObserver = null;
|
|
3073
|
+
this.handleMute = (data) => {
|
|
3074
|
+
if (this._isMuted === data.isMuted) return;
|
|
3075
|
+
this._isMuted = data.isMuted;
|
|
3076
|
+
const target = this._isMuted ? 0 : 1;
|
|
3077
|
+
this.contexts.forEach((gain, ctx) => {
|
|
3078
|
+
const now = ctx.currentTime;
|
|
3079
|
+
gain.gain.cancelScheduledValues(now);
|
|
3080
|
+
gain.gain.setValueAtTime(gain.gain.value, now);
|
|
3081
|
+
gain.gain.linearRampToValueAtTime(target, now + 0.05);
|
|
3082
|
+
});
|
|
3083
|
+
const setMutedNative = this.originalMutedDescriptor?.set;
|
|
3084
|
+
if (setMutedNative) {
|
|
3085
|
+
this.elements.forEach((el) => {
|
|
3086
|
+
const intended = this.intendedMuted.get(el) ?? false;
|
|
3087
|
+
setMutedNative.call(el, this._isMuted ? true : intended);
|
|
3088
|
+
});
|
|
3089
|
+
}
|
|
3090
|
+
this.sdk.gameEventManager.notifyGame(
|
|
3091
|
+
WavedashEvents.MUTE_CHANGED,
|
|
3092
|
+
{ isMuted: this._isMuted }
|
|
3093
|
+
);
|
|
3094
|
+
};
|
|
3095
|
+
this.installShims();
|
|
3096
|
+
this.sdk.iframeMessenger.addEventListener(
|
|
3097
|
+
IFRAME_MESSAGE_TYPE5.MUTE_CHANGED,
|
|
3098
|
+
this.handleMute
|
|
3099
|
+
);
|
|
3100
|
+
}
|
|
3101
|
+
isMuted() {
|
|
3102
|
+
return this._isMuted;
|
|
3103
|
+
}
|
|
3104
|
+
/**
|
|
3105
|
+
* Track a media element and (if SDK is currently muted) silence it.
|
|
3106
|
+
* Idempotent — safe to call multiple times for the same element.
|
|
3107
|
+
*/
|
|
3108
|
+
trackElement(el) {
|
|
3109
|
+
if (this.intendedMuted.has(el)) return;
|
|
3110
|
+
const getMuted = this.originalMutedDescriptor?.get;
|
|
3111
|
+
const setMuted = this.originalMutedDescriptor?.set;
|
|
3112
|
+
const current = getMuted ? getMuted.call(el) : el.muted;
|
|
3113
|
+
this.intendedMuted.set(el, current);
|
|
3114
|
+
this.elements.add(el);
|
|
3115
|
+
if (this._isMuted && !current && setMuted) {
|
|
3116
|
+
setMuted.call(el, true);
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
installShims() {
|
|
3120
|
+
if (typeof window === "undefined") return;
|
|
3121
|
+
if (window.AudioContext) {
|
|
3122
|
+
this.originalAudioContext = window.AudioContext;
|
|
3123
|
+
window.AudioContext = this.shimAudioContextClass(window.AudioContext);
|
|
3124
|
+
}
|
|
3125
|
+
const win = window;
|
|
3126
|
+
if (win.webkitAudioContext) {
|
|
3127
|
+
this.originalWebKitAudioContext = win.webkitAudioContext;
|
|
3128
|
+
win.webkitAudioContext = this.shimAudioContextClass(win.webkitAudioContext);
|
|
3129
|
+
}
|
|
3130
|
+
if (window.Audio) {
|
|
3131
|
+
const OriginalAudio = window.Audio;
|
|
3132
|
+
this.originalAudio = OriginalAudio;
|
|
3133
|
+
((manager) => {
|
|
3134
|
+
const Shimmed = function(src) {
|
|
3135
|
+
const audio = new OriginalAudio(src);
|
|
3136
|
+
manager.trackElement(audio);
|
|
3137
|
+
return audio;
|
|
3138
|
+
};
|
|
3139
|
+
Shimmed.prototype = OriginalAudio.prototype;
|
|
3140
|
+
window.Audio = Shimmed;
|
|
3141
|
+
})(this);
|
|
3142
|
+
}
|
|
3143
|
+
if (typeof document !== "undefined") {
|
|
3144
|
+
document.querySelectorAll("audio, video").forEach((el) => {
|
|
3145
|
+
this.trackElement(el);
|
|
3146
|
+
});
|
|
3147
|
+
this.mutationObserver = new MutationObserver((mutations) => {
|
|
3148
|
+
for (const m of mutations) {
|
|
3149
|
+
m.addedNodes.forEach((node) => {
|
|
3150
|
+
if (node instanceof HTMLMediaElement) {
|
|
3151
|
+
this.trackElement(node);
|
|
3152
|
+
} else if (node instanceof HTMLElement) {
|
|
3153
|
+
node.querySelectorAll("audio, video").forEach((el) => {
|
|
3154
|
+
this.trackElement(el);
|
|
3155
|
+
});
|
|
3156
|
+
}
|
|
3157
|
+
});
|
|
3158
|
+
}
|
|
3159
|
+
});
|
|
3160
|
+
this.mutationObserver.observe(document.documentElement, {
|
|
3161
|
+
childList: true,
|
|
3162
|
+
subtree: true
|
|
3163
|
+
});
|
|
3164
|
+
}
|
|
3165
|
+
this.originalMutedDescriptor = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, "muted") ?? null;
|
|
3166
|
+
const original = this.originalMutedDescriptor;
|
|
3167
|
+
if (original?.get && original?.set) {
|
|
3168
|
+
((manager) => {
|
|
3169
|
+
Object.defineProperty(HTMLMediaElement.prototype, "muted", {
|
|
3170
|
+
configurable: true,
|
|
3171
|
+
get() {
|
|
3172
|
+
const intended = manager.intendedMuted.get(this);
|
|
3173
|
+
return intended !== void 0 ? intended : original.get.call(this);
|
|
3174
|
+
},
|
|
3175
|
+
set(value) {
|
|
3176
|
+
manager.intendedMuted.set(this, value);
|
|
3177
|
+
manager.elements.add(this);
|
|
3178
|
+
original.set.call(this, manager._isMuted ? true : value);
|
|
3179
|
+
}
|
|
3180
|
+
});
|
|
3181
|
+
})(this);
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
shimAudioContextClass(Original) {
|
|
3185
|
+
return /* @__PURE__ */ ((manager) => class extends Original {
|
|
3186
|
+
constructor(opts) {
|
|
3187
|
+
super(opts);
|
|
3188
|
+
const masterGain = this.createGain();
|
|
3189
|
+
masterGain.connect(this.destination);
|
|
3190
|
+
masterGain.gain.setValueAtTime(
|
|
3191
|
+
manager._isMuted ? 0 : 1,
|
|
3192
|
+
this.currentTime
|
|
3193
|
+
);
|
|
3194
|
+
Object.defineProperty(this, "destination", {
|
|
3195
|
+
configurable: true,
|
|
3196
|
+
get() {
|
|
3197
|
+
return masterGain;
|
|
3198
|
+
}
|
|
3199
|
+
});
|
|
3200
|
+
manager.contexts.set(this, masterGain);
|
|
3201
|
+
}
|
|
3202
|
+
close() {
|
|
3203
|
+
manager.contexts.delete(this);
|
|
3204
|
+
return super.close();
|
|
3205
|
+
}
|
|
3206
|
+
})(this);
|
|
3207
|
+
}
|
|
3208
|
+
destroy() {
|
|
3209
|
+
this.sdk.iframeMessenger.removeEventListener(
|
|
3210
|
+
IFRAME_MESSAGE_TYPE5.MUTE_CHANGED,
|
|
3211
|
+
this.handleMute
|
|
3212
|
+
);
|
|
3213
|
+
if (this.mutationObserver) {
|
|
3214
|
+
this.mutationObserver.disconnect();
|
|
3215
|
+
this.mutationObserver = null;
|
|
3216
|
+
}
|
|
3217
|
+
if (typeof window !== "undefined") {
|
|
3218
|
+
if (this.originalAudioContext) {
|
|
3219
|
+
window.AudioContext = this.originalAudioContext;
|
|
3220
|
+
}
|
|
3221
|
+
const win = window;
|
|
3222
|
+
if (this.originalWebKitAudioContext && win.webkitAudioContext) {
|
|
3223
|
+
win.webkitAudioContext = this.originalWebKitAudioContext;
|
|
3224
|
+
}
|
|
3225
|
+
if (this.originalAudio) {
|
|
3226
|
+
window.Audio = this.originalAudio;
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
if (this.originalMutedDescriptor) {
|
|
3230
|
+
Object.defineProperty(
|
|
3231
|
+
HTMLMediaElement.prototype,
|
|
3232
|
+
"muted",
|
|
3233
|
+
this.originalMutedDescriptor
|
|
3234
|
+
);
|
|
3235
|
+
}
|
|
3236
|
+
this.contexts.clear();
|
|
3237
|
+
this.elements.clear();
|
|
3238
|
+
this.intendedMuted = /* @__PURE__ */ new WeakMap();
|
|
3239
|
+
super.destroy();
|
|
3240
|
+
}
|
|
3241
|
+
};
|
|
3242
|
+
|
|
3031
3243
|
// src/services/friends.ts
|
|
3032
3244
|
import { api as api8 } from "@wvdsh/api";
|
|
3033
3245
|
|
|
@@ -3289,7 +3501,7 @@ var SwMessenger = class {
|
|
|
3289
3501
|
};
|
|
3290
3502
|
|
|
3291
3503
|
// src/index.ts
|
|
3292
|
-
import { IFRAME_MESSAGE_TYPE as
|
|
3504
|
+
import { IFRAME_MESSAGE_TYPE as IFRAME_MESSAGE_TYPE6, UrlParams } from "@wvdsh/api";
|
|
3293
3505
|
|
|
3294
3506
|
// src/utils/validation.ts
|
|
3295
3507
|
var CONVEX_ID_REGEX = /^[0-9a-z]{31,37}$/;
|
|
@@ -3333,6 +3545,13 @@ var vRecord = (value, path) => {
|
|
|
3333
3545
|
`${path}: expected plain object, got ${describeValue(value)}`
|
|
3334
3546
|
);
|
|
3335
3547
|
}
|
|
3548
|
+
for (const [key, val] of Object.entries(value)) {
|
|
3549
|
+
if (typeof val === "object" && val !== null) {
|
|
3550
|
+
throw new Error(
|
|
3551
|
+
`${path}: expected flat record with no nested objects, but key "${key}" contains ${describeValue(val)}`
|
|
3552
|
+
);
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3336
3555
|
return value;
|
|
3337
3556
|
};
|
|
3338
3557
|
function vId(tableName) {
|
|
@@ -3377,7 +3596,12 @@ function vUnion(...variants) {
|
|
|
3377
3596
|
}
|
|
3378
3597
|
function vObject(shape) {
|
|
3379
3598
|
return (value, path) => {
|
|
3380
|
-
|
|
3599
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3600
|
+
throw new Error(
|
|
3601
|
+
`${path}: expected plain object, got ${describeValue(value)}`
|
|
3602
|
+
);
|
|
3603
|
+
}
|
|
3604
|
+
const obj = value;
|
|
3381
3605
|
for (const key of Object.keys(obj)) {
|
|
3382
3606
|
if (!(key in shape)) {
|
|
3383
3607
|
throw new Error(`${path}: unrecognized property "${key}"`);
|
|
@@ -3468,6 +3692,7 @@ var WavedashSDK = class extends EventTarget {
|
|
|
3468
3692
|
this.gameEventManager = new GameEventManager(this);
|
|
3469
3693
|
this.fullscreenManager = new FullscreenManager(this);
|
|
3470
3694
|
this.overlayManager = new OverlayManager(this);
|
|
3695
|
+
this.audioManager = new AudioManager(this);
|
|
3471
3696
|
this.managers = [
|
|
3472
3697
|
this.p2pManager,
|
|
3473
3698
|
this.lobbyManager,
|
|
@@ -3479,7 +3704,8 @@ var WavedashSDK = class extends EventTarget {
|
|
|
3479
3704
|
this.friendsManager,
|
|
3480
3705
|
this.gameEventManager,
|
|
3481
3706
|
this.fullscreenManager,
|
|
3482
|
-
this.overlayManager
|
|
3707
|
+
this.overlayManager,
|
|
3708
|
+
this.audioManager
|
|
3483
3709
|
];
|
|
3484
3710
|
this.friendsManager.cacheUsers([
|
|
3485
3711
|
{
|
|
@@ -3611,7 +3837,7 @@ var WavedashSDK = class extends EventTarget {
|
|
|
3611
3837
|
[progress]
|
|
3612
3838
|
);
|
|
3613
3839
|
this.clearSetupWarning();
|
|
3614
|
-
iframeMessenger.postToParent(
|
|
3840
|
+
iframeMessenger.postToParent(IFRAME_MESSAGE_TYPE6.PROGRESS_UPDATE, {
|
|
3615
3841
|
progress
|
|
3616
3842
|
});
|
|
3617
3843
|
}
|
|
@@ -3620,7 +3846,7 @@ var WavedashSDK = class extends EventTarget {
|
|
|
3620
3846
|
if (this.gameFinishedLoading) return;
|
|
3621
3847
|
this.gameFinishedLoading = true;
|
|
3622
3848
|
this.heartbeatManager.start();
|
|
3623
|
-
iframeMessenger.postToParent(
|
|
3849
|
+
iframeMessenger.postToParent(IFRAME_MESSAGE_TYPE6.LOADING_COMPLETE, {});
|
|
3624
3850
|
this.overlayManager.takeFocus();
|
|
3625
3851
|
}
|
|
3626
3852
|
get gameLoaded() {
|
|
@@ -4368,15 +4594,34 @@ var WavedashSDK = class extends EventTarget {
|
|
|
4368
4594
|
// User Presence
|
|
4369
4595
|
// ==============================
|
|
4370
4596
|
/**
|
|
4371
|
-
* Updates rich user presence so friends can see what the player is doing in game
|
|
4372
|
-
*
|
|
4597
|
+
* Updates rich user presence so friends can see what the player is doing in game.
|
|
4598
|
+
* Supported keys:
|
|
4599
|
+
* `status` — one-line activity shown as the primary line (e.g. "Traveling in a group")
|
|
4600
|
+
* `details` — secondary context shown beneath the status (e.g. current zone or mode)
|
|
4601
|
+
*
|
|
4602
|
+
* Pass an empty dictionary to clear all presence fields.
|
|
4603
|
+
* @param data Presence fields to update.
|
|
4373
4604
|
* @returns true if the presence was updated successfully
|
|
4374
4605
|
*/
|
|
4375
4606
|
async updateUserPresence(data) {
|
|
4607
|
+
if (typeof data === "string") {
|
|
4608
|
+
const raw = data;
|
|
4609
|
+
try {
|
|
4610
|
+
data = JSON.parse(raw);
|
|
4611
|
+
} catch (error) {
|
|
4612
|
+
const message = `updateUserPresence: invalid JSON: ${raw}`;
|
|
4613
|
+
logger.error(message, error);
|
|
4614
|
+
return this.formatResponse({
|
|
4615
|
+
success: false,
|
|
4616
|
+
data: null,
|
|
4617
|
+
message
|
|
4618
|
+
});
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4376
4621
|
return this.apiCall(
|
|
4377
4622
|
this.heartbeatManager,
|
|
4378
4623
|
"updateUserPresence",
|
|
4379
|
-
[["data",
|
|
4624
|
+
[["data", vRecord]],
|
|
4380
4625
|
data
|
|
4381
4626
|
);
|
|
4382
4627
|
}
|
|
@@ -4467,7 +4712,7 @@ var WavedashSDK = class extends EventTarget {
|
|
|
4467
4712
|
throw new Error(`Failed to refresh gameplay token: ${response.status}`);
|
|
4468
4713
|
}
|
|
4469
4714
|
this.gameplayJwt = await response.text();
|
|
4470
|
-
iframeMessenger.postToParent(
|
|
4715
|
+
iframeMessenger.postToParent(IFRAME_MESSAGE_TYPE6.GAMEPLAY_JWT_READY, {
|
|
4471
4716
|
gameplayJwt: this.gameplayJwt
|
|
4472
4717
|
});
|
|
4473
4718
|
this.swMessenger.postToServiceWorker({
|
|
@@ -4503,7 +4748,7 @@ var WavedashSDK = class extends EventTarget {
|
|
|
4503
4748
|
}
|
|
4504
4749
|
setupSessionEndListeners() {
|
|
4505
4750
|
iframeMessenger.addEventListener(
|
|
4506
|
-
|
|
4751
|
+
IFRAME_MESSAGE_TYPE6.END_SESSION,
|
|
4507
4752
|
() => this.destroy()
|
|
4508
4753
|
);
|
|
4509
4754
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wvdsh/sdk-js",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Wavedash JavaScript SDK",
|
|
6
6
|
"main": "./dist/client.js",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"typescript-eslint": "^8.52.0"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@wvdsh/api": "^0.1.
|
|
53
|
-
"convex": "^1.
|
|
52
|
+
"@wvdsh/api": "^0.1.28",
|
|
53
|
+
"convex": "^1.39.1",
|
|
54
54
|
"lodash.throttle": "^4.1.1"
|
|
55
55
|
}
|
|
56
56
|
}
|