diff options
| -rw-r--r-- | indra/llcommon/llsdutil.h | 55 | ||||
| -rw-r--r-- | indra/newview/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | indra/newview/groupchatlistener.cpp | 84 | ||||
| -rw-r--r-- | indra/newview/groupchatlistener.h | 15 | ||||
| -rw-r--r-- | indra/newview/llagentlistener.cpp | 387 | ||||
| -rw-r--r-- | indra/newview/llagentlistener.h | 16 | ||||
| -rw-r--r-- | indra/newview/llappearancelistener.cpp | 158 | ||||
| -rw-r--r-- | indra/newview/llappearancelistener.h | 46 | ||||
| -rw-r--r-- | indra/newview/llappearancemgr.cpp | 8 | ||||
| -rw-r--r-- | indra/newview/llappearancemgr.h | 1 | ||||
| -rw-r--r-- | indra/newview/llfloaterimnearbychat.cpp | 3 | ||||
| -rw-r--r-- | indra/newview/llfloaterimnearbychatlistener.cpp | 34 | ||||
| -rw-r--r-- | indra/newview/llfloaterimnearbychatlistener.h | 6 | ||||
| -rw-r--r-- | indra/newview/llgroupactions.cpp | 2 | ||||
| -rw-r--r-- | indra/newview/llviewerparcelmgr.cpp | 24 | ||||
| -rw-r--r-- | indra/newview/llviewerparcelmgr.h | 4 | ||||
| -rw-r--r-- | indra/newview/llwearableitemslist.cpp | 10 | ||||
| -rw-r--r-- | indra/newview/llwearableitemslist.h | 9 | 
18 files changed, 743 insertions, 121 deletions
| diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index 38bbe19ddd..c31030c5ea 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -553,6 +553,61 @@ LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter  } // namespace llsd +/***************************************************************************** + *   toArray(), toMap() + *****************************************************************************/ +namespace llsd +{ + +// For some T convertible to LLSD, given std::vector<T> myVec, +// toArray(myVec) returns an LLSD array whose entries correspond to the +// items in myVec. +// For some U convertible to LLSD, given function U xform(const T&), +// toArray(myVec, xform) returns an LLSD array whose every entry is +// xform(item) of the corresponding item in myVec. +// toArray() actually works with any container<C> usable with range +// 'for', not just std::vector. +// (Once we get C++20 we can use std::identity instead of this default lambda.) +template<typename C, typename FUNC> +LLSD toArray(const C& container, FUNC&& func = [](const auto& arg) { return arg; }) +{ +    LLSD array; +    for (const auto& item : container) +    { +        array.append(std::forward<FUNC>(func)(item)); +    } +    return array; +} + +// For some T convertible to LLSD, given std::map<std::string, T> myMap, +// toMap(myMap) returns an LLSD map whose entries correspond to the +// (key, value) pairs in myMap. +// For some U convertible to LLSD, given function +// std::pair<std::string, U> xform(const std::pair<std::string, T>&), +// toMap(myMap, xform) returns an LLSD map whose every entry is +// xform(pair) of the corresponding (key, value) pair in myMap. +// toMap() actually works with any container usable with range 'for', not +// just std::map. It need not even be an associative container, as long as +// you pass an xform function that returns std::pair<std::string, U>. +// (Once we get C++20 we can use std::identity instead of this default lambda.) +template<typename C, typename FUNC> +LLSD toMap(const C& container, FUNC&& func = [](const auto& arg) { return arg; }) +{ +    LLSD map; +    for (const auto& pair : container) +    { +        const auto& [key, value] = std::forward<FUNC>(func)(pair); +        map[key] = value; +    } +    return map; +} + +} // namespace llsd + +/***************************************************************************** + *   boost::hash<LLSD> + *****************************************************************************/ +  // Specialization for generating a hash value from an LLSD block.  namespace boost  { diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index ed29911a43..fff1597fd9 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -92,6 +92,7 @@ set(viewer_SOURCE_FILES      llagentwearables.cpp      llanimstatelabels.cpp      llappcorehttp.cpp +    llappearancelistener.cpp      llappearancemgr.cpp      llappviewer.cpp      llappviewerlistener.cpp @@ -761,6 +762,7 @@ set(viewer_HEADER_FILES      llanimstatelabels.h      llappcorehttp.h      llappearance.h +    llappearancelistener.h      llappearancemgr.h      llappviewer.h      llappviewerlistener.h diff --git a/indra/newview/groupchatlistener.cpp b/indra/newview/groupchatlistener.cpp index 43507f13e9..ed9e34d1bf 100644 --- a/indra/newview/groupchatlistener.cpp +++ b/indra/newview/groupchatlistener.cpp @@ -2,11 +2,11 @@   * @file   groupchatlistener.cpp   * @author Nat Goodspeed   * @date   2011-04-11 - * @brief  Implementation for groupchatlistener. + * @brief  Implementation for LLGroupChatListener.   * - * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * $LicenseInfo:firstyear=2024&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2011, Linden Research, Inc. + * Copyright (C) 2024, Linden Research, Inc.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -34,43 +34,69 @@  // std headers  // external library headers  // other Linden headers +#include "llchat.h"  #include "llgroupactions.h"  #include "llimview.h" +LLGroupChatListener::LLGroupChatListener(): +    LLEventAPI("GroupChat", +               "API to enter, leave, send and intercept group chat messages") +{ +    add("startGroupChat", +        "Enter a group chat in group with UUID [\"group_id\"]\n" +        "Assumes the logged-in agent is already a member of this group.", +        &LLGroupChatListener::startGroupChat, +        llsd::map("group_id", LLSD())); +    add("leaveGroupChat", +        "Leave a group chat in group with UUID [\"group_id\"]\n" +        "Assumes a prior successful startIM request.", +        &LLGroupChatListener::leaveGroupChat, +        llsd::map("group_id", LLSD())); +    add("sendGroupIM", +        "send a [\"message\"] to group with UUID [\"group_id\"]", +        &LLGroupChatListener::sendGroupIM, +        llsd::map("message", LLSD(), "group_id", LLSD())); +} -namespace { -    void startIm_wrapper(LLSD const & event) +bool is_in_group(LLEventAPI::Response &response, const LLSD &data) +{ +    if (!LLGroupActions::isInGroup(data["group_id"]))      { -        LLUUID session_id = LLGroupActions::startIM(event["id"].asUUID()); -        sendReply(LLSDMap("session_id", LLSD(session_id)), event); +        response.error(stringize("You are not the member of the group:", std::quoted(data["group_id"].asString()))); +        return false;      } +    return true; +} -    void send_message_wrapper(const std::string& text, const LLUUID& session_id, const LLUUID& group_id) +void LLGroupChatListener::startGroupChat(LLSD const &data) +{ +    Response response(LLSD(), data); +    if (!is_in_group(response, data)) +    { +        return; +    } +    if (LLGroupActions::startIM(data["group_id"]).isNull())      { -        LLIMModel::sendMessage(text, session_id, group_id, IM_SESSION_GROUP_START); +        return response.error(stringize("Failed to start group chat session ", std::quoted(data["group_id"].asString())));      }  } +void LLGroupChatListener::leaveGroupChat(LLSD const &data) +{ +    Response response(LLSD(), data); +    if (is_in_group(response, data)) +    { +        LLGroupActions::endIM(data["group_id"].asUUID()); +    } +} -GroupChatListener::GroupChatListener(): -    LLEventAPI("GroupChat", -               "API to enter, leave, send and intercept group chat messages") +void LLGroupChatListener::sendGroupIM(LLSD const &data)  { -    add("startIM", -        "Enter a group chat in group with UUID [\"id\"]\n" -        "Assumes the logged-in agent is already a member of this group.", -        &startIm_wrapper); -    add("endIM", -        "Leave a group chat in group with UUID [\"id\"]\n" -        "Assumes a prior successful startIM request.", -        &LLGroupActions::endIM, -        llsd::array("id")); -    add("sendIM", -        "send a groupchat IM", -        &send_message_wrapper, -        llsd::array("text", "session_id", "group_id")); +    Response response(LLSD(), data); +    if (!is_in_group(response, data)) +    { +        return; +    } +    LLUUID group_id(data["group_id"]); +    LLIMModel::sendMessage(data["message"], gIMMgr->computeSessionID(IM_SESSION_GROUP_START, group_id), group_id, IM_SESSION_SEND);  } -/* -    static void sendMessage(const std::string& utf8_text, const LLUUID& im_session_id, -                                const LLUUID& other_participant_id, EInstantMessage dialog); -*/ diff --git a/indra/newview/groupchatlistener.h b/indra/newview/groupchatlistener.h index 3819ac59b7..14cd7266a3 100644 --- a/indra/newview/groupchatlistener.h +++ b/indra/newview/groupchatlistener.h @@ -26,15 +26,20 @@   * $/LicenseInfo$   */ -#if ! defined(LL_GROUPCHATLISTENER_H) -#define LL_GROUPCHATLISTENER_H +#if ! defined(LL_LLGROUPCHATLISTENER_H) +#define LL_LLGROUPCHATLISTENER_H  #include "lleventapi.h" -class GroupChatListener: public LLEventAPI +class LLGroupChatListener: public LLEventAPI  {  public: -    GroupChatListener(); +    LLGroupChatListener(); + +private: +    void startGroupChat(LLSD const &data); +    void leaveGroupChat(LLSD const &data); +    void sendGroupIM(LLSD const &data);  }; -#endif /* ! defined(LL_GROUPCHATLISTENER_H) */ +#endif /* ! defined(LL_LLGROUPCHATLISTENER_H) */ diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 0c120ae01d..5ddb87558a 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -31,19 +31,25 @@  #include "llagentlistener.h"  #include "llagent.h" +#include "llagentcamera.h" +#include "llavatarname.h" +#include "llavatarnamecache.h"  #include "llvoavatar.h"  #include "llcommandhandler.h" +#include "llinventorymodel.h"  #include "llslurl.h"  #include "llurldispatcher.h" +#include "llviewercontrol.h"  #include "llviewernetwork.h"  #include "llviewerobject.h"  #include "llviewerobjectlist.h"  #include "llviewerregion.h" +#include "llvoavatarself.h"  #include "llsdutil.h"  #include "llsdutil_math.h"  #include "lltoolgrab.h"  #include "llhudeffectlookat.h" -#include "llagentcamera.h" +#include "llviewercamera.h"  LLAgentListener::LLAgentListener(LLAgent &agent)    : LLEventAPI("LLAgent", @@ -69,13 +75,6 @@ LLAgentListener::LLAgentListener(LLAgent &agent)      add("resetAxes",          "Set the agent to a fixed orientation (optionally specify [\"lookat\"] = array of [x, y, z])",          &LLAgentListener::resetAxes); -    add("getAxes", -        "Obsolete - use getPosition instead\n" -        "Send information about the agent's orientation on [\"reply\"]:\n" -        "[\"euler\"]: map of {roll, pitch, yaw}\n" -        "[\"quat\"]:  array of [x, y, z, w] quaternion values", -        &LLAgentListener::getAxes, -        LLSDMap("reply", LLSD()));      add("getPosition",          "Send information about the agent's position and orientation on [\"reply\"]:\n"          "[\"region\"]: array of region {x, y, z} position\n" @@ -87,33 +86,34 @@ LLAgentListener::LLAgentListener(LLAgent &agent)      add("startAutoPilot",          "Start the autopilot system using the following parameters:\n"          "[\"target_global\"]: array of target global {x, y, z} position\n" -        "[\"stop_distance\"]: target maxiumum distance from target [default: autopilot guess]\n" +        "[\"stop_distance\"]: maximum stop distance from target [default: autopilot guess]\n"          "[\"target_rotation\"]: array of [x, y, z, w] quaternion values [default: no target]\n"          "[\"rotation_threshold\"]: target maximum angle from target facing rotation [default: 0.03 radians]\n" -        "[\"behavior_name\"]: name of the autopilot behavior [default: \"\"]" -        "[\"allow_flying\"]: allow flying during autopilot [default: True]", -        //"[\"callback_pump\"]: pump to send success/failure and callback data to [default: none]\n" -        //"[\"callback_data\"]: data to send back during a callback [default: none]", -        &LLAgentListener::startAutoPilot); +        "[\"behavior_name\"]: name of the autopilot behavior [default: \"\"]\n" +        "[\"allow_flying\"]: allow flying during autopilot [default: True]\n" +        "event with [\"success\"] flag is sent to 'LLAutopilot' event pump, when auto pilot is terminated", +        &LLAgentListener::startAutoPilot, +        llsd::map("target_global", LLSD()));      add("getAutoPilot",          "Send information about current state of the autopilot system to [\"reply\"]:\n"          "[\"enabled\"]: boolean indicating whether or not autopilot is enabled\n"          "[\"target_global\"]: array of target global {x, y, z} position\n"          "[\"leader_id\"]: uuid of target autopilot is following\n" -        "[\"stop_distance\"]: target maximum distance from target\n" +        "[\"stop_distance\"]: maximum stop distance from target\n"          "[\"target_distance\"]: last known distance from target\n"          "[\"use_rotation\"]: boolean indicating if autopilot has a target facing rotation\n"          "[\"target_facing\"]: array of {x, y} target direction to face\n"          "[\"rotation_threshold\"]: target maximum angle from target facing rotation\n"          "[\"behavior_name\"]: name of the autopilot behavior",          &LLAgentListener::getAutoPilot, -        LLSDMap("reply", LLSD())); +        llsd::map("reply", LLSD()));      add("startFollowPilot",          "[\"leader_id\"]: uuid of target to follow using the autopilot system (optional with avatar_name)\n"          "[\"avatar_name\"]: avatar name to follow using the autopilot system (optional with leader_id)\n"          "[\"allow_flying\"]: allow flying during autopilot [default: True]\n" -        "[\"stop_distance\"]: target maxiumum distance from target [default: autopilot guess]", -        &LLAgentListener::startFollowPilot); +        "[\"stop_distance\"]: maximum stop distance from target [default: autopilot guess]", +        &LLAgentListener::startFollowPilot, +        llsd::map("reply", LLSD()));      add("setAutoPilotTarget",          "Update target for currently running autopilot:\n"          "[\"target_global\"]: array of target global {x, y, z} position", @@ -138,6 +138,69 @@ LLAgentListener::LLAgentListener(LLAgent &agent)          "[\"contrib\"]: user's land contribution to this group\n",          &LLAgentListener::getGroups,          LLSDMap("reply", LLSD())); +    //camera params are similar to LSL, see https://wiki.secondlife.com/wiki/LlSetCameraParams +    add("setCameraParams", +        "Set Follow camera params, and then activate it:\n" +        "[\"camera_pos\"]: vector3, camera position in region coordinates\n" +        "[\"focus_pos\"]: vector3, what the camera is aimed at (in region coordinates)\n" +        "[\"focus_offset\"]: vector3, adjusts the camera focus position relative to the target, default is (1, 0, 0)\n" +        "[\"distance\"]: float (meters), distance the camera wants to be from its target, default is 3\n" +        "[\"focus_threshold\"]: float (meters), sets the radius of a sphere around the camera's target position within which its focus is not affected by target motion, default is 1\n" +        "[\"camera_threshold\"]: float (meters), sets the radius of a sphere around the camera's ideal position within which it is not affected by target motion, default is 1\n" +        "[\"focus_lag\"]: float (seconds), how much the camera lags as it tries to aim towards the target, default is 0.1\n" +        "[\"camera_lag\"]: float (seconds), how much the camera lags as it tries to move towards its 'ideal' position, default is 0.1\n" +        "[\"camera_pitch\"]: float (degrees), adjusts the angular amount that the camera aims straight ahead vs. straight down, maintaining the same distance, default is 0\n" +        "[\"behindness_angle\"]: float (degrees), sets the angle in degrees within which the camera is not constrained by changes in target rotation, default is 10\n" +        "[\"behindness_lag\"]: float (seconds), sets how strongly the camera is forced to stay behind the target if outside of behindness angle, default is 0\n" +        "[\"camera_locked\"]: bool, locks the camera position so it will not move\n" +        "[\"focus_locked\"]: bool, locks the camera focus so it will not move", +        &LLAgentListener::setFollowCamParams); +    add("setFollowCamActive", +        "Turns on or off scripted control of the camera using boolean [\"active\"]", +        &LLAgentListener::setFollowCamActive, +        llsd::map("active", LLSD())); +    add("removeCameraParams", +        "Reset Follow camera params", +        &LLAgentListener::removeFollowCamParams); + +    add("playAnimation", +        "Play [\"item_id\"] animation locally (by default) or [\"inworld\"] (when set to true)", +        &LLAgentListener::playAnimation, +        llsd::map("item_id", LLSD(), "reply", LLSD())); +    add("stopAnimation", +        "Stop playing [\"item_id\"] animation", +        &LLAgentListener::stopAnimation, +        llsd::map("item_id", LLSD(), "reply", LLSD())); +    add("getAnimationInfo", +        "Return information about [\"item_id\"] animation", +        &LLAgentListener::getAnimationInfo, +        llsd::map("item_id", LLSD(), "reply", LLSD())); + +    add("getID", +        "Return your own avatar ID", +        &LLAgentListener::getID, +        llsd::map("reply", LLSD())); + +    add("getNearbyAvatarsList", +        "Return result set key [\"result\"] for nearby avatars in a range of [\"dist\"]\n" +        "if [\"dist\"] is not specified, 'RenderFarClip' setting is used\n" +        "reply contains \"result\" table with \"id\", \"name\", \"global_pos\", \"region_pos\", \"region_id\" fields", +        &LLAgentListener::getNearbyAvatarsList, +        llsd::map("reply", LLSD())); + +    add("getNearbyObjectsList", +        "Return result set key [\"result\"] for nearby objects in a range of [\"dist\"]\n" +        "if [\"dist\"] is not specified, 'RenderFarClip' setting is used\n" +        "reply contains \"result\" table with \"id\", \"global_pos\", \"region_pos\", \"region_id\" fields", +        &LLAgentListener::getNearbyObjectsList, +        llsd::map("reply", LLSD())); + +    add("getAgentScreenPos", +        "Return screen position of the [\"avatar_id\"] avatar or own avatar if not specified\n" +        "reply contains \"x\", \"y\" coordinates and \"onscreen\" flag to indicate if it's actually in within the current window\n" +        "avatar render position is used as the point", +        &LLAgentListener::getAgentScreenPos, +        llsd::map("reply", LLSD()));  }  void LLAgentListener::requestTeleport(LLSD const & event_data) const @@ -168,7 +231,7 @@ void LLAgentListener::requestSit(LLSD const & event_data) const      //mAgent.getAvatarObject()->sitOnObject();      // shamelessly ripped from llviewermenu.cpp:handle_sit_or_stand()      // *TODO - find a permanent place to share this code properly. - +    Response response(LLSD(), event_data);      LLViewerObject *object = NULL;      if (event_data.has("obj_uuid"))      { @@ -177,7 +240,13 @@ void LLAgentListener::requestSit(LLSD const & event_data) const      else if (event_data.has("position"))      {          LLVector3 target_position = ll_vector3_from_sd(event_data["position"]); -        object = findObjectClosestTo(target_position); +        object = findObjectClosestTo(target_position, true); +    } +    else +    { +        //just sit on the ground +        mAgent.setControlFlags(AGENT_CONTROL_SIT_ON_GROUND); +        return;      }      if (object && object->getPCode() == LL_PCODE_VOLUME) @@ -194,8 +263,7 @@ void LLAgentListener::requestSit(LLSD const & event_data) const      }      else      { -        LL_WARNS() << "LLAgent requestSit could not find the sit target: " -            << event_data << LL_ENDL; +        response.error("requestSit could not find the sit target");      }  } @@ -205,7 +273,7 @@ void LLAgentListener::requestStand(LLSD const & event_data) const  } -LLViewerObject * LLAgentListener::findObjectClosestTo( const LLVector3 & position ) const +LLViewerObject * LLAgentListener::findObjectClosestTo(const LLVector3 & position, bool sit_target) const  {      LLViewerObject *object = NULL; @@ -216,8 +284,13 @@ LLViewerObject * LLAgentListener::findObjectClosestTo( const LLVector3 & positio      while (cur_index < num_objects)      {          LLViewerObject * cur_object = gObjectList.getObject(cur_index++); -        if (cur_object) -        {   // Calculate distance from the target position +        if (cur_object && !cur_object->isAttachment()) +        { +            if(sit_target && (cur_object->getPCode() != LL_PCODE_VOLUME)) +            { +                continue; +            } +            // Calculate distance from the target position              LLVector3 target_diff = cur_object->getPositionRegion() - position;              F32 distance_to_target = target_diff.length();              if (distance_to_target < min_distance) @@ -296,22 +369,6 @@ void LLAgentListener::resetAxes(const LLSD& event_data) const      }  } -void LLAgentListener::getAxes(const LLSD& event_data) const -{ -    LLQuaternion quat(mAgent.getQuat()); -    F32 roll, pitch, yaw; -    quat.getEulerAngles(&roll, &pitch, &yaw); -    // The official query API for LLQuaternion's [x, y, z, w] values is its -    // public member mQ... -    LLSD reply = LLSD::emptyMap(); -    reply["quat"] = llsd_copy_array(boost::begin(quat.mQ), boost::end(quat.mQ)); -    reply["euler"] = LLSD::emptyMap(); -    reply["euler"]["roll"] = roll; -    reply["euler"]["pitch"] = pitch; -    reply["euler"]["yaw"] = yaw; -    sendReply(reply, event_data); -} -  void LLAgentListener::getPosition(const LLSD& event_data) const  {      F32 roll, pitch, yaw; @@ -333,14 +390,13 @@ void LLAgentListener::getPosition(const LLSD& event_data) const  void LLAgentListener::startAutoPilot(LLSD const & event_data)  { -    LLQuaternion target_rotation_value;      LLQuaternion* target_rotation = NULL;      if (event_data.has("target_rotation"))      { -        target_rotation_value = ll_quaternion_from_sd(event_data["target_rotation"]); +        LLQuaternion target_rotation_value = ll_quaternion_from_sd(event_data["target_rotation"]);          target_rotation = &target_rotation_value;      } -    // *TODO: Use callback_pump and callback_data +      F32 rotation_threshold = 0.03f;      if (event_data.has("rotation_threshold"))      { @@ -360,13 +416,24 @@ void LLAgentListener::startAutoPilot(LLSD const & event_data)          stop_distance = (F32)event_data["stop_distance"].asReal();      } +    std::string behavior_name = LLCoros::getName(); +    if (event_data.has("behavior_name")) +    { +        behavior_name = event_data["behavior_name"].asString(); +    } +      // Clear follow target, this is doing a path      mFollowTarget.setNull(); +    auto finish_cb = [](bool success, void*) +    { +        LLEventPumps::instance().obtain("LLAutopilot").post(llsd::map("success", success)); +    }; +      mAgent.startAutoPilotGlobal(ll_vector3d_from_sd(event_data["target_global"]), -                                event_data["behavior_name"], +                                behavior_name,                                  target_rotation, -                                NULL, NULL, +                                finish_cb, NULL,                                  stop_distance,                                  rotation_threshold,                                  allow_flying); @@ -374,7 +441,7 @@ void LLAgentListener::startAutoPilot(LLSD const & event_data)  void LLAgentListener::getAutoPilot(const LLSD& event_data) const  { -    LLSD reply = LLSD::emptyMap(); +    Response reply(LLSD(), event_data);      LLSD::Boolean enabled = mAgent.getAutoPilot();      reply["enabled"] = enabled; @@ -403,12 +470,11 @@ void LLAgentListener::getAutoPilot(const LLSD& event_data) const      reply["rotation_threshold"] = mAgent.getAutoPilotRotationThreshold();      reply["behavior_name"] = mAgent.getAutoPilotBehaviorName();      reply["fly"] = (LLSD::Boolean) mAgent.getFlying(); - -    sendReply(reply, event_data);  }  void LLAgentListener::startFollowPilot(LLSD const & event_data)  { +    Response response(LLSD(), event_data);      LLUUID target_id;      bool allow_flying = true; @@ -442,6 +508,10 @@ void LLAgentListener::startFollowPilot(LLSD const & event_data)              }          }      } +    else +    { +        return response.error("'leader_id' or 'avatar_name' should be specified"); +    }      F32 stop_distance = 0.f;      if (event_data.has("stop_distance")) @@ -449,13 +519,16 @@ void LLAgentListener::startFollowPilot(LLSD const & event_data)          stop_distance = (F32)event_data["stop_distance"].asReal();      } -    if (target_id.notNull()) +    if (!gObjectList.findObject(target_id))      { -        mAgent.setFlying(allow_flying); -        mFollowTarget = target_id;  // Save follow target so we can report distance later - -        mAgent.startFollowPilot(target_id, allow_flying, stop_distance); +        std::string target_info = event_data.has("leader_id") ? event_data["leader_id"] : event_data["avatar_name"]; +        return response.error(stringize("Target ", std::quoted(target_info), " was not found"));      } + +    mAgent.setFlying(allow_flying); +    mFollowTarget = target_id;  // Save follow target so we can report distance later + +    mAgent.startFollowPilot(target_id, allow_flying, stop_distance);  }  void LLAgentListener::setAutoPilotTarget(LLSD const & event_data) const @@ -519,3 +592,209 @@ void LLAgentListener::getGroups(const LLSD& event) const      }      sendReply(LLSDMap("groups", reply), event);  } + +/*----------------------------- camera control -----------------------------*/ +// specialize LLSDParam to support (const LLVector3&) arguments -- this +// wouldn't even be necessary except that the relevant LLVector3 constructor +// is explicitly explicit +template <> +class LLSDParam<const LLVector3&>: public LLSDParamBase +{ +public: +    LLSDParam(const LLSD& value): value(LLVector3(value)) {} + +    operator const LLVector3&() const { return value; } + +private: +    LLVector3 value; +}; + +// accept any of a number of similar LLFollowCamMgr methods with different +// argument types, and return a wrapper lambda that accepts LLSD and converts +// to the target argument type +template <typename T> +auto wrap(void (LLFollowCamMgr::*method)(const LLUUID& source, T arg)) +{ +    return [method](LLFollowCamMgr& followcam, const LLUUID& source, const LLSD& arg) +    { (followcam.*method)(source, LLSDParam<T>(arg)); }; +} + +// table of supported LLFollowCamMgr methods, +// with the corresponding setFollowCamParams() argument keys +static std::pair<std::string, std::function<void(LLFollowCamMgr&, const LLUUID&, const LLSD&)>> +cam_params[] = +{ +    { "camera_pos",       wrap(&LLFollowCamMgr::setPosition) }, +    { "focus_pos",        wrap(&LLFollowCamMgr::setFocus) }, +    { "focus_offset",     wrap(&LLFollowCamMgr::setFocusOffset) }, +    { "camera_locked",    wrap(&LLFollowCamMgr::setPositionLocked) }, +    { "focus_locked",     wrap(&LLFollowCamMgr::setFocusLocked) }, +    { "distance",         wrap(&LLFollowCamMgr::setDistance) }, +    { "focus_threshold",  wrap(&LLFollowCamMgr::setFocusThreshold) }, +    { "camera_threshold", wrap(&LLFollowCamMgr::setPositionThreshold) }, +    { "focus_lag",        wrap(&LLFollowCamMgr::setFocusLag) }, +    { "camera_lag",       wrap(&LLFollowCamMgr::setPositionLag) }, +    { "camera_pitch",     wrap(&LLFollowCamMgr::setPitch) }, +    { "behindness_lag",   wrap(&LLFollowCamMgr::setBehindnessLag) }, +    { "behindness_angle", wrap(&LLFollowCamMgr::setBehindnessAngle) }, +}; + +void LLAgentListener::setFollowCamParams(const LLSD& event) const +{ +    auto& followcam{ LLFollowCamMgr::instance() }; +    for (const auto& pair : cam_params) +    { +        if (event.has(pair.first)) +        { +            pair.second(followcam, gAgentID, event[pair.first]); +        } +    } +    followcam.setCameraActive(gAgentID, true); +} + +void LLAgentListener::setFollowCamActive(LLSD const & event) const +{ +    LLFollowCamMgr::getInstance()->setCameraActive(gAgentID, event["active"]); +} + +void LLAgentListener::removeFollowCamParams(LLSD const & event) const +{ +    LLFollowCamMgr::getInstance()->removeFollowCamParams(gAgentID); +} + +LLViewerInventoryItem* get_anim_item(LLEventAPI::Response &response, const LLSD &event_data) +{ +    LLViewerInventoryItem* item = gInventory.getItem(event_data["item_id"].asUUID()); +    if (!item || (item->getInventoryType() != LLInventoryType::IT_ANIMATION)) +    { +        response.error(stringize("Animation item ", std::quoted(event_data["item_id"].asString()), " was not found")); +        return NULL; +    } +    return item; +} + +void LLAgentListener::playAnimation(LLSD const &event_data) +{ +    Response response(LLSD(), event_data); +    if (LLViewerInventoryItem* item = get_anim_item(response, event_data)) +    { +        if (event_data["inworld"].asBoolean()) +        { +            mAgent.sendAnimationRequest(item->getAssetUUID(), ANIM_REQUEST_START); +        } +        else +        { +            gAgentAvatarp->startMotion(item->getAssetUUID()); +        } +    } +} + +void LLAgentListener::stopAnimation(LLSD const &event_data) +{ +    Response response(LLSD(), event_data); +    if (LLViewerInventoryItem* item = get_anim_item(response, event_data)) +    { +        gAgentAvatarp->stopMotion(item->getAssetUUID()); +        mAgent.sendAnimationRequest(item->getAssetUUID(), ANIM_REQUEST_STOP); +    } +} + +void LLAgentListener::getAnimationInfo(LLSD const &event_data) +{ +    Response response(LLSD(), event_data); +    if (LLViewerInventoryItem* item = get_anim_item(response, event_data)) +    { +        // if motion exists, will return existing one +        LLMotion* motion = gAgentAvatarp->createMotion(item->getAssetUUID()); +        response["anim_info"] = llsd::map("duration", motion->getDuration(), +                                               "is_loop", motion->getLoop(), +                                               "num_joints", motion->getNumJointMotions(), +                                               "asset_id", item->getAssetUUID(), +                                               "priority", motion->getPriority()); +    } +} + +void LLAgentListener::getID(LLSD const& event_data) +{ +    Response response(llsd::map("id", gAgentID), event_data); +} + +F32 get_search_radius(LLSD const& event_data) +{ +    static LLCachedControl<F32> render_far_clip(gSavedSettings, "RenderFarClip", 64); +    F32 dist = render_far_clip; +    if (event_data.has("dist")) +    { +        dist = llclamp((F32)event_data["dist"].asReal(), 1, 512); +    } +   return dist * dist; +} + +void LLAgentListener::getNearbyAvatarsList(LLSD const& event_data) +{ +    Response response(LLSD(), event_data); +    F32 radius = get_search_radius(event_data); +    LLVector3d agent_pos = gAgent.getPositionGlobal(); +    for (LLCharacter* character : LLCharacter::sInstances) +    { +        LLVOAvatar* avatar = (LLVOAvatar*)character; +        if (avatar && !avatar->isDead() && !avatar->isControlAvatar() && !avatar->isSelf()) +        { +            if ((dist_vec_squared(avatar->getPositionGlobal(), agent_pos) <= radius)) +            { +                LLAvatarName av_name; +                LLAvatarNameCache::get(avatar->getID(), &av_name); +                LLVector3 region_pos = avatar->getCharacterPosition(); +                response["result"].append(llsd::map("id", avatar->getID(), "global_pos", ll_sd_from_vector3d(avatar->getPosGlobalFromAgent(region_pos)), +                                                    "region_pos", ll_sd_from_vector3(region_pos), "name", av_name.getUserName(), "region_id", avatar->getRegion()->getRegionID())); +            } +        } +    } +} + +void LLAgentListener::getNearbyObjectsList(LLSD const& event_data) +{ +    Response response(LLSD(), event_data); +    F32 radius = get_search_radius(event_data); +    S32 num_objects = gObjectList.getNumObjects(); +    LLVector3d agent_pos = gAgent.getPositionGlobal(); +    for (S32 i = 0; i < num_objects; ++i) +    { +        LLViewerObject* object = gObjectList.getObject(i); +        if (object && object->getVolume() && !object->isAttachment()) +        { +            if ((dist_vec_squared(object->getPositionGlobal(), agent_pos) <= radius)) +            { +                response["result"].append(llsd::map("id", object->getID(), "global_pos", ll_sd_from_vector3d(object->getPositionGlobal()), "region_pos", +                          ll_sd_from_vector3(object->getPositionRegion()), "region_id", object->getRegion()->getRegionID())); +            } +        } +    } +} + +void LLAgentListener::getAgentScreenPos(LLSD const& event_data) +{ +    Response response(LLSD(), event_data); +    LLVector3 render_pos; +    if (event_data.has("avatar_id") && (event_data["avatar_id"].asUUID() != gAgentID)) +    { +        LLUUID avatar_id(event_data["avatar_id"]); +        for (LLCharacter* character : LLCharacter::sInstances) +        { +            LLVOAvatar* avatar = (LLVOAvatar*)character; +            if (!avatar->isDead() && (avatar->getID() == avatar_id)) +            { +                render_pos = avatar->getRenderPosition(); +                break; +            } +        } +    } +    else if (gAgentAvatarp.notNull() && gAgentAvatarp->isValid()) +    { +        render_pos = gAgentAvatarp->getRenderPosition(); +    } +    LLCoordGL screen_pos; +    response["onscreen"] = LLViewerCamera::getInstance()->projectPosAgentToScreen(render_pos, screen_pos, false); +    response["x"] = screen_pos.mX; +    response["y"] = screen_pos.mY; +} diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h index c544d089ce..b5bea8c0bd 100644 --- a/indra/newview/llagentlistener.h +++ b/indra/newview/llagentlistener.h @@ -48,7 +48,6 @@ private:      void requestStand(LLSD const & event_data) const;      void requestTouch(LLSD const & event_data) const;      void resetAxes(const LLSD& event_data) const; -    void getAxes(const LLSD& event_data) const;      void getGroups(const LLSD& event) const;      void getPosition(const LLSD& event_data) const;      void startAutoPilot(const LLSD& event_data); @@ -58,7 +57,20 @@ private:      void stopAutoPilot(const LLSD& event_data) const;      void lookAt(LLSD const & event_data) const; -    LLViewerObject * findObjectClosestTo( const LLVector3 & position ) const; +    void setFollowCamParams(LLSD const & event_data) const; +    void setFollowCamActive(LLSD const & event_data) const; +    void removeFollowCamParams(LLSD const & event_data) const; + +    void playAnimation(LLSD const &event_data); +    void stopAnimation(LLSD const &event_data); +    void getAnimationInfo(LLSD const &event_data); + +    void getID(LLSD const& event_data); +    void getNearbyAvatarsList(LLSD const& event_data); +    void getNearbyObjectsList(LLSD const& event_data); +    void getAgentScreenPos(LLSD const& event_data); + +    LLViewerObject * findObjectClosestTo( const LLVector3 & position, bool sit_target = false ) const;  private:      LLAgent &   mAgent; diff --git a/indra/newview/llappearancelistener.cpp b/indra/newview/llappearancelistener.cpp new file mode 100644 index 0000000000..a6d6e76e02 --- /dev/null +++ b/indra/newview/llappearancelistener.cpp @@ -0,0 +1,158 @@ +/** + * @file llappearancelistener.cpp + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llappearancelistener.h" + +#include "llappearancemgr.h" +#include "llinventoryfunctions.h" +#include "lltransutil.h" +#include "llwearableitemslist.h" +#include "stringize.h" + +LLAppearanceListener::LLAppearanceListener() +  : LLEventAPI("LLAppearance", +               "API to wear a specified outfit and wear/remove individual items") +{ +    add("wearOutfit", +        "Wear outfit by folder id: [\"folder_id\"] OR by folder name: [\"folder_name\"]\n" +        "When [\"append\"] is true, outfit will be added to COF\n" +        "otherwise it will replace current oufit", +        &LLAppearanceListener::wearOutfit); + +    add("wearItems", +        "Wear items by id: [items_id]", +        &LLAppearanceListener::wearItems, +        llsd::map("items_id", LLSD(), "replace", LLSD())); + +    add("detachItems", +        "Detach items by id: [items_id]", +        &LLAppearanceListener::detachItems, +        llsd::map("items_id", LLSD())); + +    add("getOutfitsList", +        "Return the table with Outfits info(id and name)", +         &LLAppearanceListener::getOutfitsList); + +    add("getOutfitItems", +        "Return the table of items with info(id : name, wearable_type, is_worn) inside specified outfit folder", +         &LLAppearanceListener::getOutfitItems); +} + + +void LLAppearanceListener::wearOutfit(LLSD const &data) +{ +    Response response(LLSD(), data); +    if (!data.has("folder_id") && !data.has("folder_name")) +    { +        return response.error("Either [folder_id] or [folder_name] is required"); +    } + +    bool append = data.has("append") ? data["append"].asBoolean() : false; +    if (!LLAppearanceMgr::instance().wearOutfit(data, append)) +    { +        response.error("Failed to wear outfit"); +    } +} + +void LLAppearanceListener::wearItems(LLSD const &data) +{ +    const LLSD& items_id{ data["items_id"] }; +    uuid_vec_t  ids; +    if (!items_id.isArray()) +    { +        ids.push_back(items_id.asUUID()); +    } +    else // array +    { +        for (const auto& id : llsd::inArray(items_id)) +        { +            ids.push_back(id); +        } +    } +    LLAppearanceMgr::instance().wearItemsOnAvatar(ids, true, data["replace"].asBoolean()); +} + +void LLAppearanceListener::detachItems(LLSD const &data) +{ +    const LLSD& items_id{ data["items_id"] }; +    uuid_vec_t  ids; +    if (!items_id.isArray()) +    { +        ids.push_back(items_id.asUUID()); +    } +    else // array +    { +        for (const auto& id : llsd::inArray(items_id)) +        { +            ids.push_back(id); +        } +    } +    LLAppearanceMgr::instance().removeItemsFromAvatar(ids); +} + +void LLAppearanceListener::getOutfitsList(LLSD const &data) +{ +    Response response(LLSD(), data); +    const LLUUID outfits_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + +    LLInventoryModel::cat_array_t cat_array; +    LLInventoryModel::item_array_t item_array; + +    LLIsType is_category(LLAssetType::AT_CATEGORY); +    gInventory.collectDescendentsIf(outfits_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, is_category); + +    response["outfits"] = llsd::toMap(cat_array, +        [](const LLPointer<LLViewerInventoryCategory> &cat) +        { return std::make_pair(cat->getUUID().asString(), cat->getName()); }); +} + +void LLAppearanceListener::getOutfitItems(LLSD const &data) +{ +    Response response(LLSD(), data); +    LLUUID outfit_id(data["outfit_id"].asUUID()); +    LLViewerInventoryCategory *cat = gInventory.getCategory(outfit_id); +    if (!cat || cat->getPreferredType() != LLFolderType::FT_OUTFIT) +    { +        return response.error(stringize("Couldn't find outfit ", outfit_id.asString())); +    } +    LLInventoryModel::cat_array_t  cat_array; +    LLInventoryModel::item_array_t item_array; + +    LLFindOutfitItems collector = LLFindOutfitItems(); +    gInventory.collectDescendentsIf(outfit_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, collector); + +    response["items"] = llsd::toMap(item_array, +        [](const LLPointer<LLViewerInventoryItem> &it) +        { +            return std::make_pair( +                it->getUUID().asString(), +                llsd::map( +                    "name", it->getName(), +                    "wearable_type", LLWearableType::getInstance()->getTypeName(it->isWearableType() ? it->getWearableType() : LLWearableType::WT_NONE), +                    "is_worn", get_is_item_worn(it))); +        }); +} diff --git a/indra/newview/llappearancelistener.h b/indra/newview/llappearancelistener.h new file mode 100644 index 0000000000..04c5eac2eb --- /dev/null +++ b/indra/newview/llappearancelistener.h @@ -0,0 +1,46 @@ +/** + * @file llappearancelistener.h + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + + +#ifndef LL_LLAPPEARANCELISTENER_H +#define LL_LLAPPEARANCELISTENER_H + +#include "lleventapi.h" + +class LLAppearanceListener : public LLEventAPI +{ +public: +    LLAppearanceListener(); + +private: +    void wearOutfit(LLSD const &data); +    void wearItems(LLSD const &data); +    void detachItems(LLSD const &data); +    void getOutfitsList(LLSD const &data); +    void getOutfitItems(LLSD const &data); +}; + +#endif // LL_LLAPPEARANCELISTENER_H + diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 101aca3823..e9d455ae53 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -31,6 +31,7 @@  #include "llagent.h"  #include "llagentcamera.h"  #include "llagentwearables.h" +#include "llappearancelistener.h"  #include "llappearancemgr.h"  #include "llattachmentsmgr.h"  #include "llcommandhandler.h" @@ -66,6 +67,8 @@  #include "llavatarpropertiesprocessor.h" +LLAppearanceListener sAppearanceListener; +  namespace  {      const S32   BAKE_RETRY_MAX_COUNT = 5; @@ -4762,6 +4765,11 @@ bool wear_category(const LLSD& query_map, bool append)      return false;  } +bool LLAppearanceMgr::wearOutfit(const LLSD& query_map, bool append) +{ +    return wear_category(query_map, append); +} +  class LLWearFolderHandler : public LLCommandHandler  {  public: diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index 6c45a32856..bc7dc9506b 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -60,6 +60,7 @@ public:      void wearInventoryCategoryOnAvatar(LLInventoryCategory* category, bool append);      void wearCategoryFinal(const LLUUID& cat_id, bool copy_items, bool append);      void wearOutfitByName(const std::string& name); +    bool wearOutfit(const LLSD& query_map, bool append = false);      void changeOutfit(bool proceed, const LLUUID& category, bool append);      void replaceCurrentOutfit(const LLUUID& new_outfit);      void renameOutfit(const LLUUID& outfit_id); diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index 28c651f0cd..db6f9ac22a 100644 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -52,6 +52,7 @@  #include "llfirstuse.h"  #include "llfloaterimnearbychat.h" +#include "llfloaterimnearbychatlistener.h"  #include "llagent.h" // gAgent  #include "llgesturemgr.h"  #include "llmultigesture.h" @@ -71,6 +72,8 @@  S32 LLFloaterIMNearbyChat::sLastSpecialChatChannel = 0; +static LLFloaterIMNearbyChatListener sChatListener; +  constexpr S32 EXPANDED_HEIGHT = 266;  constexpr S32 COLLAPSED_HEIGHT = 60;  constexpr S32 EXPANDED_MIN_HEIGHT = 150; diff --git a/indra/newview/llfloaterimnearbychatlistener.cpp b/indra/newview/llfloaterimnearbychatlistener.cpp index 43173d3680..b15a32ce40 100644 --- a/indra/newview/llfloaterimnearbychatlistener.cpp +++ b/indra/newview/llfloaterimnearbychatlistener.cpp @@ -34,12 +34,12 @@  #include "llagent.h"  #include "llchat.h"  #include "llviewercontrol.h" +#include "stringize.h" +static const F32 CHAT_THROTTLE_PERIOD = 1.f; -LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener(LLFloaterIMNearbyChat & chatbar) -  : LLEventAPI("LLChatBar", -               "LLChatBar listener to (e.g.) sendChat, etc."), -    mChatbar(chatbar) +LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener() : +    LLEventAPI("LLChatBar", "LLChatBar listener to (e.g.) sendChat, etc.")  {      add("sendChat",          "Send chat to the simulator:\n" @@ -49,10 +49,18 @@ LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener(LLFloaterIMNearbyCh          &LLFloaterIMNearbyChatListener::sendChat);  } -  // "sendChat" command -void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const +void LLFloaterIMNearbyChatListener::sendChat(LLSD const& chat_data)  { +    F64 cur_time = LLTimer::getElapsedSeconds(); + +    if (cur_time < mLastThrottleTime + CHAT_THROTTLE_PERIOD) +    { +        LL_WARNS("LLFloaterIMNearbyChatListener") << "'sendChat' was  throttled" << LL_ENDL; +        return; +    } +    mLastThrottleTime = cur_time; +      // Extract the data      std::string chat_text = chat_data["message"].asString(); @@ -81,20 +89,12 @@ void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const      }      // Have to prepend /42 style channel numbers -    std::string chat_to_send; -    if (channel == 0) -    { -        chat_to_send = chat_text; -    } -    else +    if (channel)      { -        chat_to_send += "/"; -        chat_to_send += chat_data["channel"].asString(); -        chat_to_send += " "; -        chat_to_send += chat_text; +        chat_text = stringize("/", chat_data["channel"].asString(), " ", chat_text);      }      // Send it as if it was typed in -    mChatbar.sendChatFromViewer(chat_to_send, type_o_chat, ((bool)(channel == 0)) && gSavedSettings.getBOOL("PlayChatAnim")); +    LLFloaterIMNearbyChat::sendChatFromViewer(chat_text, type_o_chat, (channel == 0) && gSavedSettings.getBOOL("PlayChatAnim"));  } diff --git a/indra/newview/llfloaterimnearbychatlistener.h b/indra/newview/llfloaterimnearbychatlistener.h index 96184d95b3..71eba53a9a 100644 --- a/indra/newview/llfloaterimnearbychatlistener.h +++ b/indra/newview/llfloaterimnearbychatlistener.h @@ -38,12 +38,12 @@ class LLFloaterIMNearbyChat;  class LLFloaterIMNearbyChatListener : public LLEventAPI  {  public: -    LLFloaterIMNearbyChatListener(LLFloaterIMNearbyChat & chatbar); +    LLFloaterIMNearbyChatListener();  private: -    void sendChat(LLSD const & chat_data) const; +    void sendChat(LLSD const & chat_data); -    LLFloaterIMNearbyChat & mChatbar; +    F64 mLastThrottleTime{0};  };  #endif // LL_LLFLOATERIMNEARBYCHATLISTENER_H diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index ba9c9fa13f..34d96aa024 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -46,7 +46,7 @@  //  // Globals  // -static GroupChatListener sGroupChatListener; +static LLGroupChatListener sGroupChatListener;  class LLGroupHandler : public LLCommandHandler  { diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 8e6657b4b9..66a19d3601 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -42,6 +42,7 @@  // Viewer includes  #include "llagent.h"  #include "llagentaccess.h" +#include "llcallbacklist.h"  #include "llviewerparcelaskplay.h"  #include "llviewerwindow.h"  #include "llviewercontrol.h" @@ -1750,6 +1751,8 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use                  {                      instance->mTeleportFinishedSignal(instance->mTeleportInProgressPosition, false);                  } +                instance->postTeleportFinished(instance->mTeleportWithinRegion); +                instance->mTeleportWithinRegion = false;              }              parcel->setParcelEnvironmentVersion(parcel_environment_version);              LL_DEBUGS("ENVIRONMENT") << "Parcel environment version is " << parcel->getParcelEnvironmentVersion() << LL_ENDL; @@ -2719,6 +2722,8 @@ void LLViewerParcelMgr::onTeleportFinished(bool local, const LLVector3d& new_pos          // Local teleport. We already have the agent parcel data.          // Emit the signal immediately.          getInstance()->mTeleportFinishedSignal(new_pos, local); + +        postTeleportFinished(true);      }      else      { @@ -2727,12 +2732,14 @@ void LLViewerParcelMgr::onTeleportFinished(bool local, const LLVector3d& new_pos          // Let's wait for the update and then emit the signal.          mTeleportInProgressPosition = new_pos;          mTeleportInProgress = true; +        mTeleportWithinRegion = local;      }  }  void LLViewerParcelMgr::onTeleportFailed()  {      mTeleportFailedSignal(); +    LLEventPumps::instance().obtain("LLTeleport").post(llsd::map("success", false));  }  bool  LLViewerParcelMgr::getTeleportInProgress() @@ -2740,3 +2747,20 @@ bool  LLViewerParcelMgr::getTeleportInProgress()      return mTeleportInProgress // case where parcel data arrives after teleport          || gAgent.getTeleportState() > LLAgent::TELEPORT_NONE; // For LOCAL, no mTeleportInProgress  } + +void LLViewerParcelMgr::postTeleportFinished(bool local) +{ +    auto post = []() +    { +        LLEventPumps::instance().obtain("LLTeleport").post(llsd::map("success", true)); +    }; +    if (local) +    { +        static LLCachedControl<F32> teleport_local_delay(gSavedSettings, "TeleportLocalDelay"); +        doAfterInterval(post, teleport_local_delay + 0.5f); +    } +    else +    { +        post(); +    } +} diff --git a/indra/newview/llviewerparcelmgr.h b/indra/newview/llviewerparcelmgr.h index 974ea39359..95d693662e 100644 --- a/indra/newview/llviewerparcelmgr.h +++ b/indra/newview/llviewerparcelmgr.h @@ -295,6 +295,8 @@ public:      void onTeleportFailed();      bool getTeleportInProgress(); +    void postTeleportFinished(bool local); +      static bool isParcelOwnedByAgent(const LLParcel* parcelp, U64 group_proxy_power);      static bool isParcelModifiableByAgent(const LLParcel* parcelp, U64 group_proxy_power); @@ -344,7 +346,9 @@ private:      std::vector<LLParcelObserver*> mObservers; +    // Used to communicate between onTeleportFinished() and processParcelProperties()      bool                        mTeleportInProgress; +    bool                        mTeleportWithinRegion{ false };      LLVector3d                  mTeleportInProgressPosition;      teleport_finished_signal_t  mTeleportFinishedSignal;      teleport_failed_signal_t    mTeleportFailedSignal; diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index 8ce1a745c3..c708e804b2 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -33,7 +33,6 @@  #include "llagentwearables.h"  #include "llappearancemgr.h" -#include "llinventoryfunctions.h"  #include "llinventoryicon.h"  #include "llgesturemgr.h"  #include "lltransutil.h" @@ -41,15 +40,6 @@  #include "llviewermenu.h"  #include "llvoavatarself.h" -class LLFindOutfitItems : public LLInventoryCollectFunctor -{ -public: -    LLFindOutfitItems() {} -    virtual ~LLFindOutfitItems() {} -    virtual bool operator()(LLInventoryCategory* cat, -                            LLInventoryItem* item); -}; -  bool LLFindOutfitItems::operator()(LLInventoryCategory* cat,                                     LLInventoryItem* item)  { diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h index 3fe1059176..7a5f29020e 100644 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -32,6 +32,7 @@  #include "llsingleton.h"  // newview +#include "llinventoryfunctions.h"  #include "llinventoryitemslist.h"  #include "llinventorylistitem.h"  #include "lllistcontextmenu.h" @@ -507,4 +508,12 @@ protected:      LLWearableType::EType mMenuWearableType;  }; +class LLFindOutfitItems : public LLInventoryCollectFunctor +{ +public: +    LLFindOutfitItems() {} +    virtual ~LLFindOutfitItems() {} +    virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); +}; +  #endif //LL_LLWEARABLEITEMSLIST_H | 
