zebra-report: demotion fixes — keep mesh, drop all publishes, no looked-away noise — mirror
This commit is contained in:
parent
4b3adb0bfd
commit
9f89786b87
1 changed files with 45 additions and 23 deletions
|
|
@ -1631,7 +1631,12 @@ function reorderTiles(){
|
|||
for (const el of els) c.appendChild(el);
|
||||
}
|
||||
function broadcastSpotlight(){
|
||||
/* fire-and-forget to the signal server; no-op if not connected yet */
|
||||
/* fire-and-forget to the signal server; no-op if not connected yet.
|
||||
* Suppressed during role transitions — otherwise removeScreenTile +
|
||||
* removeCameraTile triggered by sfuUnpublishScreen/Camera during a
|
||||
* demotion fire pickNextSpotlight → broadcasts an empty spotlight,
|
||||
* which every other peer logs as 'X looked away'. False signal. */
|
||||
if (typeof inRoleTransition !== 'undefined' && inRoleTransition) return;
|
||||
try { send({ type: 'spotlight', key: spotlightKey() }); } catch(_){}
|
||||
}
|
||||
function logSpotlightChange(uuid, key){
|
||||
|
|
@ -2797,12 +2802,13 @@ async function handleSignal(raw){
|
|||
onRoleChanged(prev, m.role);
|
||||
} else {
|
||||
logLine('', mm.handle+' is now '+m.role);
|
||||
/* mesh adjustments */
|
||||
if (canSpeak(myRole)){
|
||||
if (canSpeak(m.role) && !peers.has(m.uuid)) connectToPeer(m.uuid, myUUID < m.uuid);
|
||||
if (!canSpeak(m.role) && peers.has(m.uuid)) tearPeer(m.uuid);
|
||||
} else if (peers.has(m.uuid)){
|
||||
tearPeer(m.uuid);
|
||||
/* mesh adjustments — fox: 'never drop people out of the mesh
|
||||
* automatically'. Promote: connect if we can speak + they
|
||||
* can speak + we don't already have them. Demote: do NOT
|
||||
* tear existing mesh peer connections; let them ride as a
|
||||
* back-channel until one side actually leaves the room. */
|
||||
if (canSpeak(myRole) && canSpeak(m.role) && !peers.has(m.uuid)){
|
||||
connectToPeer(m.uuid, myUUID < m.uuid);
|
||||
}
|
||||
}
|
||||
renderRoom();
|
||||
|
|
@ -2894,23 +2900,39 @@ async function onRoleEntered(){
|
|||
sfuPublish().catch(e => logLine('err','sfu publish: '+e.message));
|
||||
}
|
||||
}
|
||||
let inRoleTransition = false;
|
||||
async function onRoleChanged(prev, next){
|
||||
if (!canSpeak(prev) && canSpeak(next)){
|
||||
await ensureMicAndUI();
|
||||
for (const [uuid, mm] of members){
|
||||
if (uuid === myUUID) continue;
|
||||
if (canSpeak(mm.role)) connectToPeer(uuid, myUUID < uuid);
|
||||
/* suppress spotlight broadcasts triggered by tile cleanup during the
|
||||
* role transition — otherwise removeScreenTile / removeCameraTile
|
||||
* fires pickNextSpotlight which broadcasts an empty spotlight key,
|
||||
* which every other peer logs as 'X looked away'. */
|
||||
inRoleTransition = true;
|
||||
try {
|
||||
if (!canSpeak(prev) && canSpeak(next)){
|
||||
await ensureMicAndUI();
|
||||
for (const [uuid, mm] of members){
|
||||
if (uuid === myUUID) continue;
|
||||
if (canSpeak(mm.role)) connectToPeer(uuid, myUUID < uuid);
|
||||
}
|
||||
/* keep sfuSub alive — screen + camera tracks still ride it. The
|
||||
* ontrack handler suppresses SFU mic when mesh is also up. */
|
||||
sfuPublish().catch(e => logLine('err','sfu publish: '+e.message));
|
||||
} else if (canSpeak(prev) && !canSpeak(next)){
|
||||
/* demote to listener: drop everything we PUBLISH (mic + screen +
|
||||
* camera + game) but DON'T tear mesh peers. fox: 'never drop
|
||||
* people out of the mesh automatically'. The mesh connections
|
||||
* stay up as a bonus low-latency audio path; they get GC'd
|
||||
* naturally when the other end leaves or also demotes. */
|
||||
dropMic(); muted = false;
|
||||
await sfuUnpublish();
|
||||
await sfuUnpublishScreen();
|
||||
await sfuUnpublishCamera();
|
||||
await sfuUnpublishGame();
|
||||
}
|
||||
/* keep sfuSub alive — screen + camera tracks still ride it. The
|
||||
* ontrack handler suppresses SFU mic when mesh is also up. */
|
||||
sfuPublish().catch(e => logLine('err','sfu publish: '+e.message));
|
||||
} else if (canSpeak(prev) && !canSpeak(next)){
|
||||
for (const u of [...peers.keys()]) tearPeer(u);
|
||||
dropMic(); muted = false;
|
||||
await sfuUnpublish();
|
||||
/* still subscribed — listener role needs the same incoming streams */
|
||||
updateRoleUI();
|
||||
} finally {
|
||||
inRoleTransition = false;
|
||||
}
|
||||
updateRoleUI();
|
||||
}
|
||||
async function ensureMicAndUI(){
|
||||
/* mic input + music-mode rows are always visible; here we just grant the
|
||||
|
|
@ -3525,8 +3547,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');
|
|||
|
||||
<footer style="margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
|
||||
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> · built <span class="stamp-date">2026-06-02</span><br>
|
||||
md5 <span class="stamp-md5">933f3fed73b0a824ed75b1cea22bba9a</span><br>
|
||||
sha256 <span class="stamp-sha">836f1145f451e86c871813a9a1ff1dd2adbd4c61ddceb2e59a8a58fe24b14f08</span><br>
|
||||
md5 <span class="stamp-md5">f908c19e47433151020f76df10fe81d0</span><br>
|
||||
sha256 <span class="stamp-sha">f751785bdcb457d172dee013fbe0a445ee01b0aacdb20c2c1fe11352ef98111c</span><br>
|
||||
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
|
||||
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify against these hashes; point at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
|
||||
</footer>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue