From a75c9b3f2024c97d44daaec23960bb2adbf960aa Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 20 Mar 2026 01:15:21 +0000 Subject: [PATCH] viewer sessions are not reusable (at least not in a trivial way) --- .../WebRtcVoice/VoiceViewerSession.cs | 27 +++- .../WebRtcVoiceRegionModule.cs | 6 +- .../WebRtcVoiceServiceModule.cs | 131 +++++++----------- 3 files changed, 76 insertions(+), 88 deletions(-) diff --git a/OpenSim/Addons/os-webrtc-janus/WebRtcVoice/VoiceViewerSession.cs b/OpenSim/Addons/os-webrtc-janus/WebRtcVoice/VoiceViewerSession.cs index ededa44a49..2002cfcefb 100644 --- a/OpenSim/Addons/os-webrtc-janus/WebRtcVoice/VoiceViewerSession.cs +++ b/OpenSim/Addons/os-webrtc-janus/WebRtcVoice/VoiceViewerSession.cs @@ -67,7 +67,7 @@ namespace osWebRtcVoice } // public static bool TryGetViewerSessionByAgentId(UUID pAgentId, out IVoiceViewerSession pViewerSession) - public static bool TryGetViewerSessionByAgentId(UUID pAgentId, out IEnumerable> pViewerSessions) + public static bool TryGetViewerSessionsByAgentId(UUID pAgentId, out IEnumerable> pViewerSessions) { lock (ViewerSessions) { @@ -76,6 +76,21 @@ namespace osWebRtcVoice } } + public static bool TryGetViewerSessionByAgentId(UUID pAgentId, out IVoiceViewerSession pViewerSession) + { + lock (ViewerSessions) + { + IEnumerable> sessions = ViewerSessions.Where(v => v.Value.AgentId == pAgentId); + if(sessions.Count() > 0) + { + pViewerSession = sessions.First().Value; + return true; + } + pViewerSession = null; + return false; + } + } + // Get a viewer session by the VoiceService session ID public static bool TryGetViewerSessionByVSSessionId(string pVSSessionId, out IVoiceViewerSession pViewerSession) { @@ -107,6 +122,15 @@ namespace osWebRtcVoice } } + public static bool TryGetViewerSessionsByAgentAndRegion(UUID pAgentId, UUID pRegionId, out IEnumerable> pViewerSessions) + { + lock (ViewerSessions) + { + pViewerSessions = ViewerSessions.Where(v => v.Value.AgentId == pAgentId && v.Value.RegionId == pRegionId); + return pViewerSessions.Count() > 0; + } + } + public static void AddViewerSession(IVoiceViewerSession pSession) { lock (ViewerSessions) @@ -114,6 +138,7 @@ namespace osWebRtcVoice ViewerSessions[pSession.ViewerSessionID] = pSession; } } + public static void RemoveViewerSession(string pSessionId) { lock (ViewerSessions) diff --git a/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceRegionModule/WebRtcVoiceRegionModule.cs b/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceRegionModule/WebRtcVoiceRegionModule.cs index 73119b2d43..68507dc790 100644 --- a/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceRegionModule/WebRtcVoiceRegionModule.cs +++ b/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceRegionModule/WebRtcVoiceRegionModule.cs @@ -211,7 +211,7 @@ namespace osWebRtcVoice } // Deserialize the request. Convert the LLSDXml to OSD for our use - OSDMap map = BodyToMap(request, "ProvisionVoiceAccountRequest"); + OSDMap map = BodyToMap(request, $"{logHeader}[ProvisionVoice]"); if (map is null) { m_log.Error($"{logHeader}[ProvisionVoice]: No request data found. Agent={agentID}"); @@ -349,7 +349,7 @@ namespace osWebRtcVoice } // Deserialize the request. Convert the LLSDXml to OSD for our use - OSDMap map = BodyToMap(request, "VoiceSignalingRequest"); + OSDMap map = BodyToMap(request, $"{logHeader}[VoiceSignaling]"); if (map is null) { m_log.Error($"{logHeader}[VoiceSignalingRequest]: No request data found. Agent={agentID}"); @@ -404,7 +404,7 @@ namespace osWebRtcVoice return; } - OSDMap reqmap = BodyToMap(request, "[ChatSessionRequest]"); + OSDMap reqmap = BodyToMap(request, $"{logHeader}[ChatSessionRequest]"); if (reqmap is null) { m_log.Warn($"{logHeader} ChatSessionRequest: message body not parsable in request for agent {agentID}"); diff --git a/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceServiceModule/WebRtcVoiceServiceModule.cs b/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceServiceModule/WebRtcVoiceServiceModule.cs index 32665310ae..5caca2a7b2 100644 --- a/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceServiceModule/WebRtcVoiceServiceModule.cs +++ b/OpenSim/Addons/os-webrtc-janus/WebRtcVoiceServiceModule/WebRtcVoiceServiceModule.cs @@ -196,6 +196,7 @@ namespace osWebRtcVoice // ===================================================================== // Thought about doing this but currently relying on the voice service // event ("hangup") to remove the viewer session. +/* private void Event_OnRemovePresence(UUID pAgentID) { // When a presence is removed, remove the viewer sessions for that agent @@ -210,36 +211,15 @@ namespace osWebRtcVoice } } } - - private static bool TryGetViewerSessionByAgentAndScene(UUID pAgentID, UUID pSceneID, out IVoiceViewerSession pViewerSession) - { - if (VoiceViewerSession.TryGetViewerSessionByAgentId(pAgentID, out IEnumerable> vSessions)) - { - foreach (KeyValuePair v in vSessions) - { - if (v.Value.RegionId == pSceneID) - { - pViewerSession = v.Value; - return true; - } - } - } - pViewerSession = null; - return false; - } +*/ private static List> GetViewerSessionsByAgentAndScene(UUID pAgentID, UUID pSceneID) { List> matches = []; - if (VoiceViewerSession.TryGetViewerSessionByAgentId(pAgentID, out IEnumerable> vSessions)) + if (VoiceViewerSession.TryGetViewerSessionsByAgentId(pAgentID, out IEnumerable> vSessions)) { foreach (KeyValuePair v in vSessions) - { - if (v.Value.RegionId == pSceneID) - { - matches.Add(v); - } - } + matches.Add(v); } return matches; } @@ -305,28 +285,6 @@ namespace osWebRtcVoice }); } } - private bool TryGetReusableViewerSession(UUID pAgentID, UUID pSceneID, out IVoiceViewerSession pViewerSession) - { - List> sessions = GetViewerSessionsByAgentAndScene(pAgentID, pSceneID); - foreach (KeyValuePair candidate in sessions) - { - if (IsViewerSessionReusable(candidate.Value)) - { - pViewerSession = candidate.Value; - CleanupDuplicateSessions(pAgentID, pSceneID, candidate.Key); - return true; - } - } - - if (sessions.Count > 0) - { - // No reusable session found: remove all stale sessions to force a clean create path. - CleanupDuplicateSessions(pAgentID, pSceneID, null); - } - - pViewerSession = null; - return false; - } // ===================================================================== // IWebRtcVoiceService @@ -337,59 +295,64 @@ namespace osWebRtcVoice IVoiceViewerSession vSession = null; if (pRequest.TryGetString("viewer_session", out string viewerSessionId)) { + if(pRequest.TryGetBool("logout", out bool islog) && islog) + { + if(UUID.ZeroString.Equals(viewerSessionId, StringComparison.OrdinalIgnoreCase)) + { + if (VoiceViewerSession.TryGetViewerSessionByAgentId(pUserID, out vSession)) + { + m_log.Warn( + $"{LogHeader} ProvisionVoiceAccountRequest: null viewer session: using {vSession.ViewerSessionID}"); + return new OSDMap + { + { "response", "OK" }, + { "message", "logout" } + }; + } + else + { + return new OSDMap + { + { "response", "error" }, + { "message", "Unable to provision voice session (missing viewer_session/channel_type or session not found)" } + }; + } + } + + if (VoiceViewerSession.TryGetViewerSession(viewerSessionId, out vSession)) + { + VoiceViewerSession.RemoveViewerSession(viewerSessionId); + OSDMap resp = vSession.VoiceService.ProvisionVoiceAccountRequest(vSession, pRequest, pUserID, pSceneID); + return resp ?? new OSDMap() { + { "response", "error" }, + { "message", "Logout session not found" } }; + } + } + // request has a viewer session. Use that to find the voice service if (VoiceViewerSession.TryGetViewerSession(viewerSessionId, out vSession)) { CleanupDuplicateSessions(pUserID, pSceneID, viewerSessionId); } - else - { - if (TryGetReusableViewerSession(pUserID, pSceneID, out vSession)) - m_log.Info( - $"{LogHeader} ProvisionVoiceAccountRequest: viewer session {viewerSessionId} not found, reconnect fallback reused {vSession.ViewerSessionID}"); - else - m_log.Error($"{LogHeader} ProvisionVoiceAccountRequest: viewer session {viewerSessionId} not found"); - } } else { // the request does not have a viewer session. See if it's an initial request if (pRequest.TryGetString("channel_type", out string channelType)) { - if (TryGetReusableViewerSession(pUserID, pSceneID, out vSession)) + // Ensure stale sessions are cleared before creating a new one. + CleanupDuplicateSessions(pUserID, pSceneID, null); + if (channelType == "local") { - m_log.Info( - $"{LogHeader} ProvisionVoiceAccountRequest: reconnect reuse for agent {pUserID}, scene {pSceneID}, viewer_session {vSession.ViewerSessionID}"); + // TODO: check if this userId is making a new session (case that user is reconnecting) + vSession = m_spatialVoiceService.CreateViewerSession(pRequest, pUserID, pSceneID); + VoiceViewerSession.AddViewerSession(vSession); } else { - // Ensure stale sessions are cleared before creating a new one. - CleanupDuplicateSessions(pUserID, pSceneID, null); - if (channelType == "local") - { - // TODO: check if this userId is making a new session (case that user is reconnecting) - vSession = m_spatialVoiceService.CreateViewerSession(pRequest, pUserID, pSceneID); - VoiceViewerSession.AddViewerSession(vSession); - } - else - { - // TODO: check if this userId is making a new session (case that user is reconnecting) - vSession = m_nonSpatialVoiceService.CreateViewerSession(pRequest, pUserID, pSceneID); - VoiceViewerSession.AddViewerSession(vSession); - } - } - } - else - { - if (TryGetReusableViewerSession(pUserID, pSceneID, out vSession)) - { - m_log.Info( - $"{LogHeader} ProvisionVoiceAccountRequest: missing channel_type, reused existing session for agent {pUserID}, scene {pSceneID}, viewer_session {vSession.ViewerSessionID}"); - } - else - { - m_log.Error( - $"{LogHeader} ProvisionVoiceAccountRequest: no channel_type in request and no existing session for agent {pUserID}, scene {pSceneID}"); + // TODO: check if this userId is making a new session (case that user is reconnecting) + vSession = m_nonSpatialVoiceService.CreateViewerSession(pRequest, pUserID, pSceneID); + VoiceViewerSession.AddViewerSession(vSession); } } }