@wvdsh/sdk-js 1.3.11 → 1.3.13
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 +69 -17
- package/dist/index.js +356 -31
- 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, 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, RemoteFileMetadata, StatsStoredPayload, UGCType, UGCVisibility, 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, 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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ConvexClient } from 'convex/browser';
|
|
2
2
|
import { GenericId } from 'convex/values';
|
|
3
3
|
export { GenericId as Id } from 'convex/values';
|
|
4
|
-
import { FunctionReturnType } from 'convex/server';
|
|
4
|
+
import { FunctionReturnType, FunctionArgs } from 'convex/server';
|
|
5
5
|
import { LOBBY_VISIBILITY, api, UGC_TYPE, UGC_VISIBILITY, LEADERBOARD_SORT_ORDER, LEADERBOARD_DISPLAY_TYPE, GAME_ENGINE, SDKUser, IFrameEventPayloadMap, IFRAME_MESSAGE_TYPE, SDKConfig, GameLaunchParams } from '@wvdsh/api';
|
|
6
6
|
export { GameLaunchParams } from '@wvdsh/api';
|
|
7
7
|
|
|
@@ -71,6 +71,13 @@ type LeaderboardSortOrder = (typeof LEADERBOARD_SORT_ORDER)[keyof typeof LEADERB
|
|
|
71
71
|
type LeaderboardDisplayType = (typeof LEADERBOARD_DISPLAY_TYPE)[keyof typeof LEADERBOARD_DISPLAY_TYPE];
|
|
72
72
|
type UGCType = (typeof UGC_TYPE)[keyof typeof UGC_TYPE];
|
|
73
73
|
type UGCVisibility = (typeof UGC_VISIBILITY)[keyof typeof UGC_VISIBILITY];
|
|
74
|
+
type UpdateUGCItemArgs = Omit<FunctionArgs<typeof api.sdk.userGeneratedContent.updateUGCItem>, "ugcId" | "createPresignedUploadUrl"> & {
|
|
75
|
+
filePath?: string;
|
|
76
|
+
};
|
|
77
|
+
type UGCItem = FunctionReturnType<typeof api.sdk.userGeneratedContent.listUGCItems>["page"][0];
|
|
78
|
+
type PaginatedUGCItems = FunctionReturnType<typeof api.sdk.userGeneratedContent.listUGCItems>;
|
|
79
|
+
type RawListUGCItemsArgs = FunctionArgs<typeof api.sdk.userGeneratedContent.listUGCItems>;
|
|
80
|
+
type ListUGCItemsArgs = Omit<RawListUGCItemsArgs, "filters"> & NonNullable<RawListUGCItemsArgs["filters"]>;
|
|
74
81
|
type LobbyUser = FunctionReturnType<typeof api.sdk.gameLobby.lobbyUsers>[0];
|
|
75
82
|
type LobbyMessage = FunctionReturnType<typeof api.sdk.gameLobby.lobbyMessages>[0];
|
|
76
83
|
type Lobby = FunctionReturnType<typeof api.sdk.gameLobby.listAvailable>[0];
|
|
@@ -429,9 +436,10 @@ declare class FileSystemManager extends WavedashManager {
|
|
|
429
436
|
declare class UGCManager extends WavedashManager {
|
|
430
437
|
constructor(sdk: WavedashSDK);
|
|
431
438
|
createUGCItem(ugcType: UGCType, title?: string, description?: string, visibility?: UGCVisibility, filePath?: string): Promise<GenericId<"userGeneratedContent">>;
|
|
432
|
-
updateUGCItem(ugcId: GenericId<"userGeneratedContent">,
|
|
439
|
+
updateUGCItem(ugcId: GenericId<"userGeneratedContent">, updates?: UpdateUGCItemArgs): Promise<GenericId<"userGeneratedContent">>;
|
|
433
440
|
deleteUGCItem(ugcId: GenericId<"userGeneratedContent">): Promise<GenericId<"userGeneratedContent">>;
|
|
434
441
|
downloadUGCItem(ugcId: GenericId<"userGeneratedContent">, filePath: string): Promise<GenericId<"userGeneratedContent">>;
|
|
442
|
+
listUGCItems(args?: ListUGCItemsArgs): Promise<PaginatedUGCItems>;
|
|
435
443
|
}
|
|
436
444
|
|
|
437
445
|
/**
|
|
@@ -619,6 +627,7 @@ declare class HeartbeatManager extends WavedashManager {
|
|
|
619
627
|
private isFirstTick;
|
|
620
628
|
private readonly TEST_CONNECTION_INTERVAL_MS;
|
|
621
629
|
private readonly DISCONNECTED_TIMEOUT_MS;
|
|
630
|
+
private cachedPresenceData;
|
|
622
631
|
constructor(sdk: WavedashSDK);
|
|
623
632
|
/** Start heartbeat and connection-check intervals */
|
|
624
633
|
start(): void;
|
|
@@ -630,11 +639,11 @@ declare class HeartbeatManager extends WavedashManager {
|
|
|
630
639
|
private tickHeartbeat;
|
|
631
640
|
private sendHeartbeat;
|
|
632
641
|
/**
|
|
633
|
-
* Updates user presence in the backend
|
|
642
|
+
* Updates user presence in the backend.
|
|
634
643
|
* @param data - Data to send to the backend
|
|
635
644
|
* @returns true if the presence was updated successfully
|
|
636
645
|
*/
|
|
637
|
-
updateUserPresence(data
|
|
646
|
+
updateUserPresence(data: Record<string, string | number | boolean | null>): Promise<boolean>;
|
|
638
647
|
/**
|
|
639
648
|
* Tests the connection to the backend
|
|
640
649
|
*/
|
|
@@ -705,6 +714,47 @@ declare class OverlayManager extends WavedashManager {
|
|
|
705
714
|
private handleKeyDown;
|
|
706
715
|
}
|
|
707
716
|
|
|
717
|
+
/**
|
|
718
|
+
* AudioManager
|
|
719
|
+
*
|
|
720
|
+
* Mutes & unmutes the game in response to MUTE_CHANGED iframe messages, without
|
|
721
|
+
* the game needing to know anything about it.
|
|
722
|
+
*
|
|
723
|
+
* Web Audio: subclass `AudioContext` so `ctx.destination` resolves to a master
|
|
724
|
+
* GainNode that we control. The master gain wires to the real native destination,
|
|
725
|
+
* so `node.connect(ctx.destination)` and any other game code is unaffected.
|
|
726
|
+
*
|
|
727
|
+
* HTML Media (`<audio>`/`<video>`): override `HTMLMediaElement.prototype.muted`
|
|
728
|
+
* to record the game's intended state, but write `true` to the underlying element
|
|
729
|
+
* whenever the SDK is muted. Tracked elements come from three sources:
|
|
730
|
+
* 1. Pre-existing DOM media (`querySelectorAll`)
|
|
731
|
+
* 2. `new Audio()` constructor shim (covers detached SFX)
|
|
732
|
+
* 3. MutationObserver for any media added to the DOM later (covers innerHTML,
|
|
733
|
+
* framework rendering, createElement + append, etc.)
|
|
734
|
+
*/
|
|
735
|
+
declare class AudioManager extends WavedashManager {
|
|
736
|
+
private _isMuted;
|
|
737
|
+
private contexts;
|
|
738
|
+
private elements;
|
|
739
|
+
private intendedMuted;
|
|
740
|
+
private originalAudioContext;
|
|
741
|
+
private originalWebKitAudioContext;
|
|
742
|
+
private originalAudio;
|
|
743
|
+
private originalMutedDescriptor;
|
|
744
|
+
private mutationObserver;
|
|
745
|
+
constructor(sdk: WavedashSDK);
|
|
746
|
+
isMuted(): boolean;
|
|
747
|
+
private handleMute;
|
|
748
|
+
/**
|
|
749
|
+
* Track a media element and (if SDK is currently muted) silence it.
|
|
750
|
+
* Idempotent — safe to call multiple times for the same element.
|
|
751
|
+
*/
|
|
752
|
+
private trackElement;
|
|
753
|
+
private installShims;
|
|
754
|
+
private shimAudioContextClass;
|
|
755
|
+
destroy(): void;
|
|
756
|
+
}
|
|
757
|
+
|
|
708
758
|
/**
|
|
709
759
|
* Friends service
|
|
710
760
|
*
|
|
@@ -848,7 +898,6 @@ declare class WavedashSDK extends EventTarget {
|
|
|
848
898
|
};
|
|
849
899
|
UGCVisibility: {
|
|
850
900
|
readonly PUBLIC: 0;
|
|
851
|
-
readonly FRIENDS_ONLY: 1;
|
|
852
901
|
readonly PRIVATE: 2;
|
|
853
902
|
};
|
|
854
903
|
AvatarSize: {
|
|
@@ -891,6 +940,7 @@ declare class WavedashSDK extends EventTarget {
|
|
|
891
940
|
p2pManager: P2PManager;
|
|
892
941
|
fullscreenManager: FullscreenManager;
|
|
893
942
|
overlayManager: OverlayManager;
|
|
943
|
+
audioManager: AudioManager;
|
|
894
944
|
private managers;
|
|
895
945
|
private gameplayJwt;
|
|
896
946
|
private gameplayJwtPromise;
|
|
@@ -998,21 +1048,19 @@ declare class WavedashSDK extends EventTarget {
|
|
|
998
1048
|
createUGCItem(ugcType: UGCType, title?: string, description?: string, visibility?: UGCVisibility, filePath?: string): Promise<WavedashResponse<GenericId<"userGeneratedContent">>>;
|
|
999
1049
|
/**
|
|
1000
1050
|
* Updates a UGC item and uploads the file to the server if a filePath is provided
|
|
1001
|
-
*
|
|
1002
|
-
* @param
|
|
1003
|
-
*
|
|
1004
|
-
* @param description
|
|
1005
|
-
* @param visibility
|
|
1006
|
-
* @param filePath - optional IndexedDB key file path to upload to the server. If not provided, the UGC item will be updated but no file will be uploaded.
|
|
1051
|
+
* @param ugcId - The ID of the UGC item to update
|
|
1052
|
+
* @param updates - Object containing the fields to update. May also be passed
|
|
1053
|
+
* as a JSON string by engine bridges (Godot) that can't marshal a dict.
|
|
1007
1054
|
* @returns ugcId
|
|
1008
1055
|
*/
|
|
1009
|
-
updateUGCItem(ugcId: GenericId<"userGeneratedContent">,
|
|
1056
|
+
updateUGCItem(ugcId: GenericId<"userGeneratedContent">, updates?: UpdateUGCItemArgs): Promise<WavedashResponse<GenericId<"userGeneratedContent">>>;
|
|
1010
1057
|
/**
|
|
1011
1058
|
* Delete a UGC item: removes the row, the R2 object, and frees up the
|
|
1012
1059
|
* user's storage quota by the size of the deleted upload.
|
|
1013
1060
|
*/
|
|
1014
1061
|
deleteUGCItem(ugcId: GenericId<"userGeneratedContent">): Promise<WavedashResponse<GenericId<"userGeneratedContent">>>;
|
|
1015
1062
|
downloadUGCItem(ugcId: GenericId<"userGeneratedContent">, filePath: string): Promise<WavedashResponse<GenericId<"userGeneratedContent">>>;
|
|
1063
|
+
listUGCItems(args?: ListUGCItemsArgs): Promise<WavedashResponse<PaginatedUGCItems>>;
|
|
1016
1064
|
/**
|
|
1017
1065
|
* Deletes a remote file from storage
|
|
1018
1066
|
* @param filePath - The path of the remote file to delete
|
|
@@ -1157,12 +1205,16 @@ declare class WavedashSDK extends EventTarget {
|
|
|
1157
1205
|
inviteUserToLobby(lobbyId: GenericId<"lobbies">, userId: GenericId<"users">): Promise<WavedashResponse<boolean>>;
|
|
1158
1206
|
getLobbyInviteLink(copyToClipboard?: boolean): Promise<WavedashResponse<string>>;
|
|
1159
1207
|
/**
|
|
1160
|
-
* Updates rich user presence so friends can see what the player is doing in game
|
|
1161
|
-
*
|
|
1162
|
-
*
|
|
1208
|
+
* Updates rich user presence so friends can see what the player is doing in game.
|
|
1209
|
+
* Supported keys:
|
|
1210
|
+
* `status` — one-line activity shown as the primary line (e.g. "Traveling in a group")
|
|
1211
|
+
* `details` — secondary context shown beneath the status (e.g. current zone or mode)
|
|
1212
|
+
*
|
|
1213
|
+
* Pass an empty dictionary to clear all presence fields.
|
|
1214
|
+
* @param data Presence fields to update.
|
|
1163
1215
|
* @returns true if the presence was updated successfully
|
|
1164
1216
|
*/
|
|
1165
|
-
updateUserPresence(data
|
|
1217
|
+
updateUserPresence(data: Record<string, string | number | boolean | null>): Promise<WavedashResponse<boolean>>;
|
|
1166
1218
|
private isGodot;
|
|
1167
1219
|
private formatResponse;
|
|
1168
1220
|
private ensureInit;
|
|
@@ -1209,4 +1261,4 @@ declare global {
|
|
|
1209
1261
|
|
|
1210
1262
|
declare function setupWavedashSDK(): WavedashSDK;
|
|
1211
1263
|
|
|
1212
|
-
export { type BackendConnectionPayload, type EngineInstance, type Friend, type FullscreenChangedPayload, type Leaderboard, type LeaderboardDisplayType, type LeaderboardEntries, type LeaderboardSortOrder, 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 RemoteFileMetadata, type StatsStoredPayload, type UGCType, type UGCVisibility, type UpsertedLeaderboardEntry, type WavedashConfig, type WavedashEvent, type WavedashEventMap, type WavedashResponse, WavedashSDK, setupWavedashSDK };
|
|
1264
|
+
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 };
|
package/dist/index.js
CHANGED
|
@@ -1033,7 +1033,8 @@ var UGCManager = class extends WavedashManager {
|
|
|
1033
1033
|
}
|
|
1034
1034
|
return ugcId;
|
|
1035
1035
|
}
|
|
1036
|
-
async updateUGCItem(ugcId,
|
|
1036
|
+
async updateUGCItem(ugcId, updates = {}) {
|
|
1037
|
+
const { title, description, visibility, filePath } = updates;
|
|
1037
1038
|
const { uploadUrl } = await this.sdk.convexClient.mutation(
|
|
1038
1039
|
api3.sdk.userGeneratedContent.updateUGCItem,
|
|
1039
1040
|
{
|
|
@@ -1079,6 +1080,18 @@ var UGCManager = class extends WavedashManager {
|
|
|
1079
1080
|
}
|
|
1080
1081
|
return ugcId;
|
|
1081
1082
|
}
|
|
1083
|
+
async listUGCItems(args = {}) {
|
|
1084
|
+
const { createdBy, ugcType, titleSearch, numItems, continueCursor } = args;
|
|
1085
|
+
const filters = createdBy !== void 0 || ugcType !== void 0 || titleSearch !== void 0 ? { createdBy, ugcType, titleSearch } : void 0;
|
|
1086
|
+
return await this.sdk.convexClient.query(
|
|
1087
|
+
api3.sdk.userGeneratedContent.listUGCItems,
|
|
1088
|
+
{
|
|
1089
|
+
filters,
|
|
1090
|
+
numItems,
|
|
1091
|
+
continueCursor
|
|
1092
|
+
}
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1082
1095
|
};
|
|
1083
1096
|
|
|
1084
1097
|
// src/services/leaderboards.ts
|
|
@@ -2696,6 +2709,7 @@ var HeartbeatManager = class extends WavedashManager {
|
|
|
2696
2709
|
this.isFirstTick = true;
|
|
2697
2710
|
this.TEST_CONNECTION_INTERVAL_MS = 1e3;
|
|
2698
2711
|
this.DISCONNECTED_TIMEOUT_MS = 9e4;
|
|
2712
|
+
this.cachedPresenceData = {};
|
|
2699
2713
|
this.handleVisibilityChange = () => {
|
|
2700
2714
|
if (document.visibilityState === "visible") {
|
|
2701
2715
|
this.start();
|
|
@@ -2765,7 +2779,7 @@ var HeartbeatManager = class extends WavedashManager {
|
|
|
2765
2779
|
this.heartbeatInFlight = true;
|
|
2766
2780
|
this.sdk.convexClient.mutation(api7.sdk.presence.heartbeat, {
|
|
2767
2781
|
...reestablish ? {
|
|
2768
|
-
data:
|
|
2782
|
+
data: this.cachedPresenceData,
|
|
2769
2783
|
deviceFingerprint: this.deviceFingerprint
|
|
2770
2784
|
} : {}
|
|
2771
2785
|
}).then((accepted) => {
|
|
@@ -2779,15 +2793,15 @@ var HeartbeatManager = class extends WavedashManager {
|
|
|
2779
2793
|
});
|
|
2780
2794
|
}
|
|
2781
2795
|
/**
|
|
2782
|
-
* Updates user presence in the backend
|
|
2796
|
+
* Updates user presence in the backend.
|
|
2783
2797
|
* @param data - Data to send to the backend
|
|
2784
2798
|
* @returns true if the presence was updated successfully
|
|
2785
2799
|
*/
|
|
2786
2800
|
async updateUserPresence(data) {
|
|
2787
2801
|
try {
|
|
2788
|
-
|
|
2802
|
+
this.cachedPresenceData = data;
|
|
2789
2803
|
await this.sdk.convexClient.mutation(api7.sdk.presence.heartbeat, {
|
|
2790
|
-
data
|
|
2804
|
+
data,
|
|
2791
2805
|
deviceFingerprint: this.deviceFingerprint
|
|
2792
2806
|
});
|
|
2793
2807
|
return true;
|
|
@@ -3015,6 +3029,211 @@ var OverlayManager = class extends WavedashManager {
|
|
|
3015
3029
|
}
|
|
3016
3030
|
};
|
|
3017
3031
|
|
|
3032
|
+
// src/services/audio.ts
|
|
3033
|
+
import { IFRAME_MESSAGE_TYPE as IFRAME_MESSAGE_TYPE5 } from "@wvdsh/api";
|
|
3034
|
+
var WeakRefSet = class {
|
|
3035
|
+
constructor() {
|
|
3036
|
+
this.set = /* @__PURE__ */ new Set();
|
|
3037
|
+
}
|
|
3038
|
+
add(value) {
|
|
3039
|
+
for (const ref of this.set) {
|
|
3040
|
+
if (ref.deref() === value) return;
|
|
3041
|
+
}
|
|
3042
|
+
this.set.add(new WeakRef(value));
|
|
3043
|
+
}
|
|
3044
|
+
forEach(callback) {
|
|
3045
|
+
for (const ref of this.set) {
|
|
3046
|
+
const v = ref.deref();
|
|
3047
|
+
if (v === void 0) this.set.delete(ref);
|
|
3048
|
+
else callback(v);
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
clear() {
|
|
3052
|
+
this.set.clear();
|
|
3053
|
+
}
|
|
3054
|
+
};
|
|
3055
|
+
var AudioManager = class extends WavedashManager {
|
|
3056
|
+
constructor(sdk) {
|
|
3057
|
+
super(sdk);
|
|
3058
|
+
this._isMuted = false;
|
|
3059
|
+
// Web Audio contexts and their master gain nodes
|
|
3060
|
+
this.contexts = /* @__PURE__ */ new Map();
|
|
3061
|
+
// HTML media elements we know about + their game-intended muted state
|
|
3062
|
+
this.elements = new WeakRefSet();
|
|
3063
|
+
this.intendedMuted = /* @__PURE__ */ new WeakMap();
|
|
3064
|
+
// Originals (restored on destroy)
|
|
3065
|
+
this.originalAudioContext = null;
|
|
3066
|
+
this.originalWebKitAudioContext = null;
|
|
3067
|
+
this.originalAudio = null;
|
|
3068
|
+
this.originalMutedDescriptor = null;
|
|
3069
|
+
this.mutationObserver = null;
|
|
3070
|
+
this.handleMute = (data) => {
|
|
3071
|
+
if (this._isMuted === data.isMuted) return;
|
|
3072
|
+
this._isMuted = data.isMuted;
|
|
3073
|
+
logger.debug(`[AudioManager] muted=${this._isMuted}`);
|
|
3074
|
+
const target = this._isMuted ? 0 : 1;
|
|
3075
|
+
this.contexts.forEach((gain, ctx) => {
|
|
3076
|
+
const now = ctx.currentTime;
|
|
3077
|
+
gain.gain.cancelScheduledValues(now);
|
|
3078
|
+
gain.gain.setValueAtTime(gain.gain.value, now);
|
|
3079
|
+
gain.gain.linearRampToValueAtTime(target, now + 0.05);
|
|
3080
|
+
});
|
|
3081
|
+
const setMutedNative = this.originalMutedDescriptor?.set;
|
|
3082
|
+
if (setMutedNative) {
|
|
3083
|
+
this.elements.forEach((el) => {
|
|
3084
|
+
const intended = this.intendedMuted.get(el) ?? false;
|
|
3085
|
+
setMutedNative.call(el, this._isMuted ? true : intended);
|
|
3086
|
+
});
|
|
3087
|
+
}
|
|
3088
|
+
};
|
|
3089
|
+
this.installShims();
|
|
3090
|
+
this.sdk.iframeMessenger.addEventListener(
|
|
3091
|
+
IFRAME_MESSAGE_TYPE5.MUTE_CHANGED,
|
|
3092
|
+
this.handleMute
|
|
3093
|
+
);
|
|
3094
|
+
}
|
|
3095
|
+
isMuted() {
|
|
3096
|
+
return this._isMuted;
|
|
3097
|
+
}
|
|
3098
|
+
/**
|
|
3099
|
+
* Track a media element and (if SDK is currently muted) silence it.
|
|
3100
|
+
* Idempotent — safe to call multiple times for the same element.
|
|
3101
|
+
*/
|
|
3102
|
+
trackElement(el) {
|
|
3103
|
+
if (this.intendedMuted.has(el)) return;
|
|
3104
|
+
const getMuted = this.originalMutedDescriptor?.get;
|
|
3105
|
+
const setMuted = this.originalMutedDescriptor?.set;
|
|
3106
|
+
const current = getMuted ? getMuted.call(el) : el.muted;
|
|
3107
|
+
this.intendedMuted.set(el, current);
|
|
3108
|
+
this.elements.add(el);
|
|
3109
|
+
if (this._isMuted && !current && setMuted) {
|
|
3110
|
+
setMuted.call(el, true);
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
installShims() {
|
|
3114
|
+
if (typeof window === "undefined") return;
|
|
3115
|
+
if (window.AudioContext) {
|
|
3116
|
+
this.originalAudioContext = window.AudioContext;
|
|
3117
|
+
window.AudioContext = this.shimAudioContextClass(window.AudioContext);
|
|
3118
|
+
}
|
|
3119
|
+
const win = window;
|
|
3120
|
+
if (win.webkitAudioContext) {
|
|
3121
|
+
this.originalWebKitAudioContext = win.webkitAudioContext;
|
|
3122
|
+
win.webkitAudioContext = this.shimAudioContextClass(win.webkitAudioContext);
|
|
3123
|
+
}
|
|
3124
|
+
if (window.Audio) {
|
|
3125
|
+
const OriginalAudio = window.Audio;
|
|
3126
|
+
this.originalAudio = OriginalAudio;
|
|
3127
|
+
((manager) => {
|
|
3128
|
+
const Shimmed = function(src) {
|
|
3129
|
+
const audio = new OriginalAudio(src);
|
|
3130
|
+
manager.trackElement(audio);
|
|
3131
|
+
return audio;
|
|
3132
|
+
};
|
|
3133
|
+
Shimmed.prototype = OriginalAudio.prototype;
|
|
3134
|
+
window.Audio = Shimmed;
|
|
3135
|
+
})(this);
|
|
3136
|
+
}
|
|
3137
|
+
if (typeof document !== "undefined") {
|
|
3138
|
+
document.querySelectorAll("audio, video").forEach((el) => {
|
|
3139
|
+
this.trackElement(el);
|
|
3140
|
+
});
|
|
3141
|
+
this.mutationObserver = new MutationObserver((mutations) => {
|
|
3142
|
+
for (const m of mutations) {
|
|
3143
|
+
m.addedNodes.forEach((node) => {
|
|
3144
|
+
if (node instanceof HTMLMediaElement) {
|
|
3145
|
+
this.trackElement(node);
|
|
3146
|
+
} else if (node instanceof HTMLElement) {
|
|
3147
|
+
node.querySelectorAll("audio, video").forEach((el) => {
|
|
3148
|
+
this.trackElement(el);
|
|
3149
|
+
});
|
|
3150
|
+
}
|
|
3151
|
+
});
|
|
3152
|
+
}
|
|
3153
|
+
});
|
|
3154
|
+
this.mutationObserver.observe(document.documentElement, {
|
|
3155
|
+
childList: true,
|
|
3156
|
+
subtree: true
|
|
3157
|
+
});
|
|
3158
|
+
}
|
|
3159
|
+
this.originalMutedDescriptor = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, "muted") ?? null;
|
|
3160
|
+
const original = this.originalMutedDescriptor;
|
|
3161
|
+
if (original?.get && original?.set) {
|
|
3162
|
+
((manager) => {
|
|
3163
|
+
Object.defineProperty(HTMLMediaElement.prototype, "muted", {
|
|
3164
|
+
configurable: true,
|
|
3165
|
+
get() {
|
|
3166
|
+
const intended = manager.intendedMuted.get(this);
|
|
3167
|
+
return intended !== void 0 ? intended : original.get.call(this);
|
|
3168
|
+
},
|
|
3169
|
+
set(value) {
|
|
3170
|
+
manager.intendedMuted.set(this, value);
|
|
3171
|
+
manager.elements.add(this);
|
|
3172
|
+
original.set.call(this, manager._isMuted ? true : value);
|
|
3173
|
+
}
|
|
3174
|
+
});
|
|
3175
|
+
})(this);
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
shimAudioContextClass(Original) {
|
|
3179
|
+
return /* @__PURE__ */ ((manager) => class extends Original {
|
|
3180
|
+
constructor(opts) {
|
|
3181
|
+
super(opts);
|
|
3182
|
+
const masterGain = this.createGain();
|
|
3183
|
+
masterGain.connect(this.destination);
|
|
3184
|
+
masterGain.gain.setValueAtTime(
|
|
3185
|
+
manager._isMuted ? 0 : 1,
|
|
3186
|
+
this.currentTime
|
|
3187
|
+
);
|
|
3188
|
+
Object.defineProperty(this, "destination", {
|
|
3189
|
+
configurable: true,
|
|
3190
|
+
get() {
|
|
3191
|
+
return masterGain;
|
|
3192
|
+
}
|
|
3193
|
+
});
|
|
3194
|
+
manager.contexts.set(this, masterGain);
|
|
3195
|
+
}
|
|
3196
|
+
close() {
|
|
3197
|
+
manager.contexts.delete(this);
|
|
3198
|
+
return super.close();
|
|
3199
|
+
}
|
|
3200
|
+
})(this);
|
|
3201
|
+
}
|
|
3202
|
+
destroy() {
|
|
3203
|
+
this.sdk.iframeMessenger.removeEventListener(
|
|
3204
|
+
IFRAME_MESSAGE_TYPE5.MUTE_CHANGED,
|
|
3205
|
+
this.handleMute
|
|
3206
|
+
);
|
|
3207
|
+
if (this.mutationObserver) {
|
|
3208
|
+
this.mutationObserver.disconnect();
|
|
3209
|
+
this.mutationObserver = null;
|
|
3210
|
+
}
|
|
3211
|
+
if (typeof window !== "undefined") {
|
|
3212
|
+
if (this.originalAudioContext) {
|
|
3213
|
+
window.AudioContext = this.originalAudioContext;
|
|
3214
|
+
}
|
|
3215
|
+
const win = window;
|
|
3216
|
+
if (this.originalWebKitAudioContext && win.webkitAudioContext) {
|
|
3217
|
+
win.webkitAudioContext = this.originalWebKitAudioContext;
|
|
3218
|
+
}
|
|
3219
|
+
if (this.originalAudio) {
|
|
3220
|
+
window.Audio = this.originalAudio;
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
if (this.originalMutedDescriptor) {
|
|
3224
|
+
Object.defineProperty(
|
|
3225
|
+
HTMLMediaElement.prototype,
|
|
3226
|
+
"muted",
|
|
3227
|
+
this.originalMutedDescriptor
|
|
3228
|
+
);
|
|
3229
|
+
}
|
|
3230
|
+
this.contexts.clear();
|
|
3231
|
+
this.elements.clear();
|
|
3232
|
+
this.intendedMuted = /* @__PURE__ */ new WeakMap();
|
|
3233
|
+
super.destroy();
|
|
3234
|
+
}
|
|
3235
|
+
};
|
|
3236
|
+
|
|
3018
3237
|
// src/services/friends.ts
|
|
3019
3238
|
import { api as api8 } from "@wvdsh/api";
|
|
3020
3239
|
|
|
@@ -3276,7 +3495,7 @@ var SwMessenger = class {
|
|
|
3276
3495
|
};
|
|
3277
3496
|
|
|
3278
3497
|
// src/index.ts
|
|
3279
|
-
import { IFRAME_MESSAGE_TYPE as
|
|
3498
|
+
import { IFRAME_MESSAGE_TYPE as IFRAME_MESSAGE_TYPE6, UrlParams } from "@wvdsh/api";
|
|
3280
3499
|
|
|
3281
3500
|
// src/utils/validation.ts
|
|
3282
3501
|
var CONVEX_ID_REGEX = /^[0-9a-z]{31,37}$/;
|
|
@@ -3320,6 +3539,13 @@ var vRecord = (value, path) => {
|
|
|
3320
3539
|
`${path}: expected plain object, got ${describeValue(value)}`
|
|
3321
3540
|
);
|
|
3322
3541
|
}
|
|
3542
|
+
for (const [key, val] of Object.entries(value)) {
|
|
3543
|
+
if (typeof val === "object" && val !== null) {
|
|
3544
|
+
throw new Error(
|
|
3545
|
+
`${path}: expected flat record with no nested objects, but key "${key}" contains ${describeValue(val)}`
|
|
3546
|
+
);
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3323
3549
|
return value;
|
|
3324
3550
|
};
|
|
3325
3551
|
function vId(tableName) {
|
|
@@ -3362,11 +3588,34 @@ function vUnion(...variants) {
|
|
|
3362
3588
|
throw new Error(`${path}: no variant matched, got ${describeValue(value)}`);
|
|
3363
3589
|
};
|
|
3364
3590
|
}
|
|
3591
|
+
function vObject(shape) {
|
|
3592
|
+
return (value, path) => {
|
|
3593
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3594
|
+
throw new Error(
|
|
3595
|
+
`${path}: expected plain object, got ${describeValue(value)}`
|
|
3596
|
+
);
|
|
3597
|
+
}
|
|
3598
|
+
const obj = value;
|
|
3599
|
+
for (const key of Object.keys(obj)) {
|
|
3600
|
+
if (!(key in shape)) {
|
|
3601
|
+
throw new Error(`${path}: unrecognized property "${key}"`);
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
for (const key of Object.keys(shape)) {
|
|
3605
|
+
shape[key](obj[key], `${path}.${key}`);
|
|
3606
|
+
}
|
|
3607
|
+
return obj;
|
|
3608
|
+
};
|
|
3609
|
+
}
|
|
3365
3610
|
function validateArgs(methodName, specs, values) {
|
|
3611
|
+
const shape = {};
|
|
3612
|
+
const obj = {};
|
|
3366
3613
|
for (let i = 0; i < specs.length; i++) {
|
|
3367
3614
|
const [argName, validator] = specs[i];
|
|
3368
|
-
|
|
3615
|
+
shape[argName] = validator;
|
|
3616
|
+
obj[argName] = values[i];
|
|
3369
3617
|
}
|
|
3618
|
+
vObject(shape)(obj, methodName);
|
|
3370
3619
|
}
|
|
3371
3620
|
function describeValue(value) {
|
|
3372
3621
|
if (value === void 0) return "undefined";
|
|
@@ -3437,6 +3686,7 @@ var WavedashSDK = class extends EventTarget {
|
|
|
3437
3686
|
this.gameEventManager = new GameEventManager(this);
|
|
3438
3687
|
this.fullscreenManager = new FullscreenManager(this);
|
|
3439
3688
|
this.overlayManager = new OverlayManager(this);
|
|
3689
|
+
this.audioManager = new AudioManager(this);
|
|
3440
3690
|
this.managers = [
|
|
3441
3691
|
this.p2pManager,
|
|
3442
3692
|
this.lobbyManager,
|
|
@@ -3448,7 +3698,8 @@ var WavedashSDK = class extends EventTarget {
|
|
|
3448
3698
|
this.friendsManager,
|
|
3449
3699
|
this.gameEventManager,
|
|
3450
3700
|
this.fullscreenManager,
|
|
3451
|
-
this.overlayManager
|
|
3701
|
+
this.overlayManager,
|
|
3702
|
+
this.audioManager
|
|
3452
3703
|
];
|
|
3453
3704
|
this.friendsManager.cacheUsers([
|
|
3454
3705
|
{
|
|
@@ -3580,7 +3831,7 @@ var WavedashSDK = class extends EventTarget {
|
|
|
3580
3831
|
[progress]
|
|
3581
3832
|
);
|
|
3582
3833
|
this.clearSetupWarning();
|
|
3583
|
-
iframeMessenger.postToParent(
|
|
3834
|
+
iframeMessenger.postToParent(IFRAME_MESSAGE_TYPE6.PROGRESS_UPDATE, {
|
|
3584
3835
|
progress
|
|
3585
3836
|
});
|
|
3586
3837
|
}
|
|
@@ -3589,7 +3840,7 @@ var WavedashSDK = class extends EventTarget {
|
|
|
3589
3840
|
if (this.gameFinishedLoading) return;
|
|
3590
3841
|
this.gameFinishedLoading = true;
|
|
3591
3842
|
this.heartbeatManager.start();
|
|
3592
|
-
iframeMessenger.postToParent(
|
|
3843
|
+
iframeMessenger.postToParent(IFRAME_MESSAGE_TYPE6.LOADING_COMPLETE, {});
|
|
3593
3844
|
this.overlayManager.takeFocus();
|
|
3594
3845
|
}
|
|
3595
3846
|
get gameLoaded() {
|
|
@@ -3817,30 +4068,45 @@ var WavedashSDK = class extends EventTarget {
|
|
|
3817
4068
|
}
|
|
3818
4069
|
/**
|
|
3819
4070
|
* Updates a UGC item and uploads the file to the server if a filePath is provided
|
|
3820
|
-
*
|
|
3821
|
-
* @param
|
|
3822
|
-
*
|
|
3823
|
-
* @param description
|
|
3824
|
-
* @param visibility
|
|
3825
|
-
* @param filePath - optional IndexedDB key file path to upload to the server. If not provided, the UGC item will be updated but no file will be uploaded.
|
|
4071
|
+
* @param ugcId - The ID of the UGC item to update
|
|
4072
|
+
* @param updates - Object containing the fields to update. May also be passed
|
|
4073
|
+
* as a JSON string by engine bridges (Godot) that can't marshal a dict.
|
|
3826
4074
|
* @returns ugcId
|
|
3827
4075
|
*/
|
|
3828
|
-
async updateUGCItem(ugcId,
|
|
4076
|
+
async updateUGCItem(ugcId, updates = {}) {
|
|
4077
|
+
if (typeof updates === "string") {
|
|
4078
|
+
const raw = updates;
|
|
4079
|
+
try {
|
|
4080
|
+
updates = JSON.parse(raw);
|
|
4081
|
+
} catch (error) {
|
|
4082
|
+
const message = `updateUGCItem: invalid JSON: ${raw}`;
|
|
4083
|
+
logger.error(message, error);
|
|
4084
|
+
return this.formatResponse({
|
|
4085
|
+
success: false,
|
|
4086
|
+
data: null,
|
|
4087
|
+
message
|
|
4088
|
+
});
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
3829
4091
|
return this.apiCall(
|
|
3830
4092
|
this.ugcManager,
|
|
3831
4093
|
"updateUGCItem",
|
|
3832
4094
|
[
|
|
3833
4095
|
["ugcId", vId("userGeneratedContent")],
|
|
3834
|
-
[
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
4096
|
+
[
|
|
4097
|
+
"updates",
|
|
4098
|
+
vOptional(
|
|
4099
|
+
vObject({
|
|
4100
|
+
title: vOptional(vString),
|
|
4101
|
+
description: vOptional(vString),
|
|
4102
|
+
visibility: vOptional(vEnum(UGC_VISIBILITY, "UGCVisibility")),
|
|
4103
|
+
filePath: vOptional(vString)
|
|
4104
|
+
})
|
|
4105
|
+
)
|
|
4106
|
+
]
|
|
3838
4107
|
],
|
|
3839
4108
|
ugcId,
|
|
3840
|
-
|
|
3841
|
-
description,
|
|
3842
|
-
visibility,
|
|
3843
|
-
filePath
|
|
4109
|
+
updates
|
|
3844
4110
|
);
|
|
3845
4111
|
}
|
|
3846
4112
|
/**
|
|
@@ -3867,6 +4133,47 @@ var WavedashSDK = class extends EventTarget {
|
|
|
3867
4133
|
filePath
|
|
3868
4134
|
);
|
|
3869
4135
|
}
|
|
4136
|
+
async listUGCItems(args = {}) {
|
|
4137
|
+
if (typeof args === "string") {
|
|
4138
|
+
const raw = args;
|
|
4139
|
+
try {
|
|
4140
|
+
args = JSON.parse(raw);
|
|
4141
|
+
} catch (error) {
|
|
4142
|
+
const message = `listUGCItems: invalid JSON: ${raw}`;
|
|
4143
|
+
logger.error(message, error);
|
|
4144
|
+
return this.formatResponse({
|
|
4145
|
+
success: false,
|
|
4146
|
+
data: null,
|
|
4147
|
+
message
|
|
4148
|
+
});
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
return this.apiCall(
|
|
4152
|
+
this.ugcManager,
|
|
4153
|
+
"listUGCItems",
|
|
4154
|
+
[
|
|
4155
|
+
[
|
|
4156
|
+
"args",
|
|
4157
|
+
vOptional((value, path) => {
|
|
4158
|
+
const obj = vObject({
|
|
4159
|
+
createdBy: vOptional(vId("users")),
|
|
4160
|
+
ugcType: vOptional(vEnum(UGC_TYPE, "UGCType")),
|
|
4161
|
+
titleSearch: vOptional(vString),
|
|
4162
|
+
numItems: vOptional(vNumber),
|
|
4163
|
+
continueCursor: vOptional(vString)
|
|
4164
|
+
})(value, path);
|
|
4165
|
+
if (obj.continueCursor !== void 0 && (obj.createdBy !== void 0 || obj.ugcType !== void 0 || obj.titleSearch !== void 0 || obj.numItems !== void 0)) {
|
|
4166
|
+
throw new Error(
|
|
4167
|
+
`${path}: continueCursor should be the only argument if present`
|
|
4168
|
+
);
|
|
4169
|
+
}
|
|
4170
|
+
return obj;
|
|
4171
|
+
})
|
|
4172
|
+
]
|
|
4173
|
+
],
|
|
4174
|
+
args
|
|
4175
|
+
);
|
|
4176
|
+
}
|
|
3870
4177
|
// ================================
|
|
3871
4178
|
// Save state / Remote File Storage
|
|
3872
4179
|
// ================================
|
|
@@ -4281,16 +4588,34 @@ var WavedashSDK = class extends EventTarget {
|
|
|
4281
4588
|
// User Presence
|
|
4282
4589
|
// ==============================
|
|
4283
4590
|
/**
|
|
4284
|
-
* Updates rich user presence so friends can see what the player is doing in game
|
|
4285
|
-
*
|
|
4286
|
-
*
|
|
4591
|
+
* Updates rich user presence so friends can see what the player is doing in game.
|
|
4592
|
+
* Supported keys:
|
|
4593
|
+
* `status` — one-line activity shown as the primary line (e.g. "Traveling in a group")
|
|
4594
|
+
* `details` — secondary context shown beneath the status (e.g. current zone or mode)
|
|
4595
|
+
*
|
|
4596
|
+
* Pass an empty dictionary to clear all presence fields.
|
|
4597
|
+
* @param data Presence fields to update.
|
|
4287
4598
|
* @returns true if the presence was updated successfully
|
|
4288
4599
|
*/
|
|
4289
4600
|
async updateUserPresence(data) {
|
|
4601
|
+
if (typeof data === "string") {
|
|
4602
|
+
const raw = data;
|
|
4603
|
+
try {
|
|
4604
|
+
data = JSON.parse(raw);
|
|
4605
|
+
} catch (error) {
|
|
4606
|
+
const message = `updateUserPresence: invalid JSON: ${raw}`;
|
|
4607
|
+
logger.error(message, error);
|
|
4608
|
+
return this.formatResponse({
|
|
4609
|
+
success: false,
|
|
4610
|
+
data: null,
|
|
4611
|
+
message
|
|
4612
|
+
});
|
|
4613
|
+
}
|
|
4614
|
+
}
|
|
4290
4615
|
return this.apiCall(
|
|
4291
4616
|
this.heartbeatManager,
|
|
4292
4617
|
"updateUserPresence",
|
|
4293
|
-
[["data",
|
|
4618
|
+
[["data", vRecord]],
|
|
4294
4619
|
data
|
|
4295
4620
|
);
|
|
4296
4621
|
}
|
|
@@ -4381,7 +4706,7 @@ var WavedashSDK = class extends EventTarget {
|
|
|
4381
4706
|
throw new Error(`Failed to refresh gameplay token: ${response.status}`);
|
|
4382
4707
|
}
|
|
4383
4708
|
this.gameplayJwt = await response.text();
|
|
4384
|
-
iframeMessenger.postToParent(
|
|
4709
|
+
iframeMessenger.postToParent(IFRAME_MESSAGE_TYPE6.GAMEPLAY_JWT_READY, {
|
|
4385
4710
|
gameplayJwt: this.gameplayJwt
|
|
4386
4711
|
});
|
|
4387
4712
|
this.swMessenger.postToServiceWorker({
|
|
@@ -4417,7 +4742,7 @@ var WavedashSDK = class extends EventTarget {
|
|
|
4417
4742
|
}
|
|
4418
4743
|
setupSessionEndListeners() {
|
|
4419
4744
|
iframeMessenger.addEventListener(
|
|
4420
|
-
|
|
4745
|
+
IFRAME_MESSAGE_TYPE6.END_SESSION,
|
|
4421
4746
|
() => this.destroy()
|
|
4422
4747
|
);
|
|
4423
4748
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wvdsh/sdk-js",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.13",
|
|
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
|
}
|