From d9fd88ee0ec1967009ea0c9212c6ce331b86f69f Mon Sep 17 00:00:00 2001 From: gwigz Date: Wed, 1 Jul 2026 22:03:18 +0100 Subject: [PATCH] Scene Explorer: hide objects with unresolved props under owner filters With an owner filter active and Full Region Coverage disabled, camming around streams new objects in whose ObjectProperties reply hasn't landed yet. The predicate deliberately let those rows pass ("can't judge what it doesn't know"), so every newly loaded object leaked into the filtered list regardless of owner, and child prims of an already-rejected linkset kept the folder visible the same way. Flag filters (e.g. Light) use local object data, which is why they were unaffected. Fail the owner clause instead while props are unresolved. The props fetch is already queued when a row is built, and applying the reply dirties the row's filter state, so matching rows appear as their ownership resolves. Fixes #335 Co-Authored-By: Claude Fable 5 --- indra/newview/alsceneexplorerpredicate.cpp | 27 ++++++++++--------- .../tests/alsceneexplorerpredicate_test.cpp | 12 +++++++-- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/indra/newview/alsceneexplorerpredicate.cpp b/indra/newview/alsceneexplorerpredicate.cpp index 4f7e54a8b1..2fd95ddf59 100644 --- a/indra/newview/alsceneexplorerpredicate.cpp +++ b/indra/newview/alsceneexplorerpredicate.cpp @@ -53,22 +53,23 @@ bool matches(const ItemFacts& item, const Constraints& c) if (c.mOwnerMode != OWNER_ANY) { // Avatars are their own "owner" so owner filters behave sensibly for - // them; object rows apply the predicate only once props have arrived. + // them. Objects with unresolved props stay hidden until the fetch + // lands and the row re-filters. + if (!item.mIsAvatar && !rec.mPropsValid) + return false; + const LLUUID& owner = item.mIsAvatar ? item.mItemId : rec.mOwnerId; const bool group_owned = !item.mIsAvatar && rec.mGroupOwned; - if (item.mIsAvatar || rec.mPropsValid) + switch (c.mOwnerMode) { - switch (c.mOwnerMode) - { - case OWNER_MINE: if (owner != c.mAgentId) return false; break; - case OWNER_GROUP: if (!group_owned) return false; break; - case OWNER_OTHERS: if (owner == c.mAgentId || group_owned) return false; break; - case OWNER_SPECIFIC: - if (owner != c.mOwnerId && !(group_owned && rec.mGroupId == c.mOwnerId)) - return false; - break; - default: break; - } + case OWNER_MINE: if (owner != c.mAgentId) return false; break; + case OWNER_GROUP: if (!group_owned) return false; break; + case OWNER_OTHERS: if (owner == c.mAgentId || group_owned) return false; break; + case OWNER_SPECIFIC: + if (owner != c.mOwnerId && !(group_owned && rec.mGroupId == c.mOwnerId)) + return false; + break; + default: break; } } diff --git a/indra/newview/tests/alsceneexplorerpredicate_test.cpp b/indra/newview/tests/alsceneexplorerpredicate_test.cpp index 6bca396086..f8808c3c5d 100644 --- a/indra/newview/tests/alsceneexplorerpredicate_test.cpp +++ b/indra/newview/tests/alsceneexplorerpredicate_test.cpp @@ -173,7 +173,7 @@ namespace tut } // Owner gating: avatars are their own owner; objects without props yet - // pass owner modes (the predicate can't judge what it doesn't know). + // fail owner modes until the reply lands. template<> template<> void predicate_object::test<7>() { @@ -187,7 +187,15 @@ namespace tut mFacts.mIsAvatar = false; mFacts.mItemId = mRec.mId; mRec.mPropsValid = false; - ensure("unresolved props pass owner modes", matches(mFacts, mC)); + ensure("unresolved props fail owner modes", !matches(mFacts, mC)); + + mRec.mPropsValid = true; + mRec.mOwnerId = mAgentId; + ensure("resolved props re-apply owner modes", matches(mFacts, mC)); + + mRec.mPropsValid = false; + mC.mOwnerMode = OWNER_ANY; + ensure("unresolved props pass without an owner mode", matches(mFacts, mC)); } // Geometry mask: empty = any; otherwise the item's kind must be in it.