aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorRobin Cornelius2011-10-01 22:32:42 +0100
committerDavid Walter Seikel2013-04-05 23:10:32 +1000
commita0dedb8a1f050465b4e447689672ea2845f0cd5b (patch)
tree94f66fbf80718a49c96e101c8275e7b1c55e536d
parentMOAP Radar patch set (diff)
downloadmeta-impy-a0dedb8a1f050465b4e447689672ea2845f0cd5b.zip
meta-impy-a0dedb8a1f050465b4e447689672ea2845f0cd5b.tar.gz
meta-impy-a0dedb8a1f050465b4e447689672ea2845f0cd5b.tar.bz2
meta-impy-a0dedb8a1f050465b4e447689672ea2845f0cd5b.tar.xz
And actually add the new files for MOAP radar
-rw-r--r--linden/indra/newview/llmediadataclient.cpp813
-rw-r--r--linden/indra/newview/llmediadataclient.h341
-rw-r--r--linden/indra/newview/rcmoapradar.cpp440
-rw-r--r--linden/indra/newview/rcmoapradar.h58
-rw-r--r--linden/indra/newview/skins/default/xui/en-us/floater_moap_radar.xml54
5 files changed, 1706 insertions, 0 deletions
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 @@
1/**
2 * @file llmediadataclient.cpp
3 * @brief class for queueing up requests for media data
4 *
5 * $LicenseInfo:firstyear=2001&license=viewergpl$
6 *
7 * Copyright (c) 2001-2010, Linden Research, Inc.
8 *
9 * Second Life Viewer Source Code
10 * The source code in this file ("Source Code") is provided by Linden Lab
11 * to you under the terms of the GNU General Public License, version 2.0
12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlife.com/developers/opensource/gplv2
16 *
17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at
21 * http://secondlife.com/developers/opensource/flossexception
22 *
23 * By copying, modifying or distributing this software, you acknowledge
24 * that you have read and understood your obligations described above,
25 * and agree to abide by those obligations.
26 *
27 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
28 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
29 * COMPLETENESS OR PERFORMANCE.
30 * $/LicenseInfo$
31 *
32 */
33
34#include "llviewerprecompiledheaders.h"
35
36#include "llmediadataclient.h"
37
38#if LL_MSVC
39// disable boost::lexical_cast warning
40#pragma warning (disable:4702)
41#endif
42
43#include <boost/lexical_cast.hpp>
44
45#include "llhttpstatuscodes.h"
46#include "llsdutil.h"
47#include "llmediaentry.h"
48#include "lltextureentry.h"
49#include "llviewerregion.h"
50
51//
52// When making a request
53// - obtain the "overall interest score" of the object.
54// This would be the sum of the impls' interest scores.
55// - put the request onto a queue sorted by this score
56// (highest score at the front of the queue)
57// - On a timer, once a second, pull off the head of the queue and send
58// the request.
59// - Any request that gets a 503 still goes through the retry logic
60//
61
62//
63// Forward decls
64//
65const F32 LLMediaDataClient::QUEUE_TIMER_DELAY = 1.0; // seconds(s)
66const F32 LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY = 10.0; // secs
67const U32 LLMediaDataClient::MAX_RETRIES = 10;
68const U32 LLMediaDataClient::MAX_SORTED_QUEUE_SIZE = 10000;
69const U32 LLMediaDataClient::MAX_ROUND_ROBIN_QUEUE_SIZE = 10000;
70
71// << operators
72std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q);
73std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &q);
74
75//////////////////////////////////////////////////////////////////////////////////////
76//
77// LLMediaDataClient
78//
79//////////////////////////////////////////////////////////////////////////////////////
80
81LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay,
82 F32 retry_timer_delay,
83 U32 max_retries,
84 U32 max_sorted_queue_size,
85 U32 max_round_robin_queue_size)
86 : mQueueTimerDelay(queue_timer_delay),
87 mRetryTimerDelay(retry_timer_delay),
88 mMaxNumRetries(max_retries),
89 mMaxSortedQueueSize(max_sorted_queue_size),
90 mMaxRoundRobinQueueSize(max_round_robin_queue_size),
91 mQueueTimerIsRunning(false),
92 mCurrentQueueIsTheSortedQueue(true)
93{
94}
95
96LLMediaDataClient::~LLMediaDataClient()
97{
98 stopQueueTimer();
99
100 // This should clear the queue, and hopefully call all the destructors.
101 LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClient destructor: queue: " <<
102 (isEmpty() ? "<empty> " : "<not empty> ") << LL_ENDL;
103
104 mSortedQueue.clear();
105 mRoundRobinQueue.clear();
106}
107
108bool LLMediaDataClient::isEmpty() const
109{
110 return mSortedQueue.empty() && mRoundRobinQueue.empty();
111}
112
113bool LLMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object)
114{
115 return (LLMediaDataClient::findOrRemove(mSortedQueue, object, false/*remove*/, LLMediaDataClient::Request::ANY).notNull()
116 || (LLMediaDataClient::findOrRemove(mRoundRobinQueue, object, false/*remove*/, LLMediaDataClient::Request::ANY).notNull()));
117}
118
119bool LLMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t &object)
120{
121 bool removedFromSortedQueue = LLMediaDataClient::findOrRemove(mSortedQueue, object, true/*remove*/, LLMediaDataClient::Request::ANY).notNull();
122 bool removedFromRoundRobinQueue = LLMediaDataClient::findOrRemove(mRoundRobinQueue, object, true/*remove*/, LLMediaDataClient::Request::ANY).notNull();
123 return removedFromSortedQueue || removedFromRoundRobinQueue;
124}
125
126//static
127LLMediaDataClient::request_ptr_t LLMediaDataClient::findOrRemove(request_queue_t &queue, const LLMediaDataClientObject::ptr_t &obj, bool remove, LLMediaDataClient::Request::Type type)
128{
129 request_ptr_t result;
130 request_queue_t::iterator iter = queue.begin();
131 request_queue_t::iterator end = queue.end();
132 while (iter != end)
133 {
134 if (obj->getID() == (*iter)->getObject()->getID() && (type == LLMediaDataClient::Request::ANY || type == (*iter)->getType()))
135 {
136 result = *iter;
137 if (remove) queue.erase(iter);
138 break;
139 }
140 iter++;
141 }
142 return result;
143}
144
145void LLMediaDataClient::request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload)
146{
147 if (object.isNull() || ! object->hasMedia()) return;
148
149 // Push the object on the queue
150 enqueue(new Request(getCapabilityName(), payload, object, this));
151}
152
153void LLMediaDataClient::enqueue(const Request *request)
154{
155 if (request->isNew())
156 {
157 // Add to sorted queue
158 if (LLMediaDataClient::findOrRemove(mSortedQueue, request->getObject(), true/*remove*/, request->getType()).notNull())
159 {
160 LL_DEBUGS("LLMediaDataClient") << "REMOVING OLD request for " << *request << " ALREADY THERE!" << LL_ENDL;
161 }
162
163 LL_DEBUGS("LLMediaDataClient") << "Queuing SORTED request for " << *request << LL_ENDL;
164
165 // Sadly, we have to const-cast because items put into the queue are not const
166 mSortedQueue.push_back(const_cast<LLMediaDataClient::Request*>(request));
167
168 LL_DEBUGS("LLMediaDataClientQueue") << "SORTED queue:" << mSortedQueue << LL_ENDL;
169 }
170 else {
171 if (mRoundRobinQueue.size() > mMaxRoundRobinQueueSize)
172 {
173 LL_INFOS_ONCE("LLMediaDataClient") << "RR QUEUE MAXED OUT!!!" << LL_ENDL;
174 LL_DEBUGS("LLMediaDataClient") << "Not queuing " << *request << LL_ENDL;
175 return;
176 }
177
178 // ROUND ROBIN: if it is there, and it is a GET request, leave it. If not, put at front!
179 request_ptr_t existing_request;
180 if (request->getType() == Request::GET)
181 {
182 existing_request = LLMediaDataClient::findOrRemove(mRoundRobinQueue, request->getObject(), false/*remove*/, request->getType());
183 }
184 if (existing_request.isNull())
185 {
186 LL_DEBUGS("LLMediaDataClient") << "Queuing RR request for " << *request << LL_ENDL;
187 // Push the request on the pending queue
188 // Sadly, we have to const-cast because items put into the queue are not const
189 mRoundRobinQueue.push_front(const_cast<LLMediaDataClient::Request*>(request));
190
191 LL_DEBUGS("LLMediaDataClientQueue") << "RR queue:" << mRoundRobinQueue << LL_ENDL;
192 }
193 else
194 {
195 LL_DEBUGS("LLMediaDataClient") << "ALREADY THERE: NOT Queuing request for " << *request << LL_ENDL;
196
197 existing_request->markSent(false);
198 }
199 }
200 // Start the timer if not already running
201 startQueueTimer();
202}
203
204void LLMediaDataClient::startQueueTimer()
205{
206 if (! mQueueTimerIsRunning)
207 {
208 LL_DEBUGS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL;
209 // LLEventTimer automagically takes care of the lifetime of this object
210 new QueueTimer(mQueueTimerDelay, this);
211 }
212 else {
213 LL_DEBUGS("LLMediaDataClient") << "not starting queue timer (it's already running, right???)" << LL_ENDL;
214 }
215}
216
217void LLMediaDataClient::stopQueueTimer()
218{
219 mQueueTimerIsRunning = false;
220}
221
222bool LLMediaDataClient::processQueueTimer()
223{
224 sortQueue();
225
226 if(!isEmpty())
227 {
228 LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, SORTED queue size is: " << mSortedQueue.size()
229 << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL;
230 LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() started, SORTED queue is: " << mSortedQueue << LL_ENDL;
231 LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() started, RR queue is: " << mRoundRobinQueue << LL_ENDL;
232 }
233
234 serviceQueue();
235
236 LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, SORTED queue size is: " << mSortedQueue.size()
237 << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL;
238 LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() finished, SORTED queue is: " << mSortedQueue << LL_ENDL;
239 LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() finished, RR queue is: " << mRoundRobinQueue << LL_ENDL;
240
241 return isEmpty();
242}
243
244void LLMediaDataClient::sortQueue()
245{
246 if(!mSortedQueue.empty())
247 {
248 // Score all items first
249 request_queue_t::iterator iter = mSortedQueue.begin();
250 request_queue_t::iterator end = mSortedQueue.end();
251 while (iter != end)
252 {
253 (*iter)->updateScore();
254 iter++;
255 }
256
257 // Re-sort the list...
258 // NOTE: should this be a stable_sort? If so we need to change to using a vector.
259 mSortedQueue.sort(LLMediaDataClient::compareRequests);
260
261 // ...then cull items over the max
262 U32 size = mSortedQueue.size();
263 if (size > mMaxSortedQueueSize)
264 {
265 U32 num_to_cull = (size - mMaxSortedQueueSize);
266 LL_INFOS_ONCE("LLMediaDataClient") << "sorted queue MAXED OUT! Culling "
267 << num_to_cull << " items" << LL_ENDL;
268 while (num_to_cull-- > 0)
269 {
270 mSortedQueue.pop_back();
271 }
272 }
273 }
274}
275
276// static
277bool LLMediaDataClient::compareRequests(const request_ptr_t &o1, const request_ptr_t &o2)
278{
279 if (o2.isNull()) return true;
280 if (o1.isNull()) return false;
281 return ( o1->getScore() > o2->getScore() );
282}
283
284void LLMediaDataClient::serviceQueue()
285{
286 request_queue_t *queue_p = getCurrentQueue();
287
288 // quick retry loop for cases where we shouldn't wait for the next timer tick
289 while(true)
290 {
291 if (queue_p->empty())
292 {
293 LL_DEBUGS("LLMediaDataClient") << "queue empty: " << (*queue_p) << LL_ENDL;
294 break;
295 }
296
297 // Peel one off of the items from the queue, and execute request
298 request_ptr_t request = queue_p->front();
299 llassert(!request.isNull());
300 const LLMediaDataClientObject *object = (request.isNull()) ? NULL : request->getObject();
301 llassert(NULL != object);
302
303 // Check for conditions that would make us just pop and rapidly loop through
304 // the queue.
305 if(request.isNull() ||
306 request->isMarkedSent() ||
307 NULL == object ||
308 object->isDead() ||
309 !object->hasMedia())
310 {
311 if (request.isNull())
312 {
313 LL_WARNS("LLMediaDataClient") << "Skipping NULL request" << LL_ENDL;
314 }
315 else {
316 LL_INFOS("LLMediaDataClient") << "Skipping : " << *request << " "
317 << ((request->isMarkedSent()) ? " request is marked sent" :
318 ((NULL == object) ? " object is NULL " :
319 ((object->isDead()) ? "object is dead" :
320 ((!object->hasMedia()) ? "object has no media!" : "BADNESS!")))) << LL_ENDL;
321 }
322 queue_p->pop_front();
323 continue; // jump back to the start of the quick retry loop
324 }
325
326 // Next, ask if this is "interesting enough" to fetch. If not, just stop
327 // and wait for the next timer go-round. Only do this for the sorted
328 // queue.
329 if (mCurrentQueueIsTheSortedQueue && !object->isInterestingEnough())
330 {
331 LL_DEBUGS("LLMediaDataClient") << "Not fetching " << *request << ": not interesting enough" << LL_ENDL;
332 break;
333 }
334
335 // Finally, try to send the HTTP message to the cap url
336 std::string url = request->getCapability();
337 bool maybe_retry = false;
338 if (!url.empty())
339 {
340 const LLSD &sd_payload = request->getPayload();
341 LL_INFOS("LLMediaDataClient") << "Sending request for " << *request << LL_ENDL;
342
343 // Call the subclass for creating the responder
344 LLHTTPClient::post(url, sd_payload, createResponder(request));
345 }
346 else {
347 LL_INFOS("LLMediaDataClient") << "NOT Sending request for " << *request << ": empty cap url!" << LL_ENDL;
348 maybe_retry = true;
349 }
350
351 bool exceeded_retries = request->getRetryCount() > mMaxNumRetries;
352 if (maybe_retry && ! exceeded_retries) // Try N times before giving up
353 {
354 // We got an empty cap, but in that case we will retry again next
355 // timer fire.
356 request->incRetryCount();
357 }
358 else {
359 if (exceeded_retries)
360 {
361 LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for "
362 << mMaxNumRetries << " tries...popping object id " << object->getID() << LL_ENDL;
363 // XXX Should we bring up a warning dialog??
364 }
365
366 queue_p->pop_front();
367
368 if (! mCurrentQueueIsTheSortedQueue) {
369 // Round robin
370 request->markSent(true);
371 mRoundRobinQueue.push_back(request);
372 }
373 }
374
375 // end of quick loop -- any cases where we want to loop will use 'continue' to jump back to the start.
376 break;
377 }
378
379 swapCurrentQueue();
380}
381
382void LLMediaDataClient::swapCurrentQueue()
383{
384 // Swap
385 mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue;
386 // If its empty, swap back
387 if (getCurrentQueue()->empty())
388 {
389 mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue;
390 }
391}
392
393LLMediaDataClient::request_queue_t *LLMediaDataClient::getCurrentQueue()
394{
395 return (mCurrentQueueIsTheSortedQueue) ? &mSortedQueue : &mRoundRobinQueue;
396}
397
398// dump the queue
399std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q)
400{
401 int i = 0;
402 LLMediaDataClient::request_queue_t::const_iterator iter = q.begin();
403 LLMediaDataClient::request_queue_t::const_iterator end = q.end();
404 while (iter != end)
405 {
406 s << "\t" << i << "]: " << (*iter)->getObject()->getID().asString() << "(" << (*iter)->getObject()->getMediaInterest() << ")";
407 iter++;
408 i++;
409 }
410 return s;
411}
412
413//////////////////////////////////////////////////////////////////////////////////////
414//
415// LLMediaDataClient::QueueTimer
416// Queue of LLMediaDataClientObject smart pointers to request media for.
417//
418//////////////////////////////////////////////////////////////////////////////////////
419
420LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc)
421: LLEventTimer(time), mMDC(mdc)
422{
423 mMDC->setIsRunning(true);
424}
425
426LLMediaDataClient::QueueTimer::~QueueTimer()
427{
428 LL_DEBUGS("LLMediaDataClient") << "~QueueTimer" << LL_ENDL;
429 mMDC->setIsRunning(false);
430 mMDC = NULL;
431}
432
433// virtual
434BOOL LLMediaDataClient::QueueTimer::tick()
435{
436 if (mMDC.isNull()) return TRUE;
437 return mMDC->processQueueTimer();
438}
439
440
441//////////////////////////////////////////////////////////////////////////////////////
442//
443// LLMediaDataClient::Responder::RetryTimer
444//
445//////////////////////////////////////////////////////////////////////////////////////
446
447LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr)
448: LLEventTimer(time), mResponder(mdr)
449{
450}
451
452// virtual
453LLMediaDataClient::Responder::RetryTimer::~RetryTimer()
454{
455 LL_DEBUGS("LLMediaDataClient") << "~RetryTimer" << *(mResponder->getRequest()) << LL_ENDL;
456
457 // XXX This is weird: Instead of doing the work in tick() (which re-schedules
458 // a timer, which might be risky), do it here, in the destructor. Yes, it is very odd.
459 // Instead of retrying, we just put the request back onto the queue
460 LL_INFOS("LLMediaDataClient") << "RetryTimer fired for: " << *(mResponder->getRequest()) << " retrying" << LL_ENDL;
461 mResponder->getRequest()->reEnqueue();
462
463 // Release the ref to the responder.
464 mResponder = NULL;
465}
466
467// virtual
468BOOL LLMediaDataClient::Responder::RetryTimer::tick()
469{
470 // Don't fire again
471 return TRUE;
472}
473
474
475//////////////////////////////////////////////////////////////////////////////////////
476//
477// LLMediaDataClient::Request
478//
479//////////////////////////////////////////////////////////////////////////////////////
480/*static*/U32 LLMediaDataClient::Request::sNum = 0;
481
482LLMediaDataClient::Request::Request(const char *cap_name,
483 const LLSD& sd_payload,
484 LLMediaDataClientObject *obj,
485 LLMediaDataClient *mdc)
486: mCapName(cap_name),
487 mPayload(sd_payload),
488 mObject(obj),
489 mNum(++sNum),
490 mRetryCount(0),
491 mMDC(mdc),
492 mMarkedSent(false),
493 mScore((F64)0.0)
494{
495}
496
497LLMediaDataClient::Request::~Request()
498{
499 LL_DEBUGS("LLMediaDataClient") << "~Request" << (*this) << LL_ENDL;
500 mMDC = NULL;
501 mObject = NULL;
502}
503
504
505std::string LLMediaDataClient::Request::getCapability() const
506{
507 return getObject()->getCapabilityUrl(getCapName());
508}
509
510// Helper function to get the "type" of request, which just pokes around to
511// discover it.
512LLMediaDataClient::Request::Type LLMediaDataClient::Request::getType() const
513{
514 if (0 == strcmp(mCapName, "ObjectMediaNavigate"))
515 {
516 return NAVIGATE;
517 }
518 else if (0 == strcmp(mCapName, "ObjectMedia"))
519 {
520 const std::string &verb = mPayload["verb"];
521 if (verb == "GET")
522 {
523 return GET;
524 }
525 else if (verb == "UPDATE")
526 {
527 return UPDATE;
528 }
529 }
530 llassert(false);
531 return GET;
532}
533
534const char *LLMediaDataClient::Request::getTypeAsString() const
535{
536 Type t = getType();
537 switch (t)
538 {
539 case GET:
540 return "GET";
541 break;
542 case UPDATE:
543 return "UPDATE";
544 break;
545 case NAVIGATE:
546 return "NAVIGATE";
547 break;
548 case ANY:
549 return "ANY";
550 break;
551 }
552 return "";
553}
554
555
556void LLMediaDataClient::Request::reEnqueue() const
557{
558 // I sure hope this doesn't deref a bad pointer:
559 mMDC->enqueue(this);
560}
561
562F32 LLMediaDataClient::Request::getRetryTimerDelay() const
563{
564 return (mMDC == NULL) ? LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY :
565 mMDC->mRetryTimerDelay;
566}
567
568U32 LLMediaDataClient::Request::getMaxNumRetries() const
569{
570 return (mMDC == NULL) ? LLMediaDataClient::MAX_RETRIES : mMDC->mMaxNumRetries;
571}
572
573void LLMediaDataClient::Request::markSent(bool flag)
574{
575 if (mMarkedSent != flag)
576 {
577 mMarkedSent = flag;
578 if (!mMarkedSent)
579 {
580 mNum = ++sNum;
581 }
582 }
583}
584
585void LLMediaDataClient::Request::updateScore()
586{
587 F64 tmp = mObject->getMediaInterest();
588 if (tmp != mScore)
589 {
590 LL_DEBUGS("LLMediaDataClient") << "Score for " << mObject->getID() << " changed from " << mScore << " to " << tmp << LL_ENDL;
591 mScore = tmp;
592 }
593}
594
595std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r)
596{
597 s << "request: num=" << r.getNum()
598 << " type=" << r.getTypeAsString()
599 << " ID=" << r.getObject()->getID()
600 << " #retries=" << r.getRetryCount();
601 return s;
602}
603
604
605//////////////////////////////////////////////////////////////////////////////////////
606//
607// LLMediaDataClient::Responder
608//
609//////////////////////////////////////////////////////////////////////////////////////
610
611LLMediaDataClient::Responder::Responder(const request_ptr_t &request)
612: mRequest(request)
613{
614}
615
616LLMediaDataClient::Responder::~Responder()
617{
618 LL_DEBUGS("LLMediaDataClient") << "~Responder" << *(getRequest()) << LL_ENDL;
619 mRequest = NULL;
620}
621
622/*virtual*/
623void LLMediaDataClient::Responder::error(U32 status, const std::string& reason)
624{
625 if (status == HTTP_SERVICE_UNAVAILABLE)
626 {
627 F32 retry_timeout = mRequest->getRetryTimerDelay();
628
629 mRequest->incRetryCount();
630
631 if (mRequest->getRetryCount() < mRequest->getMaxNumRetries())
632 {
633 LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL;
634
635 // Start timer (instances are automagically tracked by
636 // InstanceTracker<> and LLEventTimer)
637 new RetryTimer(F32(retry_timeout/*secs*/), this);
638 }
639 else {
640 LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retry count "
641 << mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL;
642 }
643 }
644 else {
645 std::string msg = boost::lexical_cast<std::string>(status) + ": " + reason;
646 LL_WARNS("LLMediaDataClient") << *mRequest << " http error(" << msg << ")" << LL_ENDL;
647 }
648}
649
650/*virtual*/
651void LLMediaDataClient::Responder::result(const LLSD& content)
652{
653 LL_DEBUGS("LLMediaDataClientResponse") << *mRequest << " result : " << ll_print_sd(content) << LL_ENDL;
654}
655
656//////////////////////////////////////////////////////////////////////////////////////
657//
658// LLObjectMediaDataClient
659// Subclass of LLMediaDataClient for the ObjectMedia cap
660//
661//////////////////////////////////////////////////////////////////////////////////////
662
663LLMediaDataClient::Responder *LLObjectMediaDataClient::createResponder(const request_ptr_t &request) const
664{
665 return new LLObjectMediaDataClient::Responder(request);
666}
667
668const char *LLObjectMediaDataClient::getCapabilityName() const
669{
670 return "ObjectMedia";
671}
672
673void LLObjectMediaDataClient::fetchMedia(LLMediaDataClientObject *object)
674{
675 LLSD sd_payload;
676 sd_payload["verb"] = "GET";
677 sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID();
678 request(object, sd_payload);
679}
680
681void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject *object)
682{
683 LLSD sd_payload;
684 sd_payload["verb"] = "UPDATE";
685 sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID();
686 LLSD object_media_data;
687 int i = 0;
688 int end = object->getMediaDataCount();
689 for ( ; i < end ; ++i)
690 {
691 object_media_data.append(object->getMediaDataLLSD(i));
692 }
693 sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data;
694
695 LL_DEBUGS("LLMediaDataClient") << "update media data: " << object->getID() << " " << ll_print_sd(sd_payload) << LL_ENDL;
696
697 request(object, sd_payload);
698}
699
700/*virtual*/
701void LLObjectMediaDataClient::Responder::result(const LLSD& content)
702{
703 const LLMediaDataClient::Request::Type type = getRequest()->getType();
704 llassert(type == LLMediaDataClient::Request::GET || type == LLMediaDataClient::Request::UPDATE)
705 if (type == LLMediaDataClient::Request::GET)
706 {
707 LL_DEBUGS("LLMediaDataClientResponse") << *(getRequest()) << " GET returned: " << ll_print_sd(content) << LL_ENDL;
708
709 // Look for an error
710 if (content.has("error"))
711 {
712 const LLSD &error = content["error"];
713 LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error getting media data for object: code=" <<
714 error["code"].asString() << ": " << error["message"].asString() << LL_ENDL;
715
716 // XXX Warn user?
717 }
718 else {
719 // Check the data
720 const LLUUID &object_id = content[LLTextureEntry::OBJECT_ID_KEY];
721 if (object_id != getRequest()->getObject()->getID())
722 {
723 // NOT good, wrong object id!!
724 LL_WARNS("LLMediaDataClient") << *(getRequest()) << " DROPPING response with wrong object id (" << object_id << ")" << LL_ENDL;
725 return;
726 }
727
728 // Otherwise, update with object media data
729 getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY],
730 content[LLTextureEntry::MEDIA_VERSION_KEY]);
731 }
732 }
733 else if (type == LLMediaDataClient::Request::UPDATE)
734 {
735 // just do what our superclass does
736 LLMediaDataClient::Responder::result(content);
737 }
738}
739
740//////////////////////////////////////////////////////////////////////////////////////
741//
742// LLObjectMediaNavigateClient
743// Subclass of LLMediaDataClient for the ObjectMediaNavigate cap
744//
745//////////////////////////////////////////////////////////////////////////////////////
746LLMediaDataClient::Responder *LLObjectMediaNavigateClient::createResponder(const request_ptr_t &request) const
747{
748 return new LLObjectMediaNavigateClient::Responder(request);
749}
750
751const char *LLObjectMediaNavigateClient::getCapabilityName() const
752{
753 return "ObjectMediaNavigate";
754}
755
756void LLObjectMediaNavigateClient::navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url)
757{
758 LLSD sd_payload;
759 sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID();
760 sd_payload[LLMediaEntry::CURRENT_URL_KEY] = url;
761 sd_payload[LLTextureEntry::TEXTURE_INDEX_KEY] = (LLSD::Integer)texture_index;
762
763 LL_INFOS("LLMediaDataClient") << "navigate() initiated: " << ll_print_sd(sd_payload) << LL_ENDL;
764
765 request(object, sd_payload);
766}
767
768/*virtual*/
769void LLObjectMediaNavigateClient::Responder::error(U32 status, const std::string& reason)
770{
771 // Bounce back (unless HTTP_SERVICE_UNAVAILABLE, in which case call base
772 // class
773 if (status == HTTP_SERVICE_UNAVAILABLE)
774 {
775 LLMediaDataClient::Responder::error(status, reason);
776 }
777 else {
778 // bounce the face back
779 LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: http code=" << status << LL_ENDL;
780 const LLSD &payload = getRequest()->getPayload();
781 // bounce the face back
782 getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]);
783 }
784}
785
786/*virtual*/
787void LLObjectMediaNavigateClient::Responder::result(const LLSD& content)
788{
789 LL_INFOS("LLMediaDataClient") << *(getRequest()) << " NAVIGATE returned " << ll_print_sd(content) << LL_ENDL;
790
791 if (content.has("error"))
792 {
793 const LLSD &error = content["error"];
794 int error_code = error["code"];
795
796 if (ERROR_PERMISSION_DENIED_CODE == error_code)
797 {
798 LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Navigation denied: bounce back" << LL_ENDL;
799 const LLSD &payload = getRequest()->getPayload();
800 // bounce the face back
801 getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]);
802 }
803 else {
804 LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: code=" <<
805 error["code"].asString() << ": " << error["message"].asString() << LL_ENDL;
806 }
807 // XXX Warn user?
808 }
809 else {
810 // just do what our superclass does
811 LLMediaDataClient::Responder::result(content);
812 }
813}
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 @@
1/**
2 * @file llmediadataclient.h
3 * @brief class for queueing up requests to the media service
4 *
5 * $LicenseInfo:firstyear=2007&license=viewergpl$
6 *
7 * Copyright (c) 2007-2010, Linden Research, Inc.
8 *
9 * Second Life Viewer Source Code
10 * The source code in this file ("Source Code") is provided by Linden Lab
11 * to you under the terms of the GNU General Public License, version 2.0
12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlife.com/developers/opensource/gplv2
16 *
17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at
21 * http://secondlife.com/developers/opensource/flossexception
22 *
23 * By copying, modifying or distributing this software, you acknowledge
24 * that you have read and understood your obligations described above,
25 * and agree to abide by those obligations.
26 *
27 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
28 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
29 * COMPLETENESS OR PERFORMANCE.
30 * $/LicenseInfo$
31 *
32 */
33
34#ifndef LL_LLMEDIADATACLIENT_H
35#define LL_LLMEDIADATACLIENT_H
36
37#include "llhttpclient.h"
38#include <queue>
39//#include "llrefcount.h"
40//#include "llpointer.h"
41//#include "lleventtimer.h"
42
43
44// Link seam for LLVOVolume
45class LLMediaDataClientObject : public LLRefCount
46{
47public:
48 // Get the number of media data items
49 virtual U8 getMediaDataCount() const = 0;
50 // Get the media data at index, as an LLSD
51 virtual LLSD getMediaDataLLSD(U8 index) const = 0;
52 // Get this object's UUID
53 virtual LLUUID getID() const = 0;
54 // Navigate back to previous URL
55 virtual void mediaNavigateBounceBack(U8 index) = 0;
56 // Does this object have media?
57 virtual bool hasMedia() const = 0;
58 // Update the object's media data to the given array
59 virtual void updateObjectMediaData(LLSD const &media_data_array, const std::string &version_string) = 0;
60 // Return the total "interest" of the media (on-screen area)
61 virtual F64 getMediaInterest() const = 0;
62 // Return the given cap url
63 virtual std::string getCapabilityUrl(const std::string &name) const = 0;
64 // Return whether the object has been marked dead
65 virtual bool isDead() const = 0;
66 // Returns a media version number for the object
67 virtual U32 getMediaVersion() const = 0;
68 // Returns whether the object is "interesting enough" to fetch
69 virtual bool isInterestingEnough() const = 0;
70 // Returns whether we've seen this object yet or not
71 virtual bool isNew() const = 0;
72
73 // smart pointer
74 typedef LLPointer<LLMediaDataClientObject> ptr_t;
75};
76
77// This object creates a priority queue for requests.
78// Abstracts the Cap URL, the request, and the responder
79class LLMediaDataClient : public LLRefCount
80{
81public:
82 LOG_CLASS(LLMediaDataClient);
83
84 const static F32 QUEUE_TIMER_DELAY;// = 1.0; // seconds(s)
85 const static F32 UNAVAILABLE_RETRY_TIMER_DELAY;// = 5.0; // secs
86 const static U32 MAX_RETRIES;// = 4;
87 const static U32 MAX_SORTED_QUEUE_SIZE;// = 10000;
88 const static U32 MAX_ROUND_ROBIN_QUEUE_SIZE;// = 10000;
89
90 // Constructor
91 LLMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY,
92 F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY,
93 U32 max_retries = MAX_RETRIES,
94 U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE,
95 U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE);
96
97 // Make the request
98 void request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload);
99
100 F32 getRetryTimerDelay() const { return mRetryTimerDelay; }
101
102 // Returns true iff the queue is empty
103 bool isEmpty() const;
104
105 // Returns true iff the given object is in the queue
106 bool isInQueue(const LLMediaDataClientObject::ptr_t &object);
107
108 // Remove the given object from the queue. Returns true iff the given object is removed.
109 bool removeFromQueue(const LLMediaDataClientObject::ptr_t &object);
110
111 // Called only by the Queue timer and tests (potentially)
112 bool processQueueTimer();
113
114protected:
115 // Destructor
116 virtual ~LLMediaDataClient(); // use unref
117
118 // Request
119 class Request : public LLRefCount
120 {
121 public:
122 enum Type {
123 GET,
124 UPDATE,
125 NAVIGATE,
126 ANY
127 };
128
129 Request(const char *cap_name, const LLSD& sd_payload, LLMediaDataClientObject *obj, LLMediaDataClient *mdc);
130 const char *getCapName() const { return mCapName; }
131 const LLSD &getPayload() const { return mPayload; }
132 LLMediaDataClientObject *getObject() const { return mObject; }
133
134 U32 getNum() const { return mNum; }
135
136 U32 getRetryCount() const { return mRetryCount; }
137 void incRetryCount() { mRetryCount++; }
138
139 // Note: may return empty string!
140 std::string getCapability() const;
141
142 Type getType() const;
143 const char *getTypeAsString() const;
144
145 // Re-enqueue thyself
146 void reEnqueue() const;
147
148 F32 getRetryTimerDelay() const;
149 U32 getMaxNumRetries() const;
150
151 bool isNew() const { return mObject.notNull() ? mObject->isNew() : false; }
152 void markSent(bool flag);
153 bool isMarkedSent() const { return mMarkedSent; }
154 void updateScore();
155 F64 getScore() const { return mScore; }
156
157 public:
158 friend std::ostream& operator<<(std::ostream &s, const Request &q);
159
160 protected:
161 virtual ~Request(); // use unref();
162
163 private:
164 const char *mCapName;
165 LLSD mPayload;
166 LLMediaDataClientObject::ptr_t mObject;
167 // Simple tracking
168 U32 mNum;
169 static U32 sNum;
170 U32 mRetryCount;
171 F64 mScore;
172 bool mMarkedSent;
173
174 // Back pointer to the MDC...not a ref!
175 LLMediaDataClient *mMDC;
176 };
177 typedef LLPointer<Request> request_ptr_t;
178
179 // Responder
180 class Responder : public LLHTTPClient::Responder
181 {
182 public:
183 Responder(const request_ptr_t &request);
184 //If we get back an error (not found, etc...), handle it here
185 virtual void error(U32 status, const std::string& reason);
186 //If we get back a normal response, handle it here. Default just logs it.
187 virtual void result(const LLSD& content);
188
189 const request_ptr_t &getRequest() const { return mRequest; }
190
191 protected:
192 virtual ~Responder();
193
194 private:
195
196 class RetryTimer : public LLEventTimer
197 {
198 public:
199 RetryTimer(F32 time, Responder *);
200 virtual ~RetryTimer();
201 virtual BOOL tick();
202 private:
203 // back-pointer
204 boost::intrusive_ptr<Responder> mResponder;
205 };
206
207 request_ptr_t mRequest;
208 };
209
210protected:
211
212 // Subclasses must override this factory method to return a new responder
213 virtual Responder *createResponder(const request_ptr_t &request) const = 0;
214
215 // Subclasses must override to return a cap name
216 virtual const char *getCapabilityName() const = 0;
217
218 virtual void sortQueue();
219 virtual void serviceQueue();
220
221private:
222 typedef std::list<request_ptr_t> request_queue_t;
223
224 void enqueue(const Request*);
225
226 // Return whether the given object is/was in the queue
227 static LLMediaDataClient::request_ptr_t findOrRemove(request_queue_t &queue, const LLMediaDataClientObject::ptr_t &obj, bool remove, Request::Type type);
228
229 // Comparator for sorting
230 static bool compareRequests(const request_ptr_t &o1, const request_ptr_t &o2);
231 static F64 getObjectScore(const LLMediaDataClientObject::ptr_t &obj);
232
233 friend std::ostream& operator<<(std::ostream &s, const Request &q);
234 friend std::ostream& operator<<(std::ostream &s, const request_queue_t &q);
235
236 class QueueTimer : public LLEventTimer
237 {
238 public:
239 QueueTimer(F32 time, LLMediaDataClient *mdc);
240 virtual BOOL tick();
241 protected:
242 virtual ~QueueTimer();
243 private:
244 // back-pointer
245 LLPointer<LLMediaDataClient> mMDC;
246 };
247
248 void startQueueTimer();
249 void stopQueueTimer();
250 void setIsRunning(bool val) { mQueueTimerIsRunning = val; }
251
252 void swapCurrentQueue();
253 request_queue_t *getCurrentQueue();
254
255 const F32 mQueueTimerDelay;
256 const F32 mRetryTimerDelay;
257 const U32 mMaxNumRetries;
258 const U32 mMaxSortedQueueSize;
259 const U32 mMaxRoundRobinQueueSize;
260
261 bool mQueueTimerIsRunning;
262
263 request_queue_t mSortedQueue;
264 request_queue_t mRoundRobinQueue;
265 bool mCurrentQueueIsTheSortedQueue;
266};
267
268
269// MediaDataClient specific for the ObjectMedia cap
270class LLObjectMediaDataClient : public LLMediaDataClient
271{
272public:
273 LLObjectMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY,
274 F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY,
275 U32 max_retries = MAX_RETRIES,
276 U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE,
277 U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE)
278 : LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries)
279 {}
280 virtual ~LLObjectMediaDataClient() {}
281
282 void fetchMedia(LLMediaDataClientObject *object);
283 void updateMedia(LLMediaDataClientObject *object);
284
285protected:
286 // Subclasses must override this factory method to return a new responder
287 virtual Responder *createResponder(const request_ptr_t &request) const;
288
289 // Subclasses must override to return a cap name
290 virtual const char *getCapabilityName() const;
291
292 class Responder : public LLMediaDataClient::Responder
293 {
294 public:
295 Responder(const request_ptr_t &request)
296 : LLMediaDataClient::Responder(request) {}
297 virtual void result(const LLSD &content);
298 };
299};
300
301
302// MediaDataClient specific for the ObjectMediaNavigate cap
303class LLObjectMediaNavigateClient : public LLMediaDataClient
304{
305public:
306 // NOTE: from llmediaservice.h
307 static const int ERROR_PERMISSION_DENIED_CODE = 8002;
308
309 LLObjectMediaNavigateClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY,
310 F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY,
311 U32 max_retries = MAX_RETRIES,
312 U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE,
313 U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE)
314 : LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries)
315 {}
316 virtual ~LLObjectMediaNavigateClient() {}
317
318 void navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url);
319
320protected:
321 // Subclasses must override this factory method to return a new responder
322 virtual Responder *createResponder(const request_ptr_t &request) const;
323
324 // Subclasses must override to return a cap name
325 virtual const char *getCapabilityName() const;
326
327 class Responder : public LLMediaDataClient::Responder
328 {
329 public:
330 Responder(const request_ptr_t &request)
331 : LLMediaDataClient::Responder(request) {}
332 virtual void error(U32 status, const std::string& reason);
333 virtual void result(const LLSD &content);
334 private:
335 void mediaNavigateBounceBack();
336 };
337
338};
339
340
341#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 @@
1// MOAP Radar
2// Robin Cornelius
3
4
5#include "llviewerprecompiledheaders.h"
6
7#include "llavatarconstants.h"
8#include "llfloateravatarlist.h"
9
10#include "lluictrlfactory.h"
11#include "llviewerwindow.h"
12#include "llscrolllistctrl.h"
13#include "llradiogroup.h"
14#include "llviewercontrol.h"
15#include "llbutton.h"
16
17#include "llvoavatar.h"
18#include "llimview.h"
19#include "rcmoapradar.h"
20#include "llregionflags.h"
21#include "llfloaterreporter.h"
22#include "llagent.h"
23#include "llviewerregion.h"
24#include "lltracker.h"
25#include "llviewerstats.h"
26#include "llerror.h"
27#include "llchat.h"
28#include "llfloaterchat.h"
29#include "llviewermessage.h"
30#include "llweb.h"
31#include "llviewerobjectlist.h"
32#include "llmutelist.h"
33#include "llcallbacklist.h"
34#include "llmediaentry.h"
35
36#include <time.h>
37#include <string.h>
38
39#include <map>
40
41#include "llworld.h"
42
43#include "llsdutil.h"
44
45LLFloaterMOAPRadar* LLFloaterMOAPRadar::sInstance = NULL;
46
47LLFloaterMOAPRadar::LLFloaterMOAPRadar() : LLFloater(std::string("MOAPradar"))
48{
49 llassert_always(sInstance == NULL);
50 sInstance = this;
51 mUpdateRate = gSavedSettings.getU32("MOAPRadarUpdateRate") * 3 + 3;
52 mTrackingRunning=false;
53}
54
55LLFloaterMOAPRadar::~LLFloaterMOAPRadar()
56{
57 gIdleCallbacks.deleteFunction(LLFloaterMOAPRadar::callbackIdle);
58 sInstance = NULL;
59}
60
61//static
62void LLFloaterMOAPRadar::toggle(void*)
63{
64 if (sInstance)
65 {
66 if (sInstance->getVisible())
67 {
68 sInstance->close(false);
69 }
70 else
71 {
72 sInstance->open();
73 }
74 }
75 else
76 {
77 showInstance();
78 }
79}
80
81//static
82void LLFloaterMOAPRadar::showInstance()
83{
84 if (sInstance)
85 {
86 if (!sInstance->getVisible())
87 {
88 sInstance->open();
89 }
90 }
91 else
92 {
93 sInstance = new LLFloaterMOAPRadar();
94 LLUICtrlFactory::getInstance()->buildFloater(sInstance, "floater_moap_radar.xml");
95 }
96}
97
98void LLFloaterMOAPRadar::draw()
99{
100 LLFloater::draw();
101}
102
103void LLFloaterMOAPRadar::onOpen()
104{
105 gSavedSettings.setBOOL("ShowMOAPRadar", TRUE);
106 sInstance->setVisible(TRUE);
107}
108
109void LLFloaterMOAPRadar::onClose(bool app_quitting)
110{
111 sInstance->setVisible(FALSE);
112 if (!app_quitting)
113 {
114 gSavedSettings.setBOOL("ShowMOAPRadar", FALSE);
115 }
116 if (!gSavedSettings.getBOOL("MOAPRadarKeepOpen") || app_quitting)
117 {
118 destroy();
119 }
120}
121
122
123BOOL LLFloaterMOAPRadar::postBuild()
124{
125
126 mMOAPList = getChild<LLScrollListCtrl>("moap_list");
127 mMOAPList->sortByColumn("distance", TRUE);
128
129 mTrackBtn = getChild<LLButton>("track_btn");
130 mTrackBtn->setLabel(LLStringExplicit("Track"));
131
132 childSetAction("open_btn", onClickOpen, this);
133 childSetAction("track_btn", onClickTrack, this);
134 childSetAction("copy_btn", onClickCopy, this);
135
136 mMOAPList->setCommitOnSelectionChange(TRUE);
137 childSetCommitCallback("moap_list", onSelectMOAP, this);
138
139 gIdleCallbacks.addFunction(LLFloaterMOAPRadar::callbackIdle);
140
141 return TRUE;
142}
143
144//static
145void LLFloaterMOAPRadar::callbackIdle(void *userdata) {
146 if (LLFloaterMOAPRadar::sInstance != NULL)
147 {
148 // Do not update at every frame: this would be insane !
149 if (gFrameCount % LLFloaterMOAPRadar::sInstance->mUpdateRate == 0)
150 {
151 LLFloaterMOAPRadar::sInstance->updateMOAPList();
152 }
153 }
154}
155
156
157void LLFloaterMOAPRadar::updateMOAPList()
158{
159 if (sInstance != this) return;
160
161 mMOAPList->deleteAllItems();
162
163 S32 obj_count=gObjectList.getNumObjects();
164
165 int count=0;
166
167 for(int objnum=0;objnum<obj_count;objnum++)
168 {
169 LLPointer<LLViewerObject> obj = gObjectList.getObject(objnum);
170
171 if(obj.isNull())
172 continue;
173
174 if(obj->isDead() || obj->isOrphaned())
175 continue;
176
177 if(obj->getMediaType() == LLViewerObject::MEDIA_TYPE_NONE)
178 continue;
179
180 LLSD element;
181
182 element["id"] = obj->getID();
183
184 for(int face=0;face<obj->getNumTEs();face++)
185 {
186 const LLTextureEntry * te=obj->getTE(face);
187 if(te==NULL)
188 continue;
189
190 if(te->hasMedia())
191 {
192 LLMediaEntry* media=te->getMediaData();
193
194 if(media==NULL)
195 continue;
196
197 LLSD media_details;
198 media->asLLSD(media_details);
199
200 element["columns"][LIST_URL]["column"] = "URL";
201 element["columns"][LIST_URL]["type"] = "text";
202 std::string URL=media->getCurrentURL();
203 if(URL=="")
204 URL="(Waiting....)";
205
206 element["columns"][LIST_URL]["value"] = URL;
207
208 if(mTrackingRunning && mTrackedID==obj->getID() && mTrackedFace==face)
209 {
210 element["columns"][LIST_URL]["font-style"] = "BOLD";
211 }
212 else
213 {
214 element["columns"][LIST_URL]["font-style"] = "NORMAL";
215 }
216
217 element["columns"][LIST_FACE]["column"] = "face";
218 element["columns"][LIST_FACE]["type"] = "text";
219 std::stringstream face_buf;
220 face_buf << face;
221 element["columns"][LIST_FACE]["value"] = face_buf.str();
222
223 LLVector3d offset = gAgent.getPositionGlobal()-obj->getPositionGlobal();
224 S32 dist= offset.length();
225
226 element["columns"][LIST_DISTANCE]["column"] = "distance";
227 element["columns"][LIST_DISTANCE]["type"] = "text";
228 std::stringstream dist_buf;
229 dist_buf << std::fixed << std::setprecision(2) << dist<<"m";
230 element["columns"][LIST_DISTANCE]["value"] = dist_buf.str();
231
232 if(dist<25)
233 element["columns"][LIST_DISTANCE]["color"] = LLColor4::green.getValue();
234 else if(dist<50)
235 element["columns"][LIST_DISTANCE]["color"] = LLColor4::blue1.getValue();
236 else if(dist<100)
237 element["columns"][LIST_DISTANCE]["color"] = LLColor4::black.getValue();
238 else if(dist<256)
239 element["columns"][LIST_DISTANCE]["color"] = LLColor4::yellow.getValue();
240 else
241 element["columns"][LIST_DISTANCE]["color"] = LLColor4::red.getValue();
242
243
244 element["columns"][LIST_POSITION]["column"] = "position";
245 element["columns"][LIST_POSITION]["type"] = "text";
246 element["columns"][LIST_POSITION]["value"] = "";
247
248 element["columns"][LIST_ALTITUDE]["column"] = "altitude";
249 element["columns"][LIST_ALTITUDE]["type"] = "text";
250 std::stringstream alt_buf;
251 alt_buf << std::fixed << std::setprecision(2) << obj->getPositionGlobal().mdV[2]<<"m";
252 element["columns"][LIST_ALTITUDE]["value"] = alt_buf.str();
253
254 element["columns"][LIST_POSITION]["column"]="position";
255 element["columns"][LIST_POSITION]["type"] = "text";
256 std::stringstream pos_buf;
257
258 S32 moap_x = (S32)offset.mdV[VX];
259 S32 moap_y = (S32)offset.mdV[VY];
260 if (moap_x >= -256 && moap_x <= 256 && moap_y >= -256 && moap_y <= 256)
261 {
262 pos_buf<< std::fixed << std::setprecision(2)<<obj->getPositionRegion().mV[VX]<<","<<obj->getPositionRegion().mV[VY];
263 }
264 else
265 {
266 if (moap_y < 0)
267 pos_buf<<"S";
268 else if (moap_y > 256)
269 pos_buf<<"N";
270
271 if (moap_x < 0)
272 pos_buf<<"W";
273 else if (moap_x > 256)
274 pos_buf<<"E";
275 }
276
277
278 element["columns"][LIST_POSITION]["value"] = pos_buf.str();
279
280 mMOAPList->addElement(element, ADD_BOTTOM);
281
282 if(mSelectedObjID==obj->getID() && mSelectedFace==face)
283 {
284 mMOAPList->selectNthItem(count);
285 }
286
287 count++;
288 }
289 }
290 }
291
292 updatetrackbtn();
293}
294
295// static
296void LLFloaterMOAPRadar::onClickOpen(void* userdata)
297{
298 LLFloaterMOAPRadar *self = (LLFloaterMOAPRadar*)userdata;
299
300 LLDynamicArray<LLUUID> ids = self->mMOAPList->getSelectedIDs();
301
302 if(ids.empty())
303 {
304 return;
305 }
306
307 LLUUID id=ids.front();
308
309 LLPointer<LLViewerObject> obj = gObjectList.findObject(id);
310 if(obj.notNull())
311 {
312
313 const LLTextureEntry * te=obj->getTE(self->mSelectedFace);
314 if(te==NULL)
315 return;
316
317 if(te->hasMedia())
318 {
319 LLMediaEntry* media=te->getMediaData();
320 if(media)
321 {
322 //gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(media->getCurrentURL()));
323 LLWeb::loadURL(media->getCurrentURL());
324 }
325 }
326 }
327
328}
329
330// static
331void LLFloaterMOAPRadar::onClickTrack(void* userdata)
332{
333 LLFloaterMOAPRadar *self = (LLFloaterMOAPRadar*)userdata;
334
335 LLDynamicArray<LLUUID> ids = self->mMOAPList->getSelectedIDs();
336
337 if(self->mTrackingRunning)
338 {
339 LLTracker::stopTracking(NULL);
340 self->mTrackingRunning=false;
341 }
342
343 if(ids.empty())
344 {
345 self->mTrackedID=LLUUID::null;
346 self->mTrackingRunning=false;
347 return;
348 }
349
350 LLUUID id=ids.front();
351
352 if(id==self->mTrackedID && self->mSelectedFace==self->mTrackedFace)
353 {
354 self->mTrackedID=LLUUID::null;
355 self->mTrackingRunning=false;
356 return;
357 }
358
359 self->mTrackedID=LLUUID::null;
360
361 LLPointer<LLViewerObject> obj = gObjectList.findObject(id);
362 if(obj.notNull())
363 {
364 LLTracker::trackLocation(obj->getPositionGlobal(),"MOAP Tracking","",LLTracker::LOCATION_ITEM);
365 self->mTrackingRunning=true;
366 self->mTrackedID=id;
367 self->mTrackedFace=self->mSelectedFace;
368 }
369}
370
371// static
372void LLFloaterMOAPRadar::onClickCopy(void* userdata)
373{
374 LLFloaterMOAPRadar *self = (LLFloaterMOAPRadar*)userdata;
375
376 LLDynamicArray<LLUUID> ids = self->mMOAPList->getSelectedIDs();
377
378 if(ids.empty())
379 {
380 LLTracker::stopTracking(NULL);
381 return;
382 }
383
384 LLUUID id=ids.front();
385
386 LLPointer<LLViewerObject> obj = gObjectList.findObject(id);
387
388 if(obj.notNull())
389 {
390
391 const LLTextureEntry * te=obj->getTE(self->mSelectedFace);
392 if(te==NULL)
393 return;
394
395 if(te->hasMedia())
396 {
397 LLMediaEntry* media=te->getMediaData();
398 if(media)
399 {
400 gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(media->getCurrentURL()));
401 }
402 }
403 }
404
405}
406
407//static
408void LLFloaterMOAPRadar::onSelectMOAP(LLUICtrl*, void* userdata)
409{
410 LLFloaterMOAPRadar *self = (LLFloaterMOAPRadar*)userdata;
411
412 LLScrollListItem *item = self->mMOAPList->getFirstSelected();
413 if (item)
414 {
415 self->mSelectedObjID = item->getUUID();
416 self->mSelectedFace = item->getColumn(LIST_FACE)->getValue().asInteger();
417 }
418
419 self->updatetrackbtn();
420}
421
422void LLFloaterMOAPRadar::updatetrackbtn()
423{
424 if( mTrackingRunning)
425 {
426 if( mTrackedID==mSelectedObjID && mTrackedFace==mSelectedFace)
427 {
428 mTrackBtn->setLabel(LLStringExplicit("Untrack"));
429 }
430 else
431 {
432 mTrackBtn->setLabel(LLStringExplicit("New Track"));
433 }
434 }
435 else
436 {
437 mTrackBtn->setLabel(LLStringExplicit("Track"));
438 }
439
440} \ 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 @@
1
2
3class LLFloaterMOAPRadar : public LLFloater
4{
5
6private:
7 LLFloaterMOAPRadar();
8public:
9 ~LLFloaterMOAPRadar();
10
11 enum AVATARS_COLUMN_ORDER
12 {
13 LIST_URL,
14 LIST_FACE,
15 LIST_DISTANCE,
16 LIST_POSITION,
17 LIST_ALTITUDE
18 };
19
20 /*virtual*/ void onClose(bool app_quitting);
21 /*virtual*/ void onOpen();
22 /*virtual*/ BOOL postBuild();
23 /*virtual*/ void draw();
24
25 static void toggle(void*);
26
27 static void showInstance();
28
29 static void callbackIdle(void *userdata);
30
31 void updateMOAPList();
32
33 static void onClickOpen(void* userdata);
34 static void onClickCopy(void* userdata);
35 static void onClickTrack(void* userdata);
36 static void onSelectMOAP(LLUICtrl*, void* userdata);
37
38
39private:
40 static LLFloaterMOAPRadar* sInstance;
41 LLScrollListCtrl* mMOAPList;
42 LLButton * mTrackBtn;
43
44 U32 mUpdateRate;
45
46 LLUUID mSelectedObjID;
47 U8 mSelectedFace;
48 bool mTrackingRunning;
49
50 LLUUID mTrackedID;
51 U8 mTrackedFace;
52
53 void updatetrackbtn();
54
55public:
56 static LLFloaterMOAPRadar* getInstance() { return sInstance; }
57
58}; \ 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 @@
1<?xml version="1.0" encoding="utf-8" standalone="yes"?>
2<floater name="MOAP" title="MOAP Radar"
3 can_resize="true" can_minimize="true" can_close="true" can_drag_on_left="false"
4 rect_control="FloaterRadarRect" min_width="700" min_height="150">
5
6 <scroll_list name="moap_list"
7 left="10" right="-10" top="-20" bottom="40" can_resize="true"
8 column_padding="0" follows="left|top|bottom|right"
9 draw_heading="true" multi_select="true" search_column="1"
10 tool_tip="MOAP List">
11 <column name="URL" label="URL" width="450" tool_tip="URL of MOAP"/>
12 <column name="face" label="Face" width="48" tool_tip="URL of MOAP"/>
13 <column name="distance" label="Dist." width="48" tool_tip="Distance from your avatar"/>
14 <column name="position" label="Pos." width="90" tool_tip="Position (X, Y) within this sim, or general direction (cardinal point) for outside sims"/>
15 <column name="altitude" label="Alt." width="48" tool_tip="Altitude"/>
16 </scroll_list>
17
18
19 <button
20 name="open_btn"
21 label="Open"
22 tool_tip="Open Current URL"
23 left="10"
24 bottom_delta="-30"
25 width="80"
26 height="20"
27 font="SansSerifSmall"
28 follows="bottom|left"
29 />
30 <button
31 name="track_btn"
32 label="(Un)Track"
33 tool_tip="Toggle tracking of this MOAPs position"
34 left_delta="90"
35 bottom_delta="0"
36 width="80"
37 height="20"
38 font="SansSerifSmall"
39 follows="bottom|left"
40 />
41 <button
42 name="copy_btn"
43 label="Get URL"
44 tool_tip="Copies URL to the clipboard"
45 left_delta="90"
46 bottom_delta="0"
47 width="80"
48 height="20"
49 font="SansSerifSmall"
50 follows="bottom|left"
51 />
52 <!-- upper middle row -->
53
54</floater>