diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md new file mode 100644 index 0000000000..f212506198 --- /dev/null +++ b/DOCUMENTATION.md @@ -0,0 +1,433 @@ +# Documentation Style Guide + +This document outlines the documentation standards for the Second Life Viewer project. + +## Philosophy + +Our documentation should be **conversational, practical, and engineer-focused**. We write for engineers who want to understand not just *what* the code does, but *why* it exists and *how* to use it effectively. This is especially important for our open source program, and third party viewer projects. + +## Tone and Voice + +### Conversational and Approachable +- Use clear, direct language: "This manages...", "Use this when...", "This handles..." +- Write like you're explaining to a colleague, not reading from a technical manual. Many viewer developers are doing this in their free time, and out of love for Second Life. +- Be friendly but professional: "Consider using this for..." or "This is designed for..." +- Avoid vague language like "magical" or "where the magic happens" - be specific about what code does +- Be mindful of platform differences: behavior on your system may differ on other configurations +- Assume that people will use it out of spec. Be respectful but clear about intended use cases. + +### Practical and Grounded +- Connect abstract concepts to real-world use cases +- Explain the "why" behind design decisions +- Include performance implications and trade-offs +- Mention common pitfalls or gotchas + +### Examples of Good Tone: +```cpp +/** + * @brief Checks if a handle still points to a valid node. + * + * Handles can become invalid when their target nodes are destroyed. This method + * verifies handle validity before use. Always check this before accessing + * node data when the handle's validity is uncertain. + * + * @return true if the handle points to a valid node, false otherwise + */ +``` + +## Structure and Format + +### Doxygen Style Headers +Every public API should have a complete doxygen comment with: + +```cpp +/** + * @brief One-line summary that completes "This function..." + * + * Detailed explanation that covers: + * - What this does in practical terms + * - Why you'd want to use it + * - Any important behavior or constraints + * - Performance considerations if relevant + * + * @param paramName Description of what this parameter controls + * @return Description of what you get back and when it might be null/empty + * @throws ExceptionType When and why this might throw + * + * @code + * // Practical usage example + * auto result = someFunction(exampleParam); + * if (result.has_value()) { + * // What to do with the result + * } + * @endcode + */ +``` + +As a rule, you should avoid duplicate documentation between the header and implementation. The header should document what the API does and how to use it, while the implementation file should document specific implementation details, optimizations, and non-obvious behaviors. + +### Required Elements + +1. **@brief** - Concise but complete summary +2. **Detailed explanation** - 2-4 sentences explaining practical usage +3. **@param/@return** - For all parameters and return values +4. **@throws** - For any exceptions that might be thrown +5. **@code example** - Real, compilable usage example + +## Content Guidelines + +### What to Document + +**Everything public:** +- Classes and their purpose +- All public methods and their parameters +- Public member variables and type aliases (with inline `/// comments`) +- Iterators and their behavior +- Helper functions and utilities + +**Everything private (equally important):** +- All private methods with full documentation +- Private member variables (with inline `/// comments`) +- Internal data structures and their invariants +- Helper classes and implementation details +- Complex algorithms and their rationale + +Private APIs often contain the most critical and complex logic. Future maintainers need to understand not just what private methods do, but why they exist, what assumptions they make, and why they're not exposed publicly. + +**Static, const, etc. variables and methods** +- Describe why things are static +- Describe why a variable is a constant + +These things are all great to use when you have a great use case for them. Let others know why to avoid potential disagreements in PRs and so on later. + +Even methods should get this treatment. Why is a given method static? Why isn't it an instance method? Etc. + +**Implementation quirks, specifics** +- Inline comments explaining non-obvious code sections +- Why specific buffer sizes or magic numbers were chosen +- Algorithmic optimizations and their trade-offs +- Workarounds for known issues or limitations +- Important behavioral details not visible from the API + +These should be documented as inline comments close to the relevant code. Avoid speculation - only document what you can verify from the code or existing comments. + +### What to Emphasize + +**Engineer Intent:** +- What problem does this solve? +- When would you choose this over alternatives? +- What are the common use patterns? + +**Practical Details:** +- Performance characteristics +- Memory usage implications +- Thread safety guarantees +- Error conditions and handling + +**Implementation Insights:** +- Why specific design choices were made +- How complex algorithms work at a high level +- Important invariants or assumptions +- Behavioral quirks and details + +### Code Examples + +Save this for more complex methods that aren't immediately obvious in how they should be utilized: + +```cpp +/** + * @code + * // Create a task graph + * AcyclicGraph taskGraph; + * auto compile = taskGraph.addNode(CompileTask{}, "compile"); + * auto link = taskGraph.addNode(LinkTask{}, "link"); + * + * // Set up dependencies + * taskGraph.addDependency(link, compile); // Link depends on compile + * + * // Execute in order + * auto order = taskGraph.getTopologicalOrder(); + * for (auto task : order) { + * executeTask(task); + * } + * @endcode + */ +``` + +**Good examples:** +- Show real, practical usage +- Demonstrate the most common use case +- Include error handling where relevant +- Use meaningful variable names + +### Implementation Comments + +For inline comments in implementation files: +- Explain non-obvious algorithms and optimizations +- Document performance trade-offs +- Keep comments concise and factual +- Add context for workarounds or unusual approaches + +Example: +```cpp +// Combine IP:port into single 64-bit key for map lookup +U64 ipport = (((U64)ip) << 32) | (U64)port; + +// Remove by moving last element to this object's position (swap-and-pop for O(1) removal) +mActiveObjects[idx] = mActiveObjects[last_index]; + +// Stack-allocated buffer to avoid heap fragmentation from frequent allocations +U8 compressed_dpbuffer[2048]; +``` + +## Specific Patterns + +### Private Methods and Internal APIs + +Private methods deserve the same level of documentation as public ones, with additional emphasis on: + +```cpp +/** + * @brief What this internal method accomplishes within the system. + * + * Detailed explanation covering: + * - Why this functionality is needed internally + * - What assumptions it makes about system state + * - Why this is private rather than public (safety, complexity, etc.) + * - How it fits into the larger internal workflow + * - Any invariants it maintains or requires + * + * This method is private because [specific reason - too low-level, unsafe + * without proper setup, implementation detail that might change, etc.] + * + * @param param What this parameter controls in the internal context + * @return What gets returned and how it's used internally + * + * @code + * // Example showing how this is used internally + * // (May reference private state or other private methods) + * @endcode + */ +``` + +**Key elements for private API documentation:** +- **Why it's private** - Explain the specific reasons (complexity, safety, implementation detail) +- **Internal context** - How it fits into private workflows +- **Assumptions** - What state the object must be in, what's been validated already +- **Invariants** - What this method maintains or requires +- **Future maintainers** - What someone modifying this needs to know + +### Classes and Major Components + +```cpp +/** + * @brief One-sentence description of what this class represents. + * + * Longer explanation that covers: + * - What real-world problem this solves + * - Key design principles or guarantees + * - How it fits into the larger system + * + * The explanation should make someone say "oh, I need this for..." + * rather than "what does this do?" + * + * @tparam TemplateParam Description of template parameters + * + * @code + * // Complete usage example showing the main workflow + * @endcode + */ +``` + +### Methods with Complex Behavior + +```cpp +/** + * @brief What this method accomplishes for the user. + * + * Explanation of how it works, including: + * - Important behavior that might be surprising + * - Performance characteristics + * - When you'd use this vs alternatives + * + * @param param Description focusing on what the user provides + * @return What the user gets back and what it means + * + * @code + * // Example showing typical usage + * @endcode + */ +``` + +### Member Variables + +```cpp +std::vector nodes; /// Brief description of what this contains +std::vector free_indices; /// Why this exists and how it's used +std::vector slot_generations; /// Key insight about this design choice +``` + +### API Documentation vs Implementation Documentation + +**For public APIs:** +- Document the public contract - what it does, how to use it +- Include usage examples for complex APIs +- Explain preconditions, postconditions, and invariants +- Focus on the "what" and "why" from a user perspective + +**For implementations:** +- Document implementation-specific details +- Explain algorithmic choices and optimizations +- Add inline comments for non-obvious code sections +- Focus on the "how" and internal "why" + +### Iterators and Complex APIs + +For iterators, document: +- STL compatibility requirements +- What iteration order means +- How invalid elements are handled +- Performance characteristics +- Typical usage patterns + +## Quality Checklist + +### For Every Public Method: +- [ ] Clear @brief that explains the user benefit +- [ ] Detailed explanation covering the "why" +- [ ] All parameters documented with user perspective +- [ ] Return value explained including edge cases +- [ ] Practical code example +- [ ] Performance notes if relevant + +### For Every Private Method: +- [ ] Clear @brief explaining internal purpose +- [ ] Why this is private rather than public +- [ ] What assumptions it makes about object state +- [ ] How it fits into internal workflows +- [ ] What invariants it maintains or requires +- [ ] Internal usage example if complex + +### For Every Class: +- [ ] Purpose explained in terms of user problems +- [ ] Key design principles mentioned +- [ ] Template parameters explained +- [ ] Main usage pattern demonstrated +- [ ] Relationship to other components noted +- [ ] Internal architecture overview for complex classes + +### For the Overall File: +- [ ] All public APIs documented +- [ ] All private methods documented +- [ ] Member variables have inline documentation +- [ ] Internal data structures explained +- [ ] File-level overview explaining the component's role + +## Working with Existing Code + +When documenting legacy code: +- **Preserve existing comments** - They often contain valuable historical context +- **Don't replace, augment** - Add new documentation alongside existing comments +- **Verify claims** - Don't make assumptions about why code exists; document only what you can verify +- **Respect the unknown** - If you don't understand why something is done a certain way, don't guess + +## Anti-Patterns to Avoid + +**Don't write encyclopedia entries:** +```cpp +// Bad: Technical but not helpful +/** + * @brief Implements Kahn's algorithm for topological sorting of directed acyclic graphs. + */ + +// Good: Explains the practical benefit +/** + * @brief Returns nodes in topological order (dependencies before dependents). + * + * Useful for dependency graphs where you need to process items in order. + * Each node in the returned list comes after all its dependencies, making + * this ideal for build systems, execution ordering, or any dependency-based + * processing. + */ +``` + +**Avoid overly casual language:** +```cpp +// Bad: Too casual +/** + * @brief This is where the magic happens for object updates. + */ + +// Good: Clear and professional +/** + * @brief Core frame update that coordinates all object-related processing. + */ +``` + +**Don't just repeat the function name:** +```cpp +// Bad: Says nothing useful +/** + * @brief Gets the node count. + */ + +// Good: Explains what the count represents +/** + * @brief Gets the number of valid nodes in the graph. + * + * @return Count of active, usable nodes. Excludes nodes that have been + * removed but whose slots haven't been recycled yet. + */ +``` + +### Language Guide + +**Professional alternatives to overly casual phrases:** +- Instead of "This is the thing that..." → "This component handles..." +- Instead of "Where the magic happens" → "Core processing occurs here" or "Primary functionality" +- Instead of "You should probably..." → "Consider using..." or "Typically used for..." +- Instead of "This is a hack" → "This is a workaround for..." or "This addresses a limitation..." +- Instead of "Great for..." → "Useful for..." or "Designed for..." + +## Tools and Automation + +- Use Doxygen-compatible comments for consistency +- Consider automated checks for missing documentation +- Review documentation during code review with the same rigor as code +- Test that examples actually compile and work + +## Example + +Here's an example of how LLViewerObjectList is documented: + +```cpp +/** + * @brief Manages all viewer-side objects in the virtual world. + * + * This is the master registry for everything you can see in Second Life - from + * avatars to prims to particles. It serves as the central hub that tracks, + * updates, and manages the lifecycle of all objects in your viewing area, + * keeping the virtual world synchronized between the server and your viewer. + * + * The class handles: + * - Object creation, updates, and destruction based on server messages + * - Tracking orphaned objects (children whose parents haven't loaded yet) + * - Managing active objects that need frequent updates + * - Caching and fetching object physics and cost data + * - Providing fast lookups by UUID or local ID + * + * Performance note: This class is on the hot path for frame updates, so many + * operations are optimized for speed over memory usage. + */ +``` + +Notice how this: +- Starts with a clear, one-line summary +- Uses concrete descriptions ("master registry") without being overly casual +- Lists specific responsibilities +- Includes performance considerations that matter to developers +- Maintains a professional but approachable tone throughout + +## Conclusion + +Good documentation is a conversation with future users (including yourself). Write like you're helping someone solve a real problem, not just describing what the code does. The extra effort to make documentation conversational and practical pays dividends in code maintainability and developer productivity. diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index d72d428c08..02ec165a26 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -28,6 +28,30 @@ #include "llviewerobjectlist.h" +/** + * Implementation Overview: + * + * The LLViewerObjectList is the central manager for all objects in the virtual world. + * It maintains several key data structures: + * + * 1. mObjects - Master list of all objects (including dead ones until cleanup) + * 2. mActiveObjects - Subset of objects needing per-frame updates + * 3. mUUIDObjectMap - Fast lookup from UUID to object pointer + * 4. mIndexAndLocalIDToUUID - Maps (region_index,local_id) pairs to UUIDs + * 5. mOrphanChildren/mOrphanParents - Tracks parent-child relationships that arrive out of order + * + * Performance Considerations: + * - Object updates arrive hundreds of times per second, so operations are optimized + * - Uses "swap and pop" for O(1) removal from vectors + * - Lazy texture updates cycle through objects in bins to avoid frame spikes + * - Dead object cleanup is batched to minimize memory allocations + * - Stack-allocated buffers for decompression to avoid heap fragmentation + * + * Thread Safety: + * - This class is NOT thread-safe and should only be accessed from the main thread + * - Coroutines are used for async HTTP requests but results are processed on main thread + */ + #include "message.h" #include "llfasttimer.h" #include "llrender.h" @@ -97,6 +121,8 @@ LLViewerObjectList gObjectList; extern LLPipeline gPipeline; // Statics for object lookup tables. +// Start at 1, not 0, so we can use 0 as a sentinel value meaning "no region assigned yet" +// This lets us quickly check if a region has been seen before in getIndex() U32 LLViewerObjectList::sSimulatorMachineIndex = 1; // Not zero deliberately, to speed up index check. LLViewerObjectList::LLViewerObjectList() @@ -133,7 +159,7 @@ void LLViewerObjectList::getUUIDFromLocal(LLUUID &id, const U32 ip, const U32 port) { - U64 ipport = (((U64)ip) << 32) | (U64)port; + U64 ipport = (((U64)ip) << 32) | (U64)port; /// Combine IP:port into single 64-bit key for map lookup U32 index = mIPAndPortToIndex[ipport]; @@ -152,7 +178,7 @@ U64 LLViewerObjectList::getIndex(const U32 local_id, const U32 ip, const U32 port) { - U64 ipport = (((U64)ip) << 32) | (U64)port; + U64 ipport = (((U64)ip) << 32) | (U64)port; /// Combine IP:port into single 64-bit key for map lookup U32 index = mIPAndPortToIndex[ipport]; @@ -200,7 +226,7 @@ void LLViewerObjectList::setUUIDAndLocal(const LLUUID &id, const U32 port, LLViewerObject* objectp) { - U64 ipport = (((U64)ip) << 32) | (U64)port; + U64 ipport = (((U64)ip) << 32) | (U64)port; /// Combine IP:port into single 64-bit key for map lookup U32 index = mIPAndPortToIndex[ipport]; @@ -457,6 +483,9 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, return; } + // Stack-allocated buffer for decompressing object data. 2048 bytes is enough + // for most object updates - larger updates are extremely rare. Using stack + // allocation avoids heap fragmentation from frequent allocations. U8 compressed_dpbuffer[2048]; LLDataPackerBinaryBuffer compressed_dp(compressed_dpbuffer, 2048); LLViewerStatsRecorder& recorder = LLViewerStatsRecorder::instance(); @@ -472,6 +501,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, S32 uncompressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data); LL_DEBUGS("ObjectUpdate") << "got binary data from message to compressed_dpbuffer" << LL_ENDL; + // Note: 2048 is the buffer size, not the data size. Data is typically much smaller. mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compressed_dpbuffer, 0, i, 2048); compressed_dp.assignBuffer(compressed_dpbuffer, uncompressed_length); @@ -499,7 +529,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, { //send to object cache regionp->cacheFullUpdate(compressed_dp, flags); - continue; + continue; // Skip creating the object now - it's cached for later } } else //OUT_TERSE_IMPROVED @@ -558,9 +588,11 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, objectp = regionp->updateCacheEntry(local_id, objectp); } - // This looks like it will break if the local_id of the object doesn't change - // upon boundary crossing, but we check for region id matching later... - // Reset object local id and region pointer if things have changed + // Handle object region/local ID changes + // This happens when: + // 1. Object crosses region boundaries (gets new local ID in new region) + // 2. Object is rezzed from inventory (may reuse an old UUID with new local ID) + // We need to update our lookup tables to track the object correctly if (objectp && ((objectp->mLocalID != local_id) || (objectp->getRegion() != regionp))) @@ -1060,7 +1092,7 @@ void LLViewerObjectList::fetchObjectCostsCoro(std::string url) - uuid_set_t diff; + uuid_set_t diff; // Objects that need cost fetch but aren't already pending std::set_difference(mStaleObjectCost.begin(), mStaleObjectCost.end(), mPendingObjectCost.begin(), mPendingObjectCost.end(), @@ -1182,8 +1214,8 @@ void LLViewerObjectList::fetchPhisicsFlagsCoro(std::string url) httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", httpPolicy)); LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); - LLSD idList; - U32 objectIndex = 0; + LLSD idList; // Array of object UUIDs to request physics for + U32 objectIndex = 0; // Index for building the request array for (uuid_set_t::iterator it = mStalePhysicsFlags.begin(); it != mStalePhysicsFlags.end();) { @@ -1420,6 +1452,7 @@ void LLViewerObjectList::cleanDeadObjects(bool use_timer) S32 num_removed = 0; LLViewerObject *objectp; + // Start from the back of the vector to build a contiguous block of dead objects vobj_list_t::reverse_iterator target = mObjects.rbegin(); vobj_list_t::iterator iter = mObjects.begin(); @@ -1434,6 +1467,7 @@ void LLViewerObjectList::cleanDeadObjects(bool use_timer) if (objectp->isDead()) { + // Swap dead object to the end without ref counting overhead LLPointer::swap(*iter, *target); *target = NULL; ++target; @@ -1472,7 +1506,7 @@ void LLViewerObjectList::removeFromActiveList(LLViewerObject* objectp) S32 size = (S32)mActiveObjects.size(); if (size > 0) // mActiveObjects could have been cleaned already { - // Remove by moving last element to this object's position + // Remove by moving last element to this object's position (swap-and-pop for O(1) removal) llassert(idx < size); // idx should be always within mActiveObjects, unless killAllObjects was called llassert(mActiveObjects[idx] == objectp); // object should be there @@ -1530,10 +1564,11 @@ void LLViewerObjectList::updateActive(LLViewerObject *objectp) } } - //post condition: if object is active, it must be on the active list + // Post-conditions to verify active list integrity (debug builds only) + // These asserts ensure our active list stays consistent with object state llassert(!active || std::find(mActiveObjects.begin(), mActiveObjects.end(), objectp) != mActiveObjects.end()); - //post condition: if object is not active, it must not be on the active list + // Post-condition: if object is not active, it must not be on the active list llassert(active || std::find(mActiveObjects.begin(), mActiveObjects.end(), objectp) == mActiveObjects.end()); } @@ -2017,13 +2052,16 @@ void LLViewerObjectList::findOrphans(LLViewerObject* objectp, U32 ip, U32 port) } if (std::find(mOrphanParents.begin(), mOrphanParents.end(), getIndex(objectp->mLocalID, ip, port)) == mOrphanParents.end()) { - // did not find objectp in OrphanParent list + // This object is not a parent of any known orphans, nothing to do return; } U64 parent_info = getIndex(objectp->mLocalID, ip, port); bool orphans_found = false; // Iterate through the orphan list, and set parents of matching children. + // We can't use remove_if here because we need to do complex operations + // on each orphan (reconnecting drawable hierarchy, updating visibility, etc.) + // This is why we manually iterate and track what to remove. for (std::vector::iterator iter = mOrphanChildren.begin(); iter != mOrphanChildren.end(); ) { @@ -2079,6 +2117,7 @@ void LLViewerObjectList::findOrphans(LLViewerObject* objectp, U32 ip, U32 port) } // Remove orphan parent and children from lists now that they've been found + // We remove the parent info first, then remove all children with matching parent { std::vector::iterator iter = std::find(mOrphanParents.begin(), mOrphanParents.end(), parent_info); if (iter != mOrphanParents.end()) diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h index 547ef9fb2d..7bb8f36ca3 100644 --- a/indra/newview/llviewerobjectlist.h +++ b/indra/newview/llviewerobjectlist.h @@ -1,6 +1,6 @@ /** * @file llviewerobjectlist.h - * @brief Description of LLViewerObjectList class. + * @brief Central manager for all viewer-side objects in Second Life's virtual world. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code @@ -44,113 +44,658 @@ class LLNetMap; class LLDebugBeacon; class LLVOCacheEntry; -const U32 CLOSE_BIN_SIZE = 10; -const U32 NUM_BINS = 128; +const U32 CLOSE_BIN_SIZE = 10; /// Size threshold for "close" objects (unused in current implementation) +const U32 NUM_BINS = 128; /// Number of bins for lazy texture update cycling -// GL name = position in object list + GL_NAME_INDEX_OFFSET so that -// we can have special numbers like zero. -const U32 GL_NAME_LAND = 0; -const U32 GL_NAME_PARCEL_WALL = 1; +/// Special GL names for non-object selections +const U32 GL_NAME_LAND = 0; /// Reserved GL name for land/terrain +const U32 GL_NAME_PARCEL_WALL = 1; /// Reserved GL name for parcel boundaries +/// Offset applied to object indices to create GL names, avoiding reserved values const U32 GL_NAME_INDEX_OFFSET = 10; +/** + * @brief Manages all viewer-side objects in the virtual world. + * + * This is the master registry for everything you can see in Second Life - from + * avatars to prims to particles. It serves as the central hub that tracks, + * updates, and manages the lifecycle of all objects in your viewing area, + * keeping the virtual world synchronized between the server and your viewer. + * + * Primary responsibilities: + * - Object lookup: Fast UUID-based lookups for avatars, prims, and UI elements + * - Frame updates: Per-frame object updates, texture priority, and animation + * - Object lifecycle: Creation from server messages, destruction on region changes + * - Cost tracking: Physics costs, streaming costs, and land impact calculations + * - Parent-child relationships: Handling linksets and attachment hierarchies + * - Debug visualization: Beacons and visual markers for development + * + * Performance note: This class is on the hot path for frame updates, so many + * operations are optimized for speed over memory usage. The findObject() method + * is called hundreds of times per frame across the codebase. + * + * @code + * // Most common usage - finding objects by UUID + * LLViewerObject* avatar = gObjectList.findObject(avatar_uuid); + * if (avatar && avatar->isAvatar()) { + * // Process avatar-specific logic + * } + * + * // Creating UI avatars for preview windows + * LLVOAvatar* preview = (LLVOAvatar*)gObjectList.createObjectViewer( + * LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR); + * + * // Per-frame updates (called from main loop) + * gObjectList.update(gAgent); + * gObjectList.updateApparentAngles(gAgent); + * @endcode + */ class LLViewerObjectList { public: + /** + * @brief Constructs the object list manager. + * + * Initializes all the internal tracking structures. There should only be one + * instance of this class (gObjectList) for the entire viewer session. + */ LLViewerObjectList(); + + /** + * @brief Destroys the object list and cleans up all tracked objects. + * + * This is only called on viewer shutdown. It ensures all objects are + * properly destroyed and references are cleaned up. + */ ~LLViewerObjectList(); + /** + * @brief Forcefully destroys all objects and clears all tracking structures. + * + * This completely resets the object system by destroying all objects. Use this + * when you need a clean slate, such as during a major disconnect or before + * shutdown. After calling this, the object list is empty but still usable. + */ void destroy(); - // For internal use only. Does NOT take a local id, takes an index into - // an internal dynamic array. + /** + * @brief Gets an object by its index in the internal array. + * + * This is for internal use only - most code should use findObject() instead. + * The index is NOT a local ID, it's the position in our mObjects array. + * Dead objects will return NULL even if they're still in the array. + * + * @param index Position in the mObjects array + * @return The object at that index, or NULL if dead/invalid + */ inline LLViewerObject *getObject(const S32 index); + /** + * @brief Finds an object by its UUID. + * + * This is THE most frequently used method in the entire object system. + * It's called hundreds of times per frame for chat, UI updates, script + * messages, hover tooltips, and more. Uses an efficient hash map for O(1) + * lookups. Returns NULL for null UUIDs, non-existent objects, or offline avatars. + * + * Common uses: + * - Finding chat speakers for name tags + * - Looking up script dialog sources + * - Resolving object references in messages + * - Finding selected objects for manipulation + * + * @param id The UUID of the object to find + * @return The object if found and valid, NULL otherwise + * + * @code + * // Typical pattern - always check for NULL + * if (LLViewerObject* obj = gObjectList.findObject(object_id)) { + * // Object exists and is valid + * obj->setDebugText("Found!"); + * } + * @endcode + */ inline LLViewerObject *findObject(const LLUUID &id); - LLViewerObject *createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp, S32 flags = 0); // Create a viewer-side object + /** + * @brief Creates a viewer-side object (not from server data). + * + * Use this when you need to create an object locally, like UI attachments + * or temporary visual effects. The object gets a generated UUID and is + * added to all the tracking structures. + * + * @param pcode The primitive code (LL_PCODE_VOLUME, LL_PCODE_AVATAR, etc.) + * @param regionp The region this object belongs to + * @param flags Creation flags (usually 0) + * @return The newly created object, or NULL on failure + */ + LLViewerObject *createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp, S32 flags = 0); + /** + * @brief Creates an object from cached data. + * + * This is used when loading objects from the region's object cache on startup + * or region crossing. The object already has a UUID and local ID from when + * it was previously in view. + * + * @param pcode The primitive code + * @param regionp The region this object belongs to + * @param uuid The object's UUID from cache + * @param local_id The object's local ID from cache + * @return The newly created object, or NULL on failure + */ LLViewerObject *createObjectFromCache(const LLPCode pcode, LLViewerRegion *regionp, const LLUUID &uuid, const U32 local_id); + /** + * @brief Creates an object from server update data. + * + * This is the main creation path for objects coming from the simulator. + * It sets up all the ID mappings and adds the object to the region's + * created list for further processing. + * + * @param pcode The primitive code + * @param regionp The region this object belongs to + * @param uuid The object's UUID (can be null to generate one) + * @param local_id The object's local ID from the server + * @param sender The host that sent this update + * @return The newly created object, or NULL on failure + */ LLViewerObject *createObject(const LLPCode pcode, LLViewerRegion *regionp, const LLUUID &uuid, const U32 local_id, const LLHost &sender); - LLViewerObject *replaceObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp); // TomY: hack to switch VO instances on the fly - + /** + * @brief Replaces an existing object with a new one of a different type. + * + * This is a workaround to handle type changes on the fly (such as when an + * avatar sits on an object and needs to change type). The old object is + * marked dead and a new one is created with the same UUID and local ID. + * + * @param id The UUID of the object to replace + * @param pcode The new primitive code + * @param regionp The region for the new object + * @return The replacement object, or NULL if original not found + */ + LLViewerObject *replaceObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp); + + /** + * @brief Marks an object for destruction. + * + * This doesn't immediately destroy the object - it marks it as dead and + * schedules it for cleanup. This is important because other parts of the + * system might still have pointers to it. The actual cleanup happens in + * cleanDeadObjects(). + * + * Special case: gAgentAvatarp is never killed unless the region is NULL + * (which means we're logging out). + * + * @param objectp The object to kill + * @return true if the object was marked dead, false if it was protected + */ bool killObject(LLViewerObject *objectp); - void killObjects(LLViewerRegion *regionp); // Kill all objects owned by a particular region. + + /** + * @brief Kills all objects belonging to a specific region. + * + * Used when a region is going away (disconnect, crossing boundaries, etc.). + * This immediately calls cleanDeadObjects() because the region is becoming + * invalid and we can't leave dead objects pointing to it. + * + * @param regionp The region whose objects should be killed + */ + void killObjects(LLViewerRegion *regionp); + + /** + * @brief Kills every object in the system. + * + * Only used during global destruction. This is the final cleanup that + * ensures everything is properly destroyed before the viewer exits. + */ void killAllObjects(); - void cleanDeadObjects(const bool use_timer = true); // Clean up the dead object list. - - // Simulator and viewer side object updates... + /** + * @brief Removes dead objects from all tracking structures. + * + * This is where dead objects are actually deleted. It's called periodically + * to clean up objects that were marked dead by killObject(). Uses an + * efficient algorithm that moves all dead objects to the end of the list + * before erasing them in one batch. + * + * @param use_timer If true, limits cleanup time (not used currently) + */ + void cleanDeadObjects(const bool use_timer = true); + + /** + * @brief Core update processing for all object updates. + * + * This is the heart of object synchronization. It processes update messages + * from the server, updates the object's properties, handles parent-child + * relationships, and manages the active/inactive state. Every object update + * eventually flows through here. + * + * @param objectp The object to update + * @param data User data from the message system + * @param block Which block in the message this update came from + * @param update_type Type of update (OUT_FULL, OUT_TERSE_IMPROVED, etc.) + * @param dpp Data packer containing compressed update data (can be NULL) + * @param justCreated true if this object was just created + * @param from_cache true if this update is from cache, not network + */ void processUpdateCore(LLViewerObject* objectp, void** data, U32 block, const EObjectUpdateType update_type, LLDataPacker* dpp, bool justCreated, bool from_cache = false); + /** + * @brief Processes a cached object update. + * + * When entering a region, we often have cached data about objects from + * previous visits. This creates or updates objects from that cache data, + * which is much faster than waiting for full updates from the server. + * + * @param entry The cache entry containing object data + * @param regionp The region this cached object belongs to + * @return The created/updated object, or NULL on failure + */ LLViewerObject* processObjectUpdateFromCache(LLVOCacheEntry* entry, LLViewerRegion* regionp); + /** + * @brief Processes object updates from the network. + * + * This is the main entry point for object updates from the simulator. + * It handles both compressed and uncompressed updates, creates new objects + * as needed, and delegates to processUpdateCore() for the actual work. + * + * @param mesgsys The message system containing the update + * @param user_data Passed through to update handlers + * @param update_type Type of update (full, terse, etc.) + * @param compressed Whether the data is compressed + */ void processObjectUpdate(LLMessageSystem *mesgsys, void **user_data, EObjectUpdateType update_type, bool compressed=false); + /** + * @brief Processes compressed object updates. + * + * Just a wrapper that calls processObjectUpdate() with compressed=true. + * Compressed updates use zlib to reduce bandwidth. + * + * @param mesgsys The message system containing the update + * @param user_data Passed through to update handlers + * @param update_type Type of update + */ void processCompressedObjectUpdate(LLMessageSystem *mesgsys, void **user_data, EObjectUpdateType update_type); + + /** + * @brief Processes cached object probe messages. + * + * The server sends these to check if we have objects in our cache. + * We respond with cache hits/misses so the server knows what to send. + * + * @param mesgsys The message system containing the probe + * @param user_data Passed through to update handlers + * @param update_type Type of update + */ void processCachedObjectUpdate(LLMessageSystem *mesgsys, void **user_data, EObjectUpdateType update_type); + /** + * @brief Updates texture priorities based on viewing angle and distance. + * + * Called every frame from LLAppViewer::idle(). This does lazy updates + * of object texture priorities - we can't update every object every frame + * (too expensive), so we cycle through chunks of the object list. This + * keeps textures loading smoothly without killing performance. Also handles + * texture priority boosts for selected objects. + * + * @param agent The agent to calculate angles relative to + */ void updateApparentAngles(LLAgent &agent); + /** + * @brief Main update function called every frame. + * + * Called from LLAppViewer::idle() immediately after gObjectList.updateApparentAngles(). + * This is the core frame update that keeps all objects synchronized: + * + * - Updates global frame timers and interpolation settings + * - Calls idleUpdate on all active objects (avatars, moving objects, particles) + * - Updates flexible objects and animated textures if enabled + * - Triggers asynchronous fetches for object costs and physics flags + * - Updates render complexity calculations + * - Collects frame statistics for performance monitoring + * + * The active object list typically contains: + * - All avatars in view + * - Moving objects (vehicles, physical objects) + * - Animating objects (texture animations, particles) + * - Selected objects being edited + * + * Performance note: This is one of the most performance-critical functions + * in the viewer. The active object list is carefully maintained to minimize + * the number of objects we need to update each frame. + * + * @param agent The agent for position calculations + */ void update(LLAgent &agent); + /** + * @brief Fetches object resource costs from the server. + * + * Object costs include physics cost, streaming cost, and land impact. + * This batches requests to avoid hammering the server. Called periodically + * from update() when there are stale costs to fetch. + */ void fetchObjectCosts(); + + /** + * @brief Fetches physics properties from the server. + * + * Gets physics shape type, density, friction, restitution, and gravity + * multiplier for objects. Like fetchObjectCosts(), this batches requests + * for efficiency. + */ void fetchPhysicsFlags(); + /** + * @brief Marks an object as needing a cost update. + * + * Adds the object (and its parent if it's a child) to the stale cost + * list. The actual fetch happens later in fetchObjectCosts(). + * + * @param object The object needing a cost update + */ void updateObjectCost(LLViewerObject* object); + + /** + * @brief Updates an object's cached cost values. + * + * Called when we receive cost data from the server. Updates both the + * individual object cost and the total linkset cost. + * + * @param object_id UUID of the object + * @param object_cost This object's individual cost + * @param link_cost Total cost of the entire linkset + * @param physics_cost This object's physics cost + * @param link_physics_cost Total physics cost of the linkset + */ void updateObjectCost(const LLUUID& object_id, F32 object_cost, F32 link_cost, F32 physics_cost, F32 link_physics_cost); + + /** + * @brief Handles failed cost fetches. + * + * Removes the object from pending lists so we don't keep retrying. + * + * @param object_id UUID of the object that failed + */ void onObjectCostFetchFailure(const LLUUID& object_id); + /** + * @brief Marks an object as needing physics flag updates. + * + * Adds the object to the stale physics flags list for batch fetching. + * + * @param object The object needing physics updates + */ void updatePhysicsFlags(const LLViewerObject* object); + + /** + * @brief Handles failed physics flag fetches. + * + * Removes the object from pending lists so we don't keep retrying. + * + * @param object_id UUID of the object that failed + */ void onPhysicsFlagsFetchFailure(const LLUUID& object_id); + + /** + * @brief Updates an object's physics shape type. + * + * Called when we receive physics data from the server. + * + * @param object_id UUID of the object + * @param type Physics shape type (convex hull, box, etc.) + */ void updatePhysicsShapeType(const LLUUID& object_id, S32 type); + + /** + * @brief Updates all physics properties for an object. + * + * Called when we receive detailed physics data from the server. + * + * @param object_id UUID of the object + * @param density Mass per unit volume + * @param friction Resistance to sliding motion + * @param restitution Bounciness (0 = no bounce, 1 = perfect bounce) + * @param gravity_multiplier Scaling factor for gravity effects + */ void updatePhysicsProperties(const LLUUID& object_id, F32 density, F32 friction, F32 restitution, F32 gravity_multiplier); + /** + * @brief Shifts all objects by a global offset. + * + * This happens when we cross region boundaries and need to recenter + * the coordinate system. Every object's position caches are updated + * and drawables are marked for shifting. + * + * @param offset The 3D offset to apply to all objects + */ void shiftObjects(const LLVector3 &offset); + + /** + * @brief Forces all objects to recalculate their spatial partitions. + * + * This is expensive but necessary after major scene changes. It ensures + * all objects are in the correct octree nodes for efficient culling. + */ void repartitionObjects(); bool hasMapObjectInRegion(LLViewerRegion* regionp) ; void clearAllMapObjectsInRegion(LLViewerRegion* regionp) ; + /** + * @brief Renders objects on the minimap. + * + * Draws colored dots for objects based on ownership and height relative + * to water. You own = yellow/green, others own = red/blue, with above/below + * water variants. Megaprims are clamped to reasonable sizes. + * + * @param netmap The minimap to render onto + */ void renderObjectsForMap(LLNetMap &netmap); + + /** + * @brief Renders object bounding boxes (currently unused). + * + * @param center Center point for rendering + */ void renderObjectBounds(const LLVector3 ¢er); + /** + * @brief Adds a debug beacon at a specific location. + * + * Debug beacons are vertical lines with text labels, useful for marking + * positions during debugging. They're rendered in renderObjectBeacons(). + * + * @param pos_agent Position in agent coordinates + * @param string Text to display at the beacon + * @param color Color of the beacon line + * @param text_color Color of the text label + * @param line_width Width of the beacon line + */ void addDebugBeacon(const LLVector3 &pos_agent, const std::string &string, const LLColor4 &color=LLColor4(1.f, 0.f, 0.f, 0.5f), const LLColor4 &text_color=LLColor4(1.f, 1.f, 1.f, 1.f), S32 line_width = 1); + + /** + * @brief Renders all active debug beacons. + * + * Called during the render pass to draw all beacons added with addDebugBeacon(). + */ void renderObjectBeacons(); + + /** + * @brief Clears all debug beacons. + */ void resetObjectBeacons(); + /** + * @brief Marks all objects as having dirty inventory. + * + * Forces all objects to refresh their inventory contents. This is used + * when global inventory state changes that might affect object contents. + */ void dirtyAllObjectInventory(); + /** + * @brief Removes an object from the active update list. + * + * This is an internal method that maintains the integrity of the active + * list by swapping the last element into the removed object's position. + * + * @param objectp The object to remove + */ void removeFromActiveList(LLViewerObject* objectp); + + /** + * @brief Updates whether an object should be on the active list. + * + * Active objects get their idleUpdate() called every frame. This checks + * if an object should be active (based on motion, scripts, etc.) and + * adds/removes it from the active list as needed. + * + * @param objectp The object to check + */ void updateActive(LLViewerObject *objectp); + /** + * @brief Updates avatar visibility culling. + * + * Part of the visibility optimization system. This is called to update + * which avatars should be visible based on distance and other factors. + */ void updateAvatarVisibility(); + /** + * @brief Gets the total number of objects being tracked. + * + * This includes all objects - active, inactive, even dead ones that + * haven't been cleaned up yet. + * + * @return Total object count + */ inline S32 getNumObjects() { return (S32) mObjects.size(); } + + /** + * @brief Gets the number of active objects. + * + * Active objects are ones that need per-frame updates (moving objects, + * animated objects, avatars, etc.). This number directly impacts frame rate. + * + * @return Active object count + */ inline S32 getNumActiveObjects() { return (S32) mActiveObjects.size(); } + /** + * @brief Adds an object to the minimap tracking list. + * + * Objects on this list are rendered as dots on the minimap. + * + * @param objectp The object to add + */ void addToMap(LLViewerObject *objectp); + + /** + * @brief Removes an object from the minimap tracking list. + * + * @param objectp The object to remove + */ void removeFromMap(LLViewerObject *objectp); + /** + * @brief Clears debug text from all objects. + * + * Removes any debug hover text that was added to objects for debugging. + * This restores objects to showing only their normal hover text. + */ void clearDebugText(); - //////////////////////////////////////////// - // - // Only accessed by markDead in LLViewerObject + /** + * @brief Removes all references to a dead object. + * + * Only called by LLViewerObject::markDead(). This removes the object + * from all tracking structures (UUID map, local ID table, active list, + * map list) but doesn't delete it. The actual deletion happens later + * in cleanDeadObjects(). + * + * @param objectp The dead object to clean up + */ void cleanupReferences(LLViewerObject *objectp); - S32 findReferences(LLDrawable *drawablep) const; // Find references to drawable in all objects, and return value. - + /** + * @brief Counts references to a drawable from all objects. + * + * Used for debugging to find what objects are referencing a specific + * drawable. Searches through all objects and their children. + * + * @param drawablep The drawable to search for + * @return Number of references found + */ + S32 findReferences(LLDrawable *drawablep) const; + + /** + * @brief Gets the number of parent IDs we're waiting for. + * + * These are parent objects that children have referenced but haven't + * arrived yet. Used for debugging parent-child synchronization issues. + * + * @return Number of pending parent IDs + */ S32 getOrphanParentCount() const { return (S32) mOrphanParents.size(); } + + /** + * @brief Gets the number of orphaned child objects. + * + * Orphans are objects whose parents haven't been created yet. This + * happens during region crossing or when updates arrive out of order. + * + * @return Number of orphaned objects + */ S32 getOrphanCount() const { return mNumOrphans; } + + /** + * @brief Gets the current avatar count. + * + * Updated every frame in update(). Useful for performance monitoring. + * + * @return Number of avatars in view + */ S32 getAvatarCount() const { return mNumAvatars; } + /** + * @brief Marks an object as orphaned. + * + * Called when we get a child object whose parent hasn't arrived yet. + * The child is made invisible and added to lists to be reconnected + * when the parent arrives. + * + * @param childp The orphaned child object + * @param parent_id Local ID of the missing parent + * @param ip IP address of the host that sent this + * @param port Port of the host that sent this + */ void orphanize(LLViewerObject *childp, U32 parent_id, U32 ip, U32 port); + + /** + * @brief Reconnects orphaned children to a parent. + * + * When a parent object arrives, this searches for any orphaned children + * waiting for it and reestablishes the parent-child relationships. + * + * @param objectp The parent object to check + * @param ip IP address of the host + * @param port Port of the host + */ void findOrphans(LLViewerObject* objectp, U32 ip, U32 port); public: - // Class for keeping track of orphaned objects + /** + * @brief Tracks parent-child relationships for orphaned objects. + * + * When child objects arrive before their parents, we need to remember + * the relationship so we can connect them later. This simple struct + * stores the parent ID info and child UUID for matching. + */ class OrphanInfo { public: @@ -163,101 +708,191 @@ class LLViewerObjectList }; - U32 mCurBin; // Current bin we're working on... + U32 mCurBin; /// Current bin index for lazy texture updates (cycles 0 to NUM_BINS-1) - // Statistics data (see also LLViewerStats) + /// Number of new objects created this frame (for statistics) S32 mNumNewObjects; - // if we paused in the last frame - // used to discount stats from this frame + /// Whether the last frame was paused - used to avoid skewing frame statistics bool mWasPaused; + /** + * @brief Looks up a UUID from local ID and host information. + * + * Objects are identified by UUID globally, but by local ID within a + * region. This translates from the region-specific ID to the global UUID. + * + * @param[out] id Filled with the UUID if found, null otherwise + * @param local_id The region-local ID + * @param ip IP address of the region + * @param port Port of the region + */ void getUUIDFromLocal(LLUUID &id, const U32 local_id, const U32 ip, const U32 port); + + /** + * @brief Creates a mapping from local ID to UUID. + * + * This is called when we know both IDs for an object. It allows future + * lookups from local ID (which is what most network messages use) to + * the UUID (which is what the viewer uses internally). + * + * The method assigns a unique region index to the object if this is the first + * time we've seen this IP:port combination. This index is used to create a + * globally unique identifier by combining it with the local ID. + * + * @param id The object's UUID + * @param local_id The object's local ID within its region + * @param ip IP address of the region + * @param port Port of the region + * @param objectp The object itself (for setting region index) + */ void setUUIDAndLocal(const LLUUID &id, const U32 local_id, const U32 ip, const U32 port, - LLViewerObject* objectp); // Requires knowledge of message system info! - + LLViewerObject* objectp); + + /** + * @brief Removes an object from the local ID lookup table. + * + * This breaks the mapping from local ID to UUID. Called when an object + * is destroyed or changes regions. + * + * @param objectp The object to remove + * @return true if the object was in the table and removed + */ bool removeFromLocalIDTable(LLViewerObject* objectp); - // Used ONLY by the orphaned object code. + + /** + * @brief Creates a unique index from local ID and host info. + * + * Used by the orphan system to create unique identifiers for parent + * objects we haven't seen yet. The index combines the region's host + * info with the local ID. + * + * @param local_id The local ID + * @param ip IP address + * @param port Port number + * @return 64-bit index combining all the information + */ U64 getIndex(const U32 local_id, const U32 ip, const U32 port); - S32 mNumUnknownUpdates; - S32 mNumDeadObjectUpdates; - S32 mNumDeadObjects; + S32 mNumUnknownUpdates; /// Count of updates received for objects we don't know about (usually means out-of-order messages) + S32 mNumDeadObjectUpdates; /// Count of updates received for objects already marked dead (should be rare) + S32 mNumDeadObjects; /// Current count of objects marked dead but not yet cleaned up protected: - std::vector mOrphanParents; // LocalID/ip,port of orphaned objects - std::vector mOrphanChildren; // UUID's of orphaned objects - S32 mNumOrphans; - S32 mNumAvatars; + std::vector mOrphanParents; /// Parent IDs we're waiting for + std::vector mOrphanChildren; /// Children waiting for their parents + S32 mNumOrphans; /// Current count of orphaned objects + S32 mNumAvatars; /// Current count of avatars in view typedef std::vector > vobj_list_t; - vobj_list_t mObjects; - std::vector > mActiveObjects; + vobj_list_t mObjects; /// Master list of all objects + std::vector > mActiveObjects; /// Objects needing per-frame updates - vobj_list_t mMapObjects; + vobj_list_t mMapObjects; /// Objects to show on minimap - uuid_set_t mDeadObjects; + uuid_set_t mDeadObjects; /// UUIDs of objects marked for deletion - std::map > mUUIDObjectMap; + std::map > mUUIDObjectMap; /// Fast lookup from UUID to object - //set of objects that need to update their cost - uuid_set_t mStaleObjectCost; - uuid_set_t mPendingObjectCost; + uuid_set_t mStaleObjectCost; /// Objects needing cost updates + uuid_set_t mPendingObjectCost; /// Objects with cost fetches in progress - //set of objects that need to update their physics flags - uuid_set_t mStalePhysicsFlags; - uuid_set_t mPendingPhysicsFlags; + uuid_set_t mStalePhysicsFlags; /// Objects needing physics updates + uuid_set_t mPendingPhysicsFlags; /// Objects with physics fetches in progress - std::vector mDebugBeacons; + std::vector mDebugBeacons; /// Debug visualization markers - S32 mCurLazyUpdateIndex; + S32 mCurLazyUpdateIndex; /// Current position in lazy update cycle - static U32 sSimulatorMachineIndex; - std::map mIPAndPortToIndex; + static U32 sSimulatorMachineIndex; /// Next available region index (starts at 1, not 0, for fast lookups) + std::map mIPAndPortToIndex; /// Maps (IP<<32)|port to unique region index - std::map mIndexAndLocalIDToUUID; + std::map mIndexAndLocalIDToUUID; /// Maps (region_index<<32)|local_id to object UUID friend class LLViewerObject; private: + /** + * @brief Reports object cost fetch failures. + * + * Called when the cost fetch coroutine fails. Processes the list of + * failed objects and calls onObjectCostFetchFailure for each. + * + * @param objectList LLSD array of object UUIDs that failed + */ static void reportObjectCostFailure(LLSD &objectList); + + /** + * @brief Coroutine that fetches object costs from the server. + * + * This runs asynchronously to batch-fetch costs for multiple objects. + * It handles the HTTP request, processes responses, and updates object + * costs or reports failures. + * + * @param url The capability URL for fetching costs + */ void fetchObjectCostsCoro(std::string url); - static void reportPhysicsFlagFailure(LLSD &obejectList); + /** + * @brief Reports physics flag fetch failures. + * + * @param objectList LLSD array of object UUIDs that failed + */ + static void reportPhysicsFlagFailure(LLSD &objectList); + + /** + * @brief Coroutine that fetches physics flags from the server. + * + * Similar to fetchObjectCostsCoro but for physics properties. Note the + * typo in the method name (Phisics vs Physics) is kept for compatibility. + * + * @param url The capability URL for fetching physics data + */ void fetchPhisicsFlagsCoro(std::string url); }; +/** + * @brief Visual debugging beacon rendered in the 3D world. + * + * These are the vertical lines with text labels you see when debugging. + * They're useful for marking positions, showing where things are happening, + * or visualizing invisible data like physics shapes or region boundaries. + */ class LLDebugBeacon { public: + /** + * @brief Destructor ensures HUD object is properly cleaned up. + */ ~LLDebugBeacon(); - - LLVector3 mPositionAgent; - std::string mString; - LLColor4 mColor; - LLColor4 mTextColor; - S32 mLineWidth; - LLPointer mHUDObject; + + LLVector3 mPositionAgent; /// Position in agent-relative coordinates + std::string mString; /// Text label to display + LLColor4 mColor; /// Color of the beacon line + LLColor4 mTextColor; /// Color of the text label + S32 mLineWidth; /// Width of the beacon line in pixels + LLPointer mHUDObject; /// HUD object for rendering }; - - -// Global object list +/** + * @brief Global object list instance. + * + * This is the one and only object list for the viewer. All object management + * goes through this global instance. It's created at startup and destroyed + * at shutdown. + */ extern LLViewerObjectList gObjectList; // Inlines -/** - * Note: - * it will return NULL for offline avatar_id - */ + inline LLViewerObject *LLViewerObjectList::findObject(const LLUUID &id) { if (id.isNull()) @@ -278,7 +913,7 @@ inline LLViewerObject *LLViewerObjectList::getObject(const S32 index) objectp = mObjects[index]; if (objectp->isDead()) { - //LL_WARNS() << "Dead object " << objectp->mID << " in getObject" << LL_ENDL; + // Dead objects are still in the list until cleanDeadObjects() runs return NULL; } return objectp;