CLose story 1.2
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
// OQ-1 Spike Result: css-fallback — FoundryVTT v14 — 2026-05-21
|
||||
//
|
||||
// Investigation findings (Story 1.2 spike):
|
||||
// - game.webrtc is foundry.av.AVMaster — no getConnection(userId) method exists.
|
||||
// - Remote stream access: game.webrtc.client.getMediaStreamForUser(userId)
|
||||
// via the abstract AVClient public interface.
|
||||
// - track.enabled = false on remote inbound tracks does NOT stop WebRTC bandwidth.
|
||||
// RTP packets continue arriving from the remote peer regardless.
|
||||
// - True bandwidth elimination requires SDP renegotiation (not in AVMaster public API).
|
||||
// - LiveKit backend (CONFIG.WebRTC.clientClass override) removes peers/remoteStreams;
|
||||
// only getMediaStreamForUser() remains safe across backends.
|
||||
//
|
||||
// Conclusion: this.webrtc = null. scrying-pool.webrtcMode = 'css-fallback' (world default).
|
||||
// CSS/DOM cosmetic hiding is the only honest implementation path for Story 1.3+.
|
||||
|
||||
/**
|
||||
* Sole gateway to game.* APIs for the Scrying Pool module.
|
||||
*
|
||||
* Feature-detects WebRTC track-disabling capability at init time via
|
||||
* {@link FoundryAdapter.probeCapability}. Exposes a `webrtc` surface
|
||||
* ({disableTrack, enableTrack}) if track-disable is confirmed; `null` otherwise.
|
||||
*
|
||||
* Construction is side-effect-free. The webrtc property must be set by the
|
||||
* caller (at Hooks.once('ready') when game.webrtc is available) via
|
||||
* {@link FoundryAdapter.buildWebRTCSurface}. Story 1.3 wires this up.
|
||||
*/
|
||||
export class FoundryAdapter {
|
||||
/** Settings namespace for all scrying-pool world settings. */
|
||||
static SETTINGS_NS = 'scrying-pool';
|
||||
|
||||
/**
|
||||
* World setting key for the resolved WebRTC capability mode.
|
||||
* Full identifier: `scrying-pool.webrtcMode`.
|
||||
* Value is one of: `'track-disable'` | `'css-fallback'` | `'unsupported'`
|
||||
*/
|
||||
static SETTING_WEBRTC_MODE = 'webrtcMode';
|
||||
|
||||
/**
|
||||
* Creates a FoundryAdapter. Side-effect-free — no game.* access in constructor.
|
||||
*/
|
||||
constructor() {
|
||||
/**
|
||||
* WebRTC track-disabling surface, or null on the css-fallback/unsupported path.
|
||||
*
|
||||
* Set to `{ disableTrack, enableTrack }` only if probeCapability returns
|
||||
* `'track-disable'`. As of FoundryVTT v14, the probe always returns
|
||||
* `'css-fallback'` or `'unsupported'` — see OQ-1 spike comment at top of file.
|
||||
*
|
||||
* @type {{ disableTrack(userId: string): void, enableTrack(userId: string): void } | null}
|
||||
*/
|
||||
this.webrtc = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Probes the game.webrtc (AVMaster) instance for WebRTC track-disabling capability.
|
||||
*
|
||||
* Probe logic and results (FoundryVTT v14, 2026-05-21):
|
||||
* - If game.webrtc is null/falsy → AV disabled or not yet initialised → `'unsupported'`
|
||||
* - If game.webrtc.client lacks getMediaStreamForUser() → non-standard backend → `'unsupported'`
|
||||
* - Otherwise: tracks are technically reachable but track.enabled = false does NOT reduce
|
||||
* inbound WebRTC bandwidth (RTP packets keep arriving). The `'track-disable'` outcome
|
||||
* requires true bandwidth elimination, so the result is `'css-fallback'`.
|
||||
*
|
||||
* The `'track-disable'` branch in buildWebRTCSurface is kept for forward compatibility
|
||||
* in case a future FoundryVTT version or custom AVClient exposes real bandwidth control.
|
||||
*
|
||||
* @param {unknown} gameWebrtc - The game.webrtc value at ready time (may be null/undefined)
|
||||
* @returns {'track-disable'|'css-fallback'|'unsupported'}
|
||||
*/
|
||||
static probeCapability(gameWebrtc) {
|
||||
if (!gameWebrtc) return 'unsupported';
|
||||
const client = /** @type {any} */ (gameWebrtc).client;
|
||||
if (!client || typeof client.getMediaStreamForUser !== 'function') return 'unsupported';
|
||||
// track.enabled = false on remote inbound tracks does NOT stop WebRTC bandwidth.
|
||||
// The 'track-disable' branch is unreachable with the current FoundryVTT v14 API.
|
||||
return 'css-fallback';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the webrtc surface object for the `'track-disable'` capability path.
|
||||
*
|
||||
* NOTE: As of FoundryVTT v14, {@link FoundryAdapter.probeCapability} never returns
|
||||
* `'track-disable'` because `track.enabled = false` does not stop inbound RTP bandwidth.
|
||||
* This method is kept for forward compatibility and as tested documentation of the
|
||||
* interface contract that Story 1.3+ consumers expect.
|
||||
*
|
||||
* @param {{ client: { getMediaStreamForUser(userId: string): (MediaStream|null|undefined) } }} gameWebrtc
|
||||
* @returns {{ disableTrack(userId: string): void, enableTrack(userId: string): void }}
|
||||
*/
|
||||
static buildWebRTCSurface(gameWebrtc) {
|
||||
return {
|
||||
disableTrack(userId) {
|
||||
try {
|
||||
const stream = gameWebrtc.client.getMediaStreamForUser(userId);
|
||||
const tracks = stream?.getVideoTracks() ?? [];
|
||||
for (const track of tracks) track.enabled = false;
|
||||
if (tracks.length === 0) {
|
||||
console.warn('[ScryingPool] disableTrack: no video tracks found for', userId);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[ScryingPool] disableTrack failed:', err);
|
||||
}
|
||||
},
|
||||
enableTrack(userId) {
|
||||
try {
|
||||
const stream = gameWebrtc.client.getMediaStreamForUser(userId);
|
||||
const tracks = stream?.getVideoTracks() ?? [];
|
||||
for (const track of tracks) track.enabled = true;
|
||||
if (tracks.length === 0) {
|
||||
console.warn('[ScryingPool] enableTrack: no video tracks found for', userId);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[ScryingPool] enableTrack failed:', err);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user