From a0dedb8a1f050465b4e447689672ea2845f0cd5b Mon Sep 17 00:00:00 2001 From: Robin Cornelius Date: Sat, 1 Oct 2011 22:32:42 +0100 Subject: And actually add the new files for MOAP radar --- linden/indra/newview/llmediadataclient.cpp | 813 +++++++++++++++++++++ linden/indra/newview/llmediadataclient.h | 341 +++++++++ linden/indra/newview/rcmoapradar.cpp | 440 +++++++++++ linden/indra/newview/rcmoapradar.h | 58 ++ .../skins/default/xui/en-us/floater_moap_radar.xml | 54 ++ 5 files changed, 1706 insertions(+) create mode 100644 linden/indra/newview/llmediadataclient.cpp create mode 100644 linden/indra/newview/llmediadataclient.h create mode 100644 linden/indra/newview/rcmoapradar.cpp create mode 100644 linden/indra/newview/rcmoapradar.h create mode 100644 linden/indra/newview/skins/default/xui/en-us/floater_moap_radar.xml (limited to 'linden/indra') diff --git a/linden/indra/newview/llmediadataclient.cpp b/linden/indra/newview/llmediadataclient.cpp new file mode 100644 index 0000000..e2fa700 --- /dev/null +++ b/linden/indra/newview/llmediadataclient.cpp @@ -0,0 +1,813 @@ +/** + * @file llmediadataclient.cpp + * @brief class for queueing up requests for media data + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + */ + +#include "llviewerprecompiledheaders.h" + +#include "llmediadataclient.h" + +#if LL_MSVC +// disable boost::lexical_cast warning +#pragma warning (disable:4702) +#endif + +#include + +#include "llhttpstatuscodes.h" +#include "llsdutil.h" +#include "llmediaentry.h" +#include "lltextureentry.h" +#include "llviewerregion.h" + +// +// When making a request +// - obtain the "overall interest score" of the object. +// This would be the sum of the impls' interest scores. +// - put the request onto a queue sorted by this score +// (highest score at the front of the queue) +// - On a timer, once a second, pull off the head of the queue and send +// the request. +// - Any request that gets a 503 still goes through the retry logic +// + +// +// Forward decls +// +const F32 LLMediaDataClient::QUEUE_TIMER_DELAY = 1.0; // seconds(s) +const F32 LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY = 10.0; // secs +const U32 LLMediaDataClient::MAX_RETRIES = 10; +const U32 LLMediaDataClient::MAX_SORTED_QUEUE_SIZE = 10000; +const U32 LLMediaDataClient::MAX_ROUND_ROBIN_QUEUE_SIZE = 10000; + +// << operators +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q); +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &q); + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay, + F32 retry_timer_delay, + U32 max_retries, + U32 max_sorted_queue_size, + U32 max_round_robin_queue_size) + : mQueueTimerDelay(queue_timer_delay), + mRetryTimerDelay(retry_timer_delay), + mMaxNumRetries(max_retries), + mMaxSortedQueueSize(max_sorted_queue_size), + mMaxRoundRobinQueueSize(max_round_robin_queue_size), + mQueueTimerIsRunning(false), + mCurrentQueueIsTheSortedQueue(true) +{ +} + +LLMediaDataClient::~LLMediaDataClient() +{ + stopQueueTimer(); + + // This should clear the queue, and hopefully call all the destructors. + LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClient destructor: queue: " << + (isEmpty() ? " " : " ") << LL_ENDL; + + mSortedQueue.clear(); + mRoundRobinQueue.clear(); +} + +bool LLMediaDataClient::isEmpty() const +{ + return mSortedQueue.empty() && mRoundRobinQueue.empty(); +} + +bool LLMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object) +{ + return (LLMediaDataClient::findOrRemove(mSortedQueue, object, false/*remove*/, LLMediaDataClient::Request::ANY).notNull() + || (LLMediaDataClient::findOrRemove(mRoundRobinQueue, object, false/*remove*/, LLMediaDataClient::Request::ANY).notNull())); +} + +bool LLMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t &object) +{ + bool removedFromSortedQueue = LLMediaDataClient::findOrRemove(mSortedQueue, object, true/*remove*/, LLMediaDataClient::Request::ANY).notNull(); + bool removedFromRoundRobinQueue = LLMediaDataClient::findOrRemove(mRoundRobinQueue, object, true/*remove*/, LLMediaDataClient::Request::ANY).notNull(); + return removedFromSortedQueue || removedFromRoundRobinQueue; +} + +//static +LLMediaDataClient::request_ptr_t LLMediaDataClient::findOrRemove(request_queue_t &queue, const LLMediaDataClientObject::ptr_t &obj, bool remove, LLMediaDataClient::Request::Type type) +{ + request_ptr_t result; + request_queue_t::iterator iter = queue.begin(); + request_queue_t::iterator end = queue.end(); + while (iter != end) + { + if (obj->getID() == (*iter)->getObject()->getID() && (type == LLMediaDataClient::Request::ANY || type == (*iter)->getType())) + { + result = *iter; + if (remove) queue.erase(iter); + break; + } + iter++; + } + return result; +} + +void LLMediaDataClient::request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload) +{ + if (object.isNull() || ! object->hasMedia()) return; + + // Push the object on the queue + enqueue(new Request(getCapabilityName(), payload, object, this)); +} + +void LLMediaDataClient::enqueue(const Request *request) +{ + if (request->isNew()) + { + // Add to sorted queue + if (LLMediaDataClient::findOrRemove(mSortedQueue, request->getObject(), true/*remove*/, request->getType()).notNull()) + { + LL_DEBUGS("LLMediaDataClient") << "REMOVING OLD request for " << *request << " ALREADY THERE!" << LL_ENDL; + } + + LL_DEBUGS("LLMediaDataClient") << "Queuing SORTED request for " << *request << LL_ENDL; + + // Sadly, we have to const-cast because items put into the queue are not const + mSortedQueue.push_back(const_cast(request)); + + LL_DEBUGS("LLMediaDataClientQueue") << "SORTED queue:" << mSortedQueue << LL_ENDL; + } + else { + if (mRoundRobinQueue.size() > mMaxRoundRobinQueueSize) + { + LL_INFOS_ONCE("LLMediaDataClient") << "RR QUEUE MAXED OUT!!!" << LL_ENDL; + LL_DEBUGS("LLMediaDataClient") << "Not queuing " << *request << LL_ENDL; + return; + } + + // ROUND ROBIN: if it is there, and it is a GET request, leave it. If not, put at front! + request_ptr_t existing_request; + if (request->getType() == Request::GET) + { + existing_request = LLMediaDataClient::findOrRemove(mRoundRobinQueue, request->getObject(), false/*remove*/, request->getType()); + } + if (existing_request.isNull()) + { + LL_DEBUGS("LLMediaDataClient") << "Queuing RR request for " << *request << LL_ENDL; + // Push the request on the pending queue + // Sadly, we have to const-cast because items put into the queue are not const + mRoundRobinQueue.push_front(const_cast(request)); + + LL_DEBUGS("LLMediaDataClientQueue") << "RR queue:" << mRoundRobinQueue << LL_ENDL; + } + else + { + LL_DEBUGS("LLMediaDataClient") << "ALREADY THERE: NOT Queuing request for " << *request << LL_ENDL; + + existing_request->markSent(false); + } + } + // Start the timer if not already running + startQueueTimer(); +} + +void LLMediaDataClient::startQueueTimer() +{ + if (! mQueueTimerIsRunning) + { + LL_DEBUGS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL; + // LLEventTimer automagically takes care of the lifetime of this object + new QueueTimer(mQueueTimerDelay, this); + } + else { + LL_DEBUGS("LLMediaDataClient") << "not starting queue timer (it's already running, right???)" << LL_ENDL; + } +} + +void LLMediaDataClient::stopQueueTimer() +{ + mQueueTimerIsRunning = false; +} + +bool LLMediaDataClient::processQueueTimer() +{ + sortQueue(); + + if(!isEmpty()) + { + LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, SORTED queue size is: " << mSortedQueue.size() + << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() started, SORTED queue is: " << mSortedQueue << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() started, RR queue is: " << mRoundRobinQueue << LL_ENDL; + } + + serviceQueue(); + + LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, SORTED queue size is: " << mSortedQueue.size() + << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() finished, SORTED queue is: " << mSortedQueue << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() finished, RR queue is: " << mRoundRobinQueue << LL_ENDL; + + return isEmpty(); +} + +void LLMediaDataClient::sortQueue() +{ + if(!mSortedQueue.empty()) + { + // Score all items first + request_queue_t::iterator iter = mSortedQueue.begin(); + request_queue_t::iterator end = mSortedQueue.end(); + while (iter != end) + { + (*iter)->updateScore(); + iter++; + } + + // Re-sort the list... + // NOTE: should this be a stable_sort? If so we need to change to using a vector. + mSortedQueue.sort(LLMediaDataClient::compareRequests); + + // ...then cull items over the max + U32 size = mSortedQueue.size(); + if (size > mMaxSortedQueueSize) + { + U32 num_to_cull = (size - mMaxSortedQueueSize); + LL_INFOS_ONCE("LLMediaDataClient") << "sorted queue MAXED OUT! Culling " + << num_to_cull << " items" << LL_ENDL; + while (num_to_cull-- > 0) + { + mSortedQueue.pop_back(); + } + } + } +} + +// static +bool LLMediaDataClient::compareRequests(const request_ptr_t &o1, const request_ptr_t &o2) +{ + if (o2.isNull()) return true; + if (o1.isNull()) return false; + return ( o1->getScore() > o2->getScore() ); +} + +void LLMediaDataClient::serviceQueue() +{ + request_queue_t *queue_p = getCurrentQueue(); + + // quick retry loop for cases where we shouldn't wait for the next timer tick + while(true) + { + if (queue_p->empty()) + { + LL_DEBUGS("LLMediaDataClient") << "queue empty: " << (*queue_p) << LL_ENDL; + break; + } + + // Peel one off of the items from the queue, and execute request + request_ptr_t request = queue_p->front(); + llassert(!request.isNull()); + const LLMediaDataClientObject *object = (request.isNull()) ? NULL : request->getObject(); + llassert(NULL != object); + + // Check for conditions that would make us just pop and rapidly loop through + // the queue. + if(request.isNull() || + request->isMarkedSent() || + NULL == object || + object->isDead() || + !object->hasMedia()) + { + if (request.isNull()) + { + LL_WARNS("LLMediaDataClient") << "Skipping NULL request" << LL_ENDL; + } + else { + LL_INFOS("LLMediaDataClient") << "Skipping : " << *request << " " + << ((request->isMarkedSent()) ? " request is marked sent" : + ((NULL == object) ? " object is NULL " : + ((object->isDead()) ? "object is dead" : + ((!object->hasMedia()) ? "object has no media!" : "BADNESS!")))) << LL_ENDL; + } + queue_p->pop_front(); + continue; // jump back to the start of the quick retry loop + } + + // Next, ask if this is "interesting enough" to fetch. If not, just stop + // and wait for the next timer go-round. Only do this for the sorted + // queue. + if (mCurrentQueueIsTheSortedQueue && !object->isInterestingEnough()) + { + LL_DEBUGS("LLMediaDataClient") << "Not fetching " << *request << ": not interesting enough" << LL_ENDL; + break; + } + + // Finally, try to send the HTTP message to the cap url + std::string url = request->getCapability(); + bool maybe_retry = false; + if (!url.empty()) + { + const LLSD &sd_payload = request->getPayload(); + LL_INFOS("LLMediaDataClient") << "Sending request for " << *request << LL_ENDL; + + // Call the subclass for creating the responder + LLHTTPClient::post(url, sd_payload, createResponder(request)); + } + else { + LL_INFOS("LLMediaDataClient") << "NOT Sending request for " << *request << ": empty cap url!" << LL_ENDL; + maybe_retry = true; + } + + bool exceeded_retries = request->getRetryCount() > mMaxNumRetries; + if (maybe_retry && ! exceeded_retries) // Try N times before giving up + { + // We got an empty cap, but in that case we will retry again next + // timer fire. + request->incRetryCount(); + } + else { + if (exceeded_retries) + { + LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " + << mMaxNumRetries << " tries...popping object id " << object->getID() << LL_ENDL; + // XXX Should we bring up a warning dialog?? + } + + queue_p->pop_front(); + + if (! mCurrentQueueIsTheSortedQueue) { + // Round robin + request->markSent(true); + mRoundRobinQueue.push_back(request); + } + } + + // end of quick loop -- any cases where we want to loop will use 'continue' to jump back to the start. + break; + } + + swapCurrentQueue(); +} + +void LLMediaDataClient::swapCurrentQueue() +{ + // Swap + mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue; + // If its empty, swap back + if (getCurrentQueue()->empty()) + { + mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue; + } +} + +LLMediaDataClient::request_queue_t *LLMediaDataClient::getCurrentQueue() +{ + return (mCurrentQueueIsTheSortedQueue) ? &mSortedQueue : &mRoundRobinQueue; +} + +// dump the queue +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q) +{ + int i = 0; + LLMediaDataClient::request_queue_t::const_iterator iter = q.begin(); + LLMediaDataClient::request_queue_t::const_iterator end = q.end(); + while (iter != end) + { + s << "\t" << i << "]: " << (*iter)->getObject()->getID().asString() << "(" << (*iter)->getObject()->getMediaInterest() << ")"; + iter++; + i++; + } + return s; +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::QueueTimer +// Queue of LLMediaDataClientObject smart pointers to request media for. +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc) +: LLEventTimer(time), mMDC(mdc) +{ + mMDC->setIsRunning(true); +} + +LLMediaDataClient::QueueTimer::~QueueTimer() +{ + LL_DEBUGS("LLMediaDataClient") << "~QueueTimer" << LL_ENDL; + mMDC->setIsRunning(false); + mMDC = NULL; +} + +// virtual +BOOL LLMediaDataClient::QueueTimer::tick() +{ + if (mMDC.isNull()) return TRUE; + return mMDC->processQueueTimer(); +} + + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::Responder::RetryTimer +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr) +: LLEventTimer(time), mResponder(mdr) +{ +} + +// virtual +LLMediaDataClient::Responder::RetryTimer::~RetryTimer() +{ + LL_DEBUGS("LLMediaDataClient") << "~RetryTimer" << *(mResponder->getRequest()) << LL_ENDL; + + // XXX This is weird: Instead of doing the work in tick() (which re-schedules + // a timer, which might be risky), do it here, in the destructor. Yes, it is very odd. + // Instead of retrying, we just put the request back onto the queue + LL_INFOS("LLMediaDataClient") << "RetryTimer fired for: " << *(mResponder->getRequest()) << " retrying" << LL_ENDL; + mResponder->getRequest()->reEnqueue(); + + // Release the ref to the responder. + mResponder = NULL; +} + +// virtual +BOOL LLMediaDataClient::Responder::RetryTimer::tick() +{ + // Don't fire again + return TRUE; +} + + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::Request +// +////////////////////////////////////////////////////////////////////////////////////// +/*static*/U32 LLMediaDataClient::Request::sNum = 0; + +LLMediaDataClient::Request::Request(const char *cap_name, + const LLSD& sd_payload, + LLMediaDataClientObject *obj, + LLMediaDataClient *mdc) +: mCapName(cap_name), + mPayload(sd_payload), + mObject(obj), + mNum(++sNum), + mRetryCount(0), + mMDC(mdc), + mMarkedSent(false), + mScore((F64)0.0) +{ +} + +LLMediaDataClient::Request::~Request() +{ + LL_DEBUGS("LLMediaDataClient") << "~Request" << (*this) << LL_ENDL; + mMDC = NULL; + mObject = NULL; +} + + +std::string LLMediaDataClient::Request::getCapability() const +{ + return getObject()->getCapabilityUrl(getCapName()); +} + +// Helper function to get the "type" of request, which just pokes around to +// discover it. +LLMediaDataClient::Request::Type LLMediaDataClient::Request::getType() const +{ + if (0 == strcmp(mCapName, "ObjectMediaNavigate")) + { + return NAVIGATE; + } + else if (0 == strcmp(mCapName, "ObjectMedia")) + { + const std::string &verb = mPayload["verb"]; + if (verb == "GET") + { + return GET; + } + else if (verb == "UPDATE") + { + return UPDATE; + } + } + llassert(false); + return GET; +} + +const char *LLMediaDataClient::Request::getTypeAsString() const +{ + Type t = getType(); + switch (t) + { + case GET: + return "GET"; + break; + case UPDATE: + return "UPDATE"; + break; + case NAVIGATE: + return "NAVIGATE"; + break; + case ANY: + return "ANY"; + break; + } + return ""; +} + + +void LLMediaDataClient::Request::reEnqueue() const +{ + // I sure hope this doesn't deref a bad pointer: + mMDC->enqueue(this); +} + +F32 LLMediaDataClient::Request::getRetryTimerDelay() const +{ + return (mMDC == NULL) ? LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY : + mMDC->mRetryTimerDelay; +} + +U32 LLMediaDataClient::Request::getMaxNumRetries() const +{ + return (mMDC == NULL) ? LLMediaDataClient::MAX_RETRIES : mMDC->mMaxNumRetries; +} + +void LLMediaDataClient::Request::markSent(bool flag) +{ + if (mMarkedSent != flag) + { + mMarkedSent = flag; + if (!mMarkedSent) + { + mNum = ++sNum; + } + } +} + +void LLMediaDataClient::Request::updateScore() +{ + F64 tmp = mObject->getMediaInterest(); + if (tmp != mScore) + { + LL_DEBUGS("LLMediaDataClient") << "Score for " << mObject->getID() << " changed from " << mScore << " to " << tmp << LL_ENDL; + mScore = tmp; + } +} + +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r) +{ + s << "request: num=" << r.getNum() + << " type=" << r.getTypeAsString() + << " ID=" << r.getObject()->getID() + << " #retries=" << r.getRetryCount(); + return s; +} + + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::Responder +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::Responder::Responder(const request_ptr_t &request) +: mRequest(request) +{ +} + +LLMediaDataClient::Responder::~Responder() +{ + LL_DEBUGS("LLMediaDataClient") << "~Responder" << *(getRequest()) << LL_ENDL; + mRequest = NULL; +} + +/*virtual*/ +void LLMediaDataClient::Responder::error(U32 status, const std::string& reason) +{ + if (status == HTTP_SERVICE_UNAVAILABLE) + { + F32 retry_timeout = mRequest->getRetryTimerDelay(); + + mRequest->incRetryCount(); + + if (mRequest->getRetryCount() < mRequest->getMaxNumRetries()) + { + LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL; + + // Start timer (instances are automagically tracked by + // InstanceTracker<> and LLEventTimer) + new RetryTimer(F32(retry_timeout/*secs*/), this); + } + else { + LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retry count " + << mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL; + } + } + else { + std::string msg = boost::lexical_cast(status) + ": " + reason; + LL_WARNS("LLMediaDataClient") << *mRequest << " http error(" << msg << ")" << LL_ENDL; + } +} + +/*virtual*/ +void LLMediaDataClient::Responder::result(const LLSD& content) +{ + LL_DEBUGS("LLMediaDataClientResponse") << *mRequest << " result : " << ll_print_sd(content) << LL_ENDL; +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLObjectMediaDataClient +// Subclass of LLMediaDataClient for the ObjectMedia cap +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::Responder *LLObjectMediaDataClient::createResponder(const request_ptr_t &request) const +{ + return new LLObjectMediaDataClient::Responder(request); +} + +const char *LLObjectMediaDataClient::getCapabilityName() const +{ + return "ObjectMedia"; +} + +void LLObjectMediaDataClient::fetchMedia(LLMediaDataClientObject *object) +{ + LLSD sd_payload; + sd_payload["verb"] = "GET"; + sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); + request(object, sd_payload); +} + +void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject *object) +{ + LLSD sd_payload; + sd_payload["verb"] = "UPDATE"; + sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); + LLSD object_media_data; + int i = 0; + int end = object->getMediaDataCount(); + for ( ; i < end ; ++i) + { + object_media_data.append(object->getMediaDataLLSD(i)); + } + sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data; + + LL_DEBUGS("LLMediaDataClient") << "update media data: " << object->getID() << " " << ll_print_sd(sd_payload) << LL_ENDL; + + request(object, sd_payload); +} + +/*virtual*/ +void LLObjectMediaDataClient::Responder::result(const LLSD& content) +{ + const LLMediaDataClient::Request::Type type = getRequest()->getType(); + llassert(type == LLMediaDataClient::Request::GET || type == LLMediaDataClient::Request::UPDATE) + if (type == LLMediaDataClient::Request::GET) + { + LL_DEBUGS("LLMediaDataClientResponse") << *(getRequest()) << " GET returned: " << ll_print_sd(content) << LL_ENDL; + + // Look for an error + if (content.has("error")) + { + const LLSD &error = content["error"]; + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error getting media data for object: code=" << + error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; + + // XXX Warn user? + } + else { + // Check the data + const LLUUID &object_id = content[LLTextureEntry::OBJECT_ID_KEY]; + if (object_id != getRequest()->getObject()->getID()) + { + // NOT good, wrong object id!! + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " DROPPING response with wrong object id (" << object_id << ")" << LL_ENDL; + return; + } + + // Otherwise, update with object media data + getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY], + content[LLTextureEntry::MEDIA_VERSION_KEY]); + } + } + else if (type == LLMediaDataClient::Request::UPDATE) + { + // just do what our superclass does + LLMediaDataClient::Responder::result(content); + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLObjectMediaNavigateClient +// Subclass of LLMediaDataClient for the ObjectMediaNavigate cap +// +////////////////////////////////////////////////////////////////////////////////////// +LLMediaDataClient::Responder *LLObjectMediaNavigateClient::createResponder(const request_ptr_t &request) const +{ + return new LLObjectMediaNavigateClient::Responder(request); +} + +const char *LLObjectMediaNavigateClient::getCapabilityName() const +{ + return "ObjectMediaNavigate"; +} + +void LLObjectMediaNavigateClient::navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url) +{ + LLSD sd_payload; + sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); + sd_payload[LLMediaEntry::CURRENT_URL_KEY] = url; + sd_payload[LLTextureEntry::TEXTURE_INDEX_KEY] = (LLSD::Integer)texture_index; + + LL_INFOS("LLMediaDataClient") << "navigate() initiated: " << ll_print_sd(sd_payload) << LL_ENDL; + + request(object, sd_payload); +} + +/*virtual*/ +void LLObjectMediaNavigateClient::Responder::error(U32 status, const std::string& reason) +{ + // Bounce back (unless HTTP_SERVICE_UNAVAILABLE, in which case call base + // class + if (status == HTTP_SERVICE_UNAVAILABLE) + { + LLMediaDataClient::Responder::error(status, reason); + } + else { + // bounce the face back + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: http code=" << status << LL_ENDL; + const LLSD &payload = getRequest()->getPayload(); + // bounce the face back + getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]); + } +} + +/*virtual*/ +void LLObjectMediaNavigateClient::Responder::result(const LLSD& content) +{ + LL_INFOS("LLMediaDataClient") << *(getRequest()) << " NAVIGATE returned " << ll_print_sd(content) << LL_ENDL; + + if (content.has("error")) + { + const LLSD &error = content["error"]; + int error_code = error["code"]; + + if (ERROR_PERMISSION_DENIED_CODE == error_code) + { + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Navigation denied: bounce back" << LL_ENDL; + const LLSD &payload = getRequest()->getPayload(); + // bounce the face back + getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]); + } + else { + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: code=" << + error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; + } + // XXX Warn user? + } + else { + // just do what our superclass does + LLMediaDataClient::Responder::result(content); + } +} diff --git a/linden/indra/newview/llmediadataclient.h b/linden/indra/newview/llmediadataclient.h new file mode 100644 index 0000000..0ed7c57 --- /dev/null +++ b/linden/indra/newview/llmediadataclient.h @@ -0,0 +1,341 @@ +/** + * @file llmediadataclient.h + * @brief class for queueing up requests to the media service + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + */ + +#ifndef LL_LLMEDIADATACLIENT_H +#define LL_LLMEDIADATACLIENT_H + +#include "llhttpclient.h" +#include +//#include "llrefcount.h" +//#include "llpointer.h" +//#include "lleventtimer.h" + + +// Link seam for LLVOVolume +class LLMediaDataClientObject : public LLRefCount +{ +public: + // Get the number of media data items + virtual U8 getMediaDataCount() const = 0; + // Get the media data at index, as an LLSD + virtual LLSD getMediaDataLLSD(U8 index) const = 0; + // Get this object's UUID + virtual LLUUID getID() const = 0; + // Navigate back to previous URL + virtual void mediaNavigateBounceBack(U8 index) = 0; + // Does this object have media? + virtual bool hasMedia() const = 0; + // Update the object's media data to the given array + virtual void updateObjectMediaData(LLSD const &media_data_array, const std::string &version_string) = 0; + // Return the total "interest" of the media (on-screen area) + virtual F64 getMediaInterest() const = 0; + // Return the given cap url + virtual std::string getCapabilityUrl(const std::string &name) const = 0; + // Return whether the object has been marked dead + virtual bool isDead() const = 0; + // Returns a media version number for the object + virtual U32 getMediaVersion() const = 0; + // Returns whether the object is "interesting enough" to fetch + virtual bool isInterestingEnough() const = 0; + // Returns whether we've seen this object yet or not + virtual bool isNew() const = 0; + + // smart pointer + typedef LLPointer ptr_t; +}; + +// This object creates a priority queue for requests. +// Abstracts the Cap URL, the request, and the responder +class LLMediaDataClient : public LLRefCount +{ +public: + LOG_CLASS(LLMediaDataClient); + + const static F32 QUEUE_TIMER_DELAY;// = 1.0; // seconds(s) + const static F32 UNAVAILABLE_RETRY_TIMER_DELAY;// = 5.0; // secs + const static U32 MAX_RETRIES;// = 4; + const static U32 MAX_SORTED_QUEUE_SIZE;// = 10000; + const static U32 MAX_ROUND_ROBIN_QUEUE_SIZE;// = 10000; + + // Constructor + LLMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES, + U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE, + U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE); + + // Make the request + void request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload); + + F32 getRetryTimerDelay() const { return mRetryTimerDelay; } + + // Returns true iff the queue is empty + bool isEmpty() const; + + // Returns true iff the given object is in the queue + bool isInQueue(const LLMediaDataClientObject::ptr_t &object); + + // Remove the given object from the queue. Returns true iff the given object is removed. + bool removeFromQueue(const LLMediaDataClientObject::ptr_t &object); + + // Called only by the Queue timer and tests (potentially) + bool processQueueTimer(); + +protected: + // Destructor + virtual ~LLMediaDataClient(); // use unref + + // Request + class Request : public LLRefCount + { + public: + enum Type { + GET, + UPDATE, + NAVIGATE, + ANY + }; + + Request(const char *cap_name, const LLSD& sd_payload, LLMediaDataClientObject *obj, LLMediaDataClient *mdc); + const char *getCapName() const { return mCapName; } + const LLSD &getPayload() const { return mPayload; } + LLMediaDataClientObject *getObject() const { return mObject; } + + U32 getNum() const { return mNum; } + + U32 getRetryCount() const { return mRetryCount; } + void incRetryCount() { mRetryCount++; } + + // Note: may return empty string! + std::string getCapability() const; + + Type getType() const; + const char *getTypeAsString() const; + + // Re-enqueue thyself + void reEnqueue() const; + + F32 getRetryTimerDelay() const; + U32 getMaxNumRetries() const; + + bool isNew() const { return mObject.notNull() ? mObject->isNew() : false; } + void markSent(bool flag); + bool isMarkedSent() const { return mMarkedSent; } + void updateScore(); + F64 getScore() const { return mScore; } + + public: + friend std::ostream& operator<<(std::ostream &s, const Request &q); + + protected: + virtual ~Request(); // use unref(); + + private: + const char *mCapName; + LLSD mPayload; + LLMediaDataClientObject::ptr_t mObject; + // Simple tracking + U32 mNum; + static U32 sNum; + U32 mRetryCount; + F64 mScore; + bool mMarkedSent; + + // Back pointer to the MDC...not a ref! + LLMediaDataClient *mMDC; + }; + typedef LLPointer request_ptr_t; + + // Responder + class Responder : public LLHTTPClient::Responder + { + public: + Responder(const request_ptr_t &request); + //If we get back an error (not found, etc...), handle it here + virtual void error(U32 status, const std::string& reason); + //If we get back a normal response, handle it here. Default just logs it. + virtual void result(const LLSD& content); + + const request_ptr_t &getRequest() const { return mRequest; } + + protected: + virtual ~Responder(); + + private: + + class RetryTimer : public LLEventTimer + { + public: + RetryTimer(F32 time, Responder *); + virtual ~RetryTimer(); + virtual BOOL tick(); + private: + // back-pointer + boost::intrusive_ptr mResponder; + }; + + request_ptr_t mRequest; + }; + +protected: + + // Subclasses must override this factory method to return a new responder + virtual Responder *createResponder(const request_ptr_t &request) const = 0; + + // Subclasses must override to return a cap name + virtual const char *getCapabilityName() const = 0; + + virtual void sortQueue(); + virtual void serviceQueue(); + +private: + typedef std::list request_queue_t; + + void enqueue(const Request*); + + // Return whether the given object is/was in the queue + static LLMediaDataClient::request_ptr_t findOrRemove(request_queue_t &queue, const LLMediaDataClientObject::ptr_t &obj, bool remove, Request::Type type); + + // Comparator for sorting + static bool compareRequests(const request_ptr_t &o1, const request_ptr_t &o2); + static F64 getObjectScore(const LLMediaDataClientObject::ptr_t &obj); + + friend std::ostream& operator<<(std::ostream &s, const Request &q); + friend std::ostream& operator<<(std::ostream &s, const request_queue_t &q); + + class QueueTimer : public LLEventTimer + { + public: + QueueTimer(F32 time, LLMediaDataClient *mdc); + virtual BOOL tick(); + protected: + virtual ~QueueTimer(); + private: + // back-pointer + LLPointer mMDC; + }; + + void startQueueTimer(); + void stopQueueTimer(); + void setIsRunning(bool val) { mQueueTimerIsRunning = val; } + + void swapCurrentQueue(); + request_queue_t *getCurrentQueue(); + + const F32 mQueueTimerDelay; + const F32 mRetryTimerDelay; + const U32 mMaxNumRetries; + const U32 mMaxSortedQueueSize; + const U32 mMaxRoundRobinQueueSize; + + bool mQueueTimerIsRunning; + + request_queue_t mSortedQueue; + request_queue_t mRoundRobinQueue; + bool mCurrentQueueIsTheSortedQueue; +}; + + +// MediaDataClient specific for the ObjectMedia cap +class LLObjectMediaDataClient : public LLMediaDataClient +{ +public: + LLObjectMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES, + U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE, + U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE) + : LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries) + {} + virtual ~LLObjectMediaDataClient() {} + + void fetchMedia(LLMediaDataClientObject *object); + void updateMedia(LLMediaDataClientObject *object); + +protected: + // Subclasses must override this factory method to return a new responder + virtual Responder *createResponder(const request_ptr_t &request) const; + + // Subclasses must override to return a cap name + virtual const char *getCapabilityName() const; + + class Responder : public LLMediaDataClient::Responder + { + public: + Responder(const request_ptr_t &request) + : LLMediaDataClient::Responder(request) {} + virtual void result(const LLSD &content); + }; +}; + + +// MediaDataClient specific for the ObjectMediaNavigate cap +class LLObjectMediaNavigateClient : public LLMediaDataClient +{ +public: + // NOTE: from llmediaservice.h + static const int ERROR_PERMISSION_DENIED_CODE = 8002; + + LLObjectMediaNavigateClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES, + U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE, + U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE) + : LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries) + {} + virtual ~LLObjectMediaNavigateClient() {} + + void navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url); + +protected: + // Subclasses must override this factory method to return a new responder + virtual Responder *createResponder(const request_ptr_t &request) const; + + // Subclasses must override to return a cap name + virtual const char *getCapabilityName() const; + + class Responder : public LLMediaDataClient::Responder + { + public: + Responder(const request_ptr_t &request) + : LLMediaDataClient::Responder(request) {} + virtual void error(U32 status, const std::string& reason); + virtual void result(const LLSD &content); + private: + void mediaNavigateBounceBack(); + }; + +}; + + +#endif // LL_LLMEDIADATACLIENT_H diff --git a/linden/indra/newview/rcmoapradar.cpp b/linden/indra/newview/rcmoapradar.cpp new file mode 100644 index 0000000..f0593ef --- /dev/null +++ b/linden/indra/newview/rcmoapradar.cpp @@ -0,0 +1,440 @@ +// MOAP Radar +// Robin Cornelius + + +#include "llviewerprecompiledheaders.h" + +#include "llavatarconstants.h" +#include "llfloateravatarlist.h" + +#include "lluictrlfactory.h" +#include "llviewerwindow.h" +#include "llscrolllistctrl.h" +#include "llradiogroup.h" +#include "llviewercontrol.h" +#include "llbutton.h" + +#include "llvoavatar.h" +#include "llimview.h" +#include "rcmoapradar.h" +#include "llregionflags.h" +#include "llfloaterreporter.h" +#include "llagent.h" +#include "llviewerregion.h" +#include "lltracker.h" +#include "llviewerstats.h" +#include "llerror.h" +#include "llchat.h" +#include "llfloaterchat.h" +#include "llviewermessage.h" +#include "llweb.h" +#include "llviewerobjectlist.h" +#include "llmutelist.h" +#include "llcallbacklist.h" +#include "llmediaentry.h" + +#include +#include + +#include + +#include "llworld.h" + +#include "llsdutil.h" + +LLFloaterMOAPRadar* LLFloaterMOAPRadar::sInstance = NULL; + +LLFloaterMOAPRadar::LLFloaterMOAPRadar() : LLFloater(std::string("MOAPradar")) +{ + llassert_always(sInstance == NULL); + sInstance = this; + mUpdateRate = gSavedSettings.getU32("MOAPRadarUpdateRate") * 3 + 3; + mTrackingRunning=false; +} + +LLFloaterMOAPRadar::~LLFloaterMOAPRadar() +{ + gIdleCallbacks.deleteFunction(LLFloaterMOAPRadar::callbackIdle); + sInstance = NULL; +} + +//static +void LLFloaterMOAPRadar::toggle(void*) +{ + if (sInstance) + { + if (sInstance->getVisible()) + { + sInstance->close(false); + } + else + { + sInstance->open(); + } + } + else + { + showInstance(); + } +} + +//static +void LLFloaterMOAPRadar::showInstance() +{ + if (sInstance) + { + if (!sInstance->getVisible()) + { + sInstance->open(); + } + } + else + { + sInstance = new LLFloaterMOAPRadar(); + LLUICtrlFactory::getInstance()->buildFloater(sInstance, "floater_moap_radar.xml"); + } +} + +void LLFloaterMOAPRadar::draw() +{ + LLFloater::draw(); +} + +void LLFloaterMOAPRadar::onOpen() +{ + gSavedSettings.setBOOL("ShowMOAPRadar", TRUE); + sInstance->setVisible(TRUE); +} + +void LLFloaterMOAPRadar::onClose(bool app_quitting) +{ + sInstance->setVisible(FALSE); + if (!app_quitting) + { + gSavedSettings.setBOOL("ShowMOAPRadar", FALSE); + } + if (!gSavedSettings.getBOOL("MOAPRadarKeepOpen") || app_quitting) + { + destroy(); + } +} + + +BOOL LLFloaterMOAPRadar::postBuild() +{ + + mMOAPList = getChild("moap_list"); + mMOAPList->sortByColumn("distance", TRUE); + + mTrackBtn = getChild("track_btn"); + mTrackBtn->setLabel(LLStringExplicit("Track")); + + childSetAction("open_btn", onClickOpen, this); + childSetAction("track_btn", onClickTrack, this); + childSetAction("copy_btn", onClickCopy, this); + + mMOAPList->setCommitOnSelectionChange(TRUE); + childSetCommitCallback("moap_list", onSelectMOAP, this); + + gIdleCallbacks.addFunction(LLFloaterMOAPRadar::callbackIdle); + + return TRUE; +} + +//static +void LLFloaterMOAPRadar::callbackIdle(void *userdata) { + if (LLFloaterMOAPRadar::sInstance != NULL) + { + // Do not update at every frame: this would be insane ! + if (gFrameCount % LLFloaterMOAPRadar::sInstance->mUpdateRate == 0) + { + LLFloaterMOAPRadar::sInstance->updateMOAPList(); + } + } +} + + +void LLFloaterMOAPRadar::updateMOAPList() +{ + if (sInstance != this) return; + + mMOAPList->deleteAllItems(); + + S32 obj_count=gObjectList.getNumObjects(); + + int count=0; + + for(int objnum=0;objnum obj = gObjectList.getObject(objnum); + + if(obj.isNull()) + continue; + + if(obj->isDead() || obj->isOrphaned()) + continue; + + if(obj->getMediaType() == LLViewerObject::MEDIA_TYPE_NONE) + continue; + + LLSD element; + + element["id"] = obj->getID(); + + for(int face=0;facegetNumTEs();face++) + { + const LLTextureEntry * te=obj->getTE(face); + if(te==NULL) + continue; + + if(te->hasMedia()) + { + LLMediaEntry* media=te->getMediaData(); + + if(media==NULL) + continue; + + LLSD media_details; + media->asLLSD(media_details); + + element["columns"][LIST_URL]["column"] = "URL"; + element["columns"][LIST_URL]["type"] = "text"; + std::string URL=media->getCurrentURL(); + if(URL=="") + URL="(Waiting....)"; + + element["columns"][LIST_URL]["value"] = URL; + + if(mTrackingRunning && mTrackedID==obj->getID() && mTrackedFace==face) + { + element["columns"][LIST_URL]["font-style"] = "BOLD"; + } + else + { + element["columns"][LIST_URL]["font-style"] = "NORMAL"; + } + + element["columns"][LIST_FACE]["column"] = "face"; + element["columns"][LIST_FACE]["type"] = "text"; + std::stringstream face_buf; + face_buf << face; + element["columns"][LIST_FACE]["value"] = face_buf.str(); + + LLVector3d offset = gAgent.getPositionGlobal()-obj->getPositionGlobal(); + S32 dist= offset.length(); + + element["columns"][LIST_DISTANCE]["column"] = "distance"; + element["columns"][LIST_DISTANCE]["type"] = "text"; + std::stringstream dist_buf; + dist_buf << std::fixed << std::setprecision(2) << dist<<"m"; + element["columns"][LIST_DISTANCE]["value"] = dist_buf.str(); + + if(dist<25) + element["columns"][LIST_DISTANCE]["color"] = LLColor4::green.getValue(); + else if(dist<50) + element["columns"][LIST_DISTANCE]["color"] = LLColor4::blue1.getValue(); + else if(dist<100) + element["columns"][LIST_DISTANCE]["color"] = LLColor4::black.getValue(); + else if(dist<256) + element["columns"][LIST_DISTANCE]["color"] = LLColor4::yellow.getValue(); + else + element["columns"][LIST_DISTANCE]["color"] = LLColor4::red.getValue(); + + + element["columns"][LIST_POSITION]["column"] = "position"; + element["columns"][LIST_POSITION]["type"] = "text"; + element["columns"][LIST_POSITION]["value"] = ""; + + element["columns"][LIST_ALTITUDE]["column"] = "altitude"; + element["columns"][LIST_ALTITUDE]["type"] = "text"; + std::stringstream alt_buf; + alt_buf << std::fixed << std::setprecision(2) << obj->getPositionGlobal().mdV[2]<<"m"; + element["columns"][LIST_ALTITUDE]["value"] = alt_buf.str(); + + element["columns"][LIST_POSITION]["column"]="position"; + element["columns"][LIST_POSITION]["type"] = "text"; + std::stringstream pos_buf; + + S32 moap_x = (S32)offset.mdV[VX]; + S32 moap_y = (S32)offset.mdV[VY]; + if (moap_x >= -256 && moap_x <= 256 && moap_y >= -256 && moap_y <= 256) + { + pos_buf<< std::fixed << std::setprecision(2)<getPositionRegion().mV[VX]<<","<getPositionRegion().mV[VY]; + } + else + { + if (moap_y < 0) + pos_buf<<"S"; + else if (moap_y > 256) + pos_buf<<"N"; + + if (moap_x < 0) + pos_buf<<"W"; + else if (moap_x > 256) + pos_buf<<"E"; + } + + + element["columns"][LIST_POSITION]["value"] = pos_buf.str(); + + mMOAPList->addElement(element, ADD_BOTTOM); + + if(mSelectedObjID==obj->getID() && mSelectedFace==face) + { + mMOAPList->selectNthItem(count); + } + + count++; + } + } + } + + updatetrackbtn(); +} + +// static +void LLFloaterMOAPRadar::onClickOpen(void* userdata) +{ + LLFloaterMOAPRadar *self = (LLFloaterMOAPRadar*)userdata; + + LLDynamicArray ids = self->mMOAPList->getSelectedIDs(); + + if(ids.empty()) + { + return; + } + + LLUUID id=ids.front(); + + LLPointer obj = gObjectList.findObject(id); + if(obj.notNull()) + { + + const LLTextureEntry * te=obj->getTE(self->mSelectedFace); + if(te==NULL) + return; + + if(te->hasMedia()) + { + LLMediaEntry* media=te->getMediaData(); + if(media) + { + //gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(media->getCurrentURL())); + LLWeb::loadURL(media->getCurrentURL()); + } + } + } + +} + +// static +void LLFloaterMOAPRadar::onClickTrack(void* userdata) +{ + LLFloaterMOAPRadar *self = (LLFloaterMOAPRadar*)userdata; + + LLDynamicArray ids = self->mMOAPList->getSelectedIDs(); + + if(self->mTrackingRunning) + { + LLTracker::stopTracking(NULL); + self->mTrackingRunning=false; + } + + if(ids.empty()) + { + self->mTrackedID=LLUUID::null; + self->mTrackingRunning=false; + return; + } + + LLUUID id=ids.front(); + + if(id==self->mTrackedID && self->mSelectedFace==self->mTrackedFace) + { + self->mTrackedID=LLUUID::null; + self->mTrackingRunning=false; + return; + } + + self->mTrackedID=LLUUID::null; + + LLPointer obj = gObjectList.findObject(id); + if(obj.notNull()) + { + LLTracker::trackLocation(obj->getPositionGlobal(),"MOAP Tracking","",LLTracker::LOCATION_ITEM); + self->mTrackingRunning=true; + self->mTrackedID=id; + self->mTrackedFace=self->mSelectedFace; + } +} + +// static +void LLFloaterMOAPRadar::onClickCopy(void* userdata) +{ + LLFloaterMOAPRadar *self = (LLFloaterMOAPRadar*)userdata; + + LLDynamicArray ids = self->mMOAPList->getSelectedIDs(); + + if(ids.empty()) + { + LLTracker::stopTracking(NULL); + return; + } + + LLUUID id=ids.front(); + + LLPointer obj = gObjectList.findObject(id); + + if(obj.notNull()) + { + + const LLTextureEntry * te=obj->getTE(self->mSelectedFace); + if(te==NULL) + return; + + if(te->hasMedia()) + { + LLMediaEntry* media=te->getMediaData(); + if(media) + { + gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(media->getCurrentURL())); + } + } + } + +} + +//static +void LLFloaterMOAPRadar::onSelectMOAP(LLUICtrl*, void* userdata) +{ + LLFloaterMOAPRadar *self = (LLFloaterMOAPRadar*)userdata; + + LLScrollListItem *item = self->mMOAPList->getFirstSelected(); + if (item) + { + self->mSelectedObjID = item->getUUID(); + self->mSelectedFace = item->getColumn(LIST_FACE)->getValue().asInteger(); + } + + self->updatetrackbtn(); +} + +void LLFloaterMOAPRadar::updatetrackbtn() +{ + if( mTrackingRunning) + { + if( mTrackedID==mSelectedObjID && mTrackedFace==mSelectedFace) + { + mTrackBtn->setLabel(LLStringExplicit("Untrack")); + } + else + { + mTrackBtn->setLabel(LLStringExplicit("New Track")); + } + } + else + { + mTrackBtn->setLabel(LLStringExplicit("Track")); + } + +} \ No newline at end of file diff --git a/linden/indra/newview/rcmoapradar.h b/linden/indra/newview/rcmoapradar.h new file mode 100644 index 0000000..1d01546 --- /dev/null +++ b/linden/indra/newview/rcmoapradar.h @@ -0,0 +1,58 @@ + + +class LLFloaterMOAPRadar : public LLFloater +{ + +private: + LLFloaterMOAPRadar(); +public: + ~LLFloaterMOAPRadar(); + + enum AVATARS_COLUMN_ORDER + { + LIST_URL, + LIST_FACE, + LIST_DISTANCE, + LIST_POSITION, + LIST_ALTITUDE + }; + + /*virtual*/ void onClose(bool app_quitting); + /*virtual*/ void onOpen(); + /*virtual*/ BOOL postBuild(); + /*virtual*/ void draw(); + + static void toggle(void*); + + static void showInstance(); + + static void callbackIdle(void *userdata); + + void updateMOAPList(); + + static void onClickOpen(void* userdata); + static void onClickCopy(void* userdata); + static void onClickTrack(void* userdata); + static void onSelectMOAP(LLUICtrl*, void* userdata); + + +private: + static LLFloaterMOAPRadar* sInstance; + LLScrollListCtrl* mMOAPList; + LLButton * mTrackBtn; + + U32 mUpdateRate; + + LLUUID mSelectedObjID; + U8 mSelectedFace; + bool mTrackingRunning; + + LLUUID mTrackedID; + U8 mTrackedFace; + + void updatetrackbtn(); + +public: + static LLFloaterMOAPRadar* getInstance() { return sInstance; } + +}; \ No newline at end of file diff --git a/linden/indra/newview/skins/default/xui/en-us/floater_moap_radar.xml b/linden/indra/newview/skins/default/xui/en-us/floater_moap_radar.xml new file mode 100644 index 0000000..b9e3f99 --- /dev/null +++ b/linden/indra/newview/skins/default/xui/en-us/floater_moap_radar.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + +