fix(vitalsharing): clear stopped peers from the NetworkUI window
Peers who unsubscribed or disconnected from vital sharing were lingering forever in the Vital Sharing browser window because nothing ever deleted them from the server-side state or told the browser to drop them. Backend: - share_unsubscribe now pops the character from _vital_sharing_peer_state (not just flips connected=false) and broadcasts a share_peer_removed envelope to browser clients. - On real plugin disconnect, do the same: pop the state entry and broadcast share_peer_removed so the NetworkUI updates immediately. Frontend: - New removeVitalSharingPeer(name) deletes from the local vitalSharingPeers dict and re-renders. - socket.onmessage now routes share_peer_removed to it. - refreshVitalSharingPeers() reconciles against the server's list and prunes any local entries the server no longer knows about, catching any race where the broadcast was missed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7a31469d69
commit
da4e840581
2 changed files with 32 additions and 4 deletions
18
main.py
18
main.py
|
|
@ -3204,9 +3204,13 @@ async def ws_receive_snapshots(
|
|||
char = data.get("character_name")
|
||||
if char:
|
||||
_vital_sharing_subscribers.discard(char)
|
||||
if char in _vital_sharing_peer_state:
|
||||
_vital_sharing_peer_state[char]["connected"] = False
|
||||
_vital_sharing_peer_state.pop(char, None)
|
||||
logger.info(f"🤝 VITAL_SHARING_UNSUBSCRIBED: {char}")
|
||||
# Tell browser clients to drop this peer from their UI
|
||||
await _broadcast_to_browser_clients({
|
||||
"type": "share_peer_removed",
|
||||
"character_name": char,
|
||||
})
|
||||
continue
|
||||
|
||||
if msg_type and msg_type.startswith("share_"):
|
||||
|
|
@ -3233,9 +3237,15 @@ async def ws_receive_snapshots(
|
|||
plugin_conns.pop(name, None)
|
||||
live_equipment_cantrip_states.pop(name, None)
|
||||
live_nearby_objects.pop(name, None)
|
||||
was_sharing = name in _vital_sharing_subscribers or name in _vital_sharing_peer_state
|
||||
_vital_sharing_subscribers.discard(name)
|
||||
if name in _vital_sharing_peer_state:
|
||||
_vital_sharing_peer_state[name]["connected"] = False
|
||||
_vital_sharing_peer_state.pop(name, None)
|
||||
if was_sharing:
|
||||
# Tell browser clients to drop this peer from their UI
|
||||
await _broadcast_to_browser_clients({
|
||||
"type": "share_peer_removed",
|
||||
"character_name": name,
|
||||
})
|
||||
|
||||
# Clean up any plugin registrations for this socket
|
||||
to_remove = [n for n, ws in plugin_conns.items() if ws is websocket]
|
||||
|
|
|
|||
|
|
@ -3074,6 +3074,8 @@ function initWebSocket() {
|
|||
const rw = radarWindows[msg.character_name];
|
||||
if (rw) rw._radarDungeonLandblock = msg.landblock;
|
||||
}
|
||||
} else if (msg.type === 'share_peer_removed') {
|
||||
removeVitalSharingPeer(msg.character_name);
|
||||
} else if (typeof msg.type === 'string' && msg.type.startsWith('share_')) {
|
||||
updateVitalSharingPeer(msg);
|
||||
}
|
||||
|
|
@ -4438,6 +4440,14 @@ function showCommentsSection(row, issue, win) {
|
|||
const vitalSharingPeers = {};
|
||||
let _vitalSharingPollTimer = null;
|
||||
|
||||
function removeVitalSharingPeer(name) {
|
||||
if (!name) return;
|
||||
if (vitalSharingPeers[name]) {
|
||||
delete vitalSharingPeers[name];
|
||||
renderVitalSharingWindow();
|
||||
}
|
||||
}
|
||||
|
||||
function updateVitalSharingPeer(msg) {
|
||||
const name = msg.character_name;
|
||||
if (!name) return;
|
||||
|
|
@ -4504,10 +4514,18 @@ async function refreshVitalSharingPeers() {
|
|||
const resp = await fetch('/vital-sharing/peers');
|
||||
if (!resp.ok) return;
|
||||
const data = await resp.json();
|
||||
const serverNames = new Set();
|
||||
(data.peers || []).forEach(p => {
|
||||
serverNames.add(p.character_name);
|
||||
const existing = vitalSharingPeers[p.character_name] || {};
|
||||
vitalSharingPeers[p.character_name] = { ...existing, ...p };
|
||||
});
|
||||
// Prune any local entries the server no longer knows about (unsubscribed
|
||||
// or disconnected). This catches any race where the share_peer_removed
|
||||
// broadcast didn't arrive.
|
||||
Object.keys(vitalSharingPeers).forEach(name => {
|
||||
if (!serverNames.has(name)) delete vitalSharingPeers[name];
|
||||
});
|
||||
} catch (e) {
|
||||
// ignore transient errors
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue