@@ -1230,90 +1230,31 @@ void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, Au
12301230 } else {
12311231 ZoneScopedN (TracyConstants::AUDIO_WHISPER_CACHE_CREATE);
12321232
1233- const WhisperTarget &wt = u->qmTargets .value (static_cast < int >(audioData.targetOrContext ));
1234- if (!wt.qlChannels .isEmpty ()) {
1235- QMutexLocker qml (&qmCache);
1236-
1237- foreach (const WhisperTarget::Channel &wtc, wt.qlChannels ) {
1238- Channel *wc = qhChannels.value (static_cast < unsigned int >(wtc.iId ));
1239- if (wc) {
1240- bool link = wtc.bLinks && !wc->qhLinks .isEmpty ();
1241- bool dochildren = wtc.bChildren && !wc->qlChannels .isEmpty ();
1242- bool group = !wtc.qsGroup .isEmpty ();
1243- if (!link && !dochildren && !group) {
1244- // Common case
1245- if (ChanACL::hasPermission (u, wc, ChanACL::Whisper, &acCache)) {
1246- foreach (User *p, wc->qlUsers ) { channel.insert (static_cast < ServerUser * >(p)); }
1247-
1248- foreach (unsigned int currentSession,
1249- m_channelListenerManager.getListenersForChannel (wc->iId )) {
1250- ServerUser *pDst = static_cast < ServerUser * >(qhUsers.value (currentSession));
1251-
1252- if (pDst) {
1253- addListener (cachedListeners, *pDst, *wc);
1254- }
1255- }
1256- }
1257- } else {
1258- QSet< Channel * > channels;
1259- if (link)
1260- channels = wc->allLinks ();
1261- else
1262- channels.insert (wc);
1263- if (dochildren)
1264- channels.unite (wc->allChildren ());
1265- const QString &redirect = u->qmWhisperRedirect .value (wtc.qsGroup );
1266- const QString &qsg = redirect.isEmpty () ? wtc.qsGroup : redirect;
1267- foreach (Channel *tc, channels) {
1268- if (ChanACL::hasPermission (u, tc, ChanACL::Whisper, &acCache)) {
1269- foreach (User *p, tc->qlUsers ) {
1270- ServerUser *su = static_cast < ServerUser * >(p);
1271-
1272- if (!group || Group::appliesToUser (*tc, *tc, qsg, *su)) {
1273- channel.insert (su);
1274- }
1275- }
1276-
1277- foreach (unsigned int currentSession,
1278- m_channelListenerManager.getListenersForChannel (tc->iId )) {
1279- ServerUser *pDst = static_cast < ServerUser * >(qhUsers.value (currentSession));
1280-
1281- if (pDst && (!group || Group::appliesToUser (*tc, *tc, qsg, *pDst))) {
1282- // Only send audio to listener if the user exists and it is in the group the
1283- // speech is directed at (if any)
1284- addListener (cachedListeners, *pDst, *tc);
1285- }
1286- }
1287- }
1288- }
1289- }
1290- }
1291- }
1233+ const unsigned int uiSession = u->uiSession ;
1234+ qrwlVoiceThread.unlock ();
1235+ qrwlVoiceThread.lockForWrite ();
1236+
1237+ if (!qhUsers.contains (uiSession)) {
1238+ return ;
12921239 }
12931240
1294- {
1295- QMutexLocker qml (&qmCache);
1241+ // Create cache entry for the given target
1242+ // Note: We have to compute the cache entry and add it to the user's cache store in an atomic
1243+ // transaction (ensured by the lock) to avoid running into situations in which a user from the cache
1244+ // gets deleted without this particular cache entry being purged (which happens, if the cache entry is
1245+ // in the store at the point of deleting the user).
1246+ const WhisperTarget &wt = u->qmTargets .value (static_cast < int >(audioData.targetOrContext ));
1247+ WhisperTargetCache cache = createWhisperTargetCacheFor (*u, wt);
12961248
1297- foreach (unsigned int id, wt.qlSessions ) {
1298- ServerUser *pDst = qhUsers.value (id);
1299- if (pDst && ChanACL::hasPermission (u, pDst->cChannel , ChanACL::Whisper, &acCache)
1300- && !channel.contains (pDst))
1301- direct.insert (pDst);
1302- }
1303- }
1249+ u->qmTargetCache .insert (static_cast < int >(audioData.targetOrContext ), std::move (cache));
13041250
1305- unsigned int uiSession = u->uiSession ;
1306- qrwlVoiceThread.unlock ();
1307- qrwlVoiceThread.lockForWrite ();
13081251
1309- if (qhUsers.contains (uiSession))
1310- u->qmTargetCache .insert (static_cast < int >(audioData.targetOrContext ),
1311- { channel, direct, cachedListeners });
13121252 qrwlVoiceThread.unlock ();
13131253 qrwlVoiceThread.lockForRead ();
13141254 if (!qhUsers.contains (uiSession))
13151255 return ;
13161256 }
1257+
13171258 // These users receive the audio because someone is shouting to their channel
13181259 for (ServerUser *pDst : channel) {
13191260 buffer.addReceiver (*u, *pDst, Mumble::Protocol::AudioContext::SHOUT, audioData.containsPositionalData );
@@ -2412,5 +2353,104 @@ bool Server::canNest(Channel *newParent, Channel *channel) const {
24122353 return (parentLevel + channelDepth) < iChannelNestingLimit;
24132354}
24142355
2356+ WhisperTargetCache Server::createWhisperTargetCacheFor (ServerUser &speaker, const WhisperTarget &target) {
2357+ ZoneScoped;
2358+
2359+ QMutexLocker qml (&qmCache);
2360+
2361+ WhisperTargetCache cache;
2362+
2363+ if (!target.channels .empty ()) {
2364+ for (const WhisperTarget::Channel ¤tTarget : target.channels ) {
2365+ Channel *targetChannel = qhChannels.value (currentTarget.id );
2366+
2367+ if (targetChannel) {
2368+ bool includeLinks = currentTarget.includeLinks && !targetChannel->qhLinks .isEmpty ();
2369+ bool includeChildren = currentTarget.includeChildren && !targetChannel->qlChannels .isEmpty ();
2370+ bool restrictToGroup = !currentTarget.targetGroup .isEmpty ();
2371+
2372+ if (!includeLinks && !includeChildren && !restrictToGroup) {
2373+ // Common case
2374+ if (ChanACL::hasPermission (&speaker, targetChannel, ChanACL::Whisper, &acCache)) {
2375+ for (User *p : targetChannel->qlUsers ) {
2376+ // Add users of the target channel
2377+ cache.channelTargets .insert (static_cast < ServerUser * >(p));
2378+ }
2379+
2380+ for (unsigned int currentSession :
2381+ m_channelListenerManager.getListenersForChannel (targetChannel->iId )) {
2382+ // Add users that listen to the target channel (duplicates with users directly
2383+ // in this channel are handled further down)
2384+ ServerUser *pDst = static_cast < ServerUser * >(qhUsers.value (currentSession));
2385+
2386+ if (pDst) {
2387+ addListener (cache.listeningTargets , *pDst, *targetChannel);
2388+ }
2389+ }
2390+ }
2391+ } else {
2392+ QSet< Channel * > channels;
2393+
2394+ if (includeLinks) {
2395+ // allLinks contains the channel itself
2396+ channels = targetChannel->allLinks ();
2397+ } else {
2398+ channels.insert (targetChannel);
2399+ }
2400+
2401+ if (includeChildren) {
2402+ channels.unite (targetChannel->allChildren ());
2403+ }
2404+
2405+ // The target group might be changed by a redirect set up via RPC (Ice/gRPC). In that
2406+ // case the shout is sent to the redirection target instead the originally specified group
2407+ const QString &redirect = speaker.qmWhisperRedirect .value (currentTarget.targetGroup );
2408+ const QString &targetGroup = redirect.isEmpty () ? currentTarget.targetGroup : redirect;
2409+
2410+ for (Channel *subTargetChan : channels) {
2411+ if (ChanACL::hasPermission (&speaker, subTargetChan, ChanACL::Whisper, &acCache)) {
2412+ for (User *p : subTargetChan->qlUsers ) {
2413+ ServerUser *su = static_cast < ServerUser * >(p);
2414+
2415+ if (!restrictToGroup
2416+ || Group::appliesToUser (*subTargetChan, *subTargetChan, targetGroup, *su)) {
2417+ cache.channelTargets .insert (su);
2418+ }
2419+ }
2420+
2421+ for (unsigned int currentSession :
2422+ m_channelListenerManager.getListenersForChannel (subTargetChan->iId )) {
2423+ ServerUser *pDst = static_cast < ServerUser * >(qhUsers.value (currentSession));
2424+
2425+ if (pDst
2426+ && (!restrictToGroup
2427+ || Group::appliesToUser (*subTargetChan, *subTargetChan, targetGroup, *pDst))) {
2428+ // Only send audio to listener if the user exists and it is in the group the
2429+ // speech is directed at (if any)
2430+ addListener (cache.listeningTargets , *pDst, *subTargetChan);
2431+ }
2432+ }
2433+ }
2434+ }
2435+ }
2436+ }
2437+ }
2438+ }
2439+
2440+ for (unsigned int id : target.sessions ) {
2441+ ServerUser *pDst = qhUsers.value (id);
2442+ if (pDst && ChanACL::hasPermission (&speaker, pDst->cChannel , ChanACL::Whisper, &acCache)
2443+ && !cache.channelTargets .contains (pDst))
2444+ cache.directTargets .insert (pDst);
2445+ }
2446+
2447+ // Make sure the speaker themselves is not contained in these lists
2448+ cache.channelTargets .remove (&speaker);
2449+ cache.directTargets .remove (&speaker);
2450+ cache.listeningTargets .remove (&speaker);
2451+
2452+ return cache;
2453+ }
2454+
24152455#undef SIO_UDP_CONNRESET
24162456#undef SENDTO
0 commit comments