summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/CMakeLists.txt5
-rw-r--r--indra/newview/llagent.cpp209
-rw-r--r--indra/newview/llagent.h4
-rw-r--r--indra/newview/llagentlanguage.h2
-rw-r--r--indra/newview/llappviewer.cpp13
-rw-r--r--indra/newview/llcapabilitylistener.cpp183
-rw-r--r--indra/newview/llcapabilitylistener.h113
-rw-r--r--indra/newview/llcapabilityprovider.h39
-rw-r--r--indra/newview/llfloatergroups.cpp2
-rw-r--r--indra/newview/llfloatergroups.h4
-rw-r--r--indra/newview/llinventorybridge.cpp2
-rw-r--r--indra/newview/llnetmap.cpp2
-rw-r--r--indra/newview/llnetmap.h10
-rw-r--r--indra/newview/llviewercontrol.h8
-rw-r--r--indra/newview/llviewerinventory.cpp63
-rw-r--r--indra/newview/llviewermedia.cpp2
-rw-r--r--indra/newview/llviewermenu.cpp3
-rw-r--r--indra/newview/llviewermenufile.cpp2
-rw-r--r--indra/newview/llviewerregion.cpp33
-rw-r--r--indra/newview/llviewerregion.h26
-rw-r--r--indra/newview/llviewerthrottle.cpp2
-rw-r--r--indra/newview/tests/llcapabilitylistener_test.cpp274
22 files changed, 841 insertions, 160 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 7b1c3003f0..57f1716db5 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -78,6 +78,7 @@ set(viewer_SOURCE_FILES
llbox.cpp
llcallbacklist.cpp
llcallingcard.cpp
+ llcapabilitylistener.cpp
llcaphttpsender.cpp
llchatbar.cpp
llclassifiedinfo.cpp
@@ -481,6 +482,8 @@ set(viewer_HEADER_FILES
llbox.h
llcallbacklist.h
llcallingcard.h
+ llcapabilitylistener.h
+ llcapabilityprovider.h
llcaphttpsender.h
llchatbar.h
llclassifiedinfo.h
@@ -1391,3 +1394,5 @@ if (INSTALL)
endif (INSTALL)
ADD_VIEWER_BUILD_TEST(llagentaccess viewer)
+ADD_VIEWER_COMM_BUILD_TEST(llcapabilitylistener viewer
+ ${CMAKE_CURRENT_SOURCE_DIR}/../llmessage/tests/test_llsdmessage_peer.py)
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 98526c8a57..07503404dc 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -134,6 +134,8 @@
#include "llappviewer.h"
#include "llviewerjoystick.h"
#include "llfollowcam.h"
+#include "stringize.h"
+#include "llcapabilitylistener.h"
using namespace LLVOAvatarDefines;
@@ -914,7 +916,7 @@ LLViewerRegion *LLAgent::getRegion() const
}
-const LLHost& LLAgent::getRegionHost() const
+LLHost LLAgent::getRegionHost() const
{
if (mRegionp)
{
@@ -4674,109 +4676,128 @@ void LLAgent::lookAtLastChat()
const F32 SIT_POINT_EXTENTS = 0.2f;
+LLSD ll_sdmap_from_vector3(const LLVector3& vec)
+{
+ LLSD ret;
+ ret["X"] = vec.mV[VX];
+ ret["Y"] = vec.mV[VY];
+ ret["Z"] = vec.mV[VZ];
+ return ret;
+}
+
+LLVector3 ll_vector3_from_sdmap(const LLSD& sd)
+{
+ LLVector3 ret;
+ ret.mV[VX] = F32(sd["X"].asReal());
+ ret.mV[VY] = F32(sd["Y"].asReal());
+ ret.mV[VZ] = F32(sd["Z"].asReal());
+ return ret;
+}
+
void LLAgent::setStartPosition( U32 location_id )
{
- LLViewerObject *object;
+ LLViewerObject *object;
- if ( !(gAgentID == LLUUID::null) )
- {
+ if (gAgentID == LLUUID::null)
+ {
+ return;
+ }
// we've got an ID for an agent viewerobject
object = gObjectList.findObject(gAgentID);
- if (object)
+ if (! object)
{
- // we've got the viewer object
- // Sometimes the agent can be velocity interpolated off of
- // this simulator. Clamp it to the region the agent is
- // in, a little bit in on each side.
- const F32 INSET = 0.5f; //meters
- const F32 REGION_WIDTH = LLWorld::getInstance()->getRegionWidthInMeters();
-
- LLVector3 agent_pos = getPositionAgent();
- LLVector3 agent_look_at = mFrameAgent.getAtAxis();
-
- if (mAvatarObject.notNull())
- {
- // the z height is at the agent's feet
- agent_pos.mV[VZ] -= 0.5f * mAvatarObject->mBodySize.mV[VZ];
- }
-
- agent_pos.mV[VX] = llclamp( agent_pos.mV[VX], INSET, REGION_WIDTH - INSET );
- agent_pos.mV[VY] = llclamp( agent_pos.mV[VY], INSET, REGION_WIDTH - INSET );
-
- // Don't let them go below ground, or too high.
- agent_pos.mV[VZ] = llclamp( agent_pos.mV[VZ],
- mRegionp->getLandHeightRegion( agent_pos ),
- LLWorld::getInstance()->getRegionMaxHeight() );
- // Send the CapReq
-
- LLSD body;
-
- std::string url = gAgent.getRegion()->getCapability("HomeLocation");
- std::ostringstream strBuffer;
- if( url.empty() )
- {
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_SetStartLocationRequest);
- msg->nextBlockFast( _PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, getID());
- msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
- msg->nextBlockFast( _PREHASH_StartLocationData);
- // corrected by sim
- msg->addStringFast(_PREHASH_SimName, "");
- msg->addU32Fast(_PREHASH_LocationID, location_id);
- msg->addVector3Fast(_PREHASH_LocationPos, agent_pos);
- msg->addVector3Fast(_PREHASH_LocationLookAt,mFrameAgent.getAtAxis());
-
- // Reliable only helps when setting home location. Last
- // location is sent on quit, and we don't have time to ack
- // the packets.
- msg->sendReliable(mRegionp->getHost());
-
- const U32 HOME_INDEX = 1;
- if( HOME_INDEX == location_id )
- {
- setHomePosRegion( mRegionp->getHandle(), getPositionAgent() );
- }
- }
- else
- {
- strBuffer << location_id;
- body["HomeLocation"]["LocationId"] = strBuffer.str();
-
- strBuffer.str("");
- strBuffer << agent_pos.mV[VX];
- body["HomeLocation"]["LocationPos"]["X"] = strBuffer.str();
-
- strBuffer.str("");
- strBuffer << agent_pos.mV[VY];
- body["HomeLocation"]["LocationPos"]["Y"] = strBuffer.str();
-
- strBuffer.str("");
- strBuffer << agent_pos.mV[VZ];
- body["HomeLocation"]["LocationPos"]["Z"] = strBuffer.str();
-
- strBuffer.str("");
- strBuffer << agent_look_at.mV[VX];
- body["HomeLocation"]["LocationLookAt"]["X"] = strBuffer.str();
-
- strBuffer.str("");
- strBuffer << agent_look_at.mV[VY];
- body["HomeLocation"]["LocationLookAt"]["Y"] = strBuffer.str();
-
- strBuffer.str("");
- strBuffer << agent_look_at.mV[VZ];
- body["HomeLocation"]["LocationLookAt"]["Z"] = strBuffer.str();
-
- LLHTTPClient::post( url, body, new LLHomeLocationResponder() );
- }
+ llinfos << "setStartPosition - Can't find agent viewerobject id " << gAgentID << llendl;
+ return;
+ }
+ // we've got the viewer object
+ // Sometimes the agent can be velocity interpolated off of
+ // this simulator. Clamp it to the region the agent is
+ // in, a little bit in on each side.
+ const F32 INSET = 0.5f; //meters
+ const F32 REGION_WIDTH = LLWorld::getInstance()->getRegionWidthInMeters();
+
+ LLVector3 agent_pos = getPositionAgent();
+
+ if (mAvatarObject.notNull())
+ {
+ // the z height is at the agent's feet
+ agent_pos.mV[VZ] -= 0.5f * mAvatarObject->mBodySize.mV[VZ];
}
- else
+
+ agent_pos.mV[VX] = llclamp( agent_pos.mV[VX], INSET, REGION_WIDTH - INSET );
+ agent_pos.mV[VY] = llclamp( agent_pos.mV[VY], INSET, REGION_WIDTH - INSET );
+
+ // Don't let them go below ground, or too high.
+ agent_pos.mV[VZ] = llclamp( agent_pos.mV[VZ],
+ mRegionp->getLandHeightRegion( agent_pos ),
+ LLWorld::getInstance()->getRegionMaxHeight() );
+ // Send the CapReq
+ LLSD request;
+ LLSD body;
+ LLSD homeLocation;
+
+ homeLocation["LocationId"] = LLSD::Integer(location_id);
+ homeLocation["LocationPos"] = ll_sdmap_from_vector3(agent_pos);
+ homeLocation["LocationLookAt"] = ll_sdmap_from_vector3(mFrameAgent.getAtAxis());
+
+ body["HomeLocation"] = homeLocation;
+
+ // This awkward idiom warrants explanation.
+ // For starters, LLSDMessage::ResponderAdapter is ONLY for testing the new
+ // LLSDMessage functionality with a pre-existing LLHTTPClient::Responder.
+ // In new code, define your reply/error methods on the same class as the
+ // sending method, bind them to local LLEventPump objects and pass those
+ // LLEventPump names in the request LLSD object.
+ // When testing old code, the new LLHomeLocationResponder object
+ // is referenced by an LLHTTPClient::ResponderPtr, so when the
+ // ResponderAdapter is deleted, the LLHomeLocationResponder will be too.
+ // We must trust that the underlying LLHTTPClient code will eventually
+ // fire either the reply callback or the error callback; either will cause
+ // the ResponderAdapter to delete itself.
+ LLSDMessage::ResponderAdapter*
+ adapter(new LLSDMessage::ResponderAdapter(new LLHomeLocationResponder()));
+
+ request["message"] = "HomeLocation";
+ request["payload"] = body;
+ request["reply"] = adapter->getReplyName();
+ request["error"] = adapter->getErrorName();
+
+ gAgent.getRegion()->getCapAPI().post(request);
+
+ const U32 HOME_INDEX = 1;
+ if( HOME_INDEX == location_id )
{
- llinfos << "setStartPosition - Can't find agent viewerobject id " << gAgentID << llendl;
+ setHomePosRegion( mRegionp->getHandle(), getPositionAgent() );
}
- }
}
+struct HomeLocationMapper: public LLCapabilityListener::CapabilityMapper
+{
+ // No reply message expected
+ HomeLocationMapper(): LLCapabilityListener::CapabilityMapper("HomeLocation") {}
+ virtual void buildMessage(LLMessageSystem* msg,
+ const LLUUID& agentID,
+ const LLUUID& sessionID,
+ const std::string& capabilityName,
+ const LLSD& payload) const
+ {
+ msg->newMessageFast(_PREHASH_SetStartLocationRequest);
+ msg->nextBlockFast( _PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, agentID);
+ msg->addUUIDFast(_PREHASH_SessionID, sessionID);
+ msg->nextBlockFast( _PREHASH_StartLocationData);
+ // corrected by sim
+ msg->addStringFast(_PREHASH_SimName, "");
+ msg->addU32Fast(_PREHASH_LocationID, payload["HomeLocation"]["LocationId"].asInteger());
+ msg->addVector3Fast(_PREHASH_LocationPos,
+ ll_vector3_from_sdmap(payload["HomeLocation"]["LocationPos"]));
+ msg->addVector3Fast(_PREHASH_LocationLookAt,
+ ll_vector3_from_sdmap(payload["HomeLocation"]["LocationLookAt"]));
+ }
+};
+// Need an instance of this class so it will self-register
+static HomeLocationMapper homeLocationMapper;
+
void LLAgent::requestStopMotion( LLMotion* motion )
{
// Notify all avatars that a motion has stopped.
@@ -5400,7 +5421,7 @@ void update_group_floaters(const LLUUID& group_id)
gIMMgr->refresh();
}
- gAgent.fireEvent(new LLEvent(&gAgent, "new group"), "");
+ gAgent.fireEvent(new LLOldEvents::LLEvent(&gAgent, "new group"), "");
}
// static
diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
index 226c78e631..27fcb07fd5 100644
--- a/indra/newview/llagent.h
+++ b/indra/newview/llagent.h
@@ -119,7 +119,7 @@ inline bool operator==(const LLGroupData &a, const LLGroupData &b)
//
-class LLAgent : public LLObservable
+class LLAgent : public LLOldEvents::LLObservable
{
LOG_CLASS(LLAgent);
@@ -176,7 +176,7 @@ public:
// Set the home data
void setRegion(LLViewerRegion *regionp);
LLViewerRegion *getRegion() const;
- const LLHost& getRegionHost() const;
+ LLHost getRegionHost() const;
std::string getSLURL() const;
void updateAgentPosition(const F32 dt, const F32 yaw, const S32 mouse_x, const S32 mouse_y); // call once per frame to update position, angles radians
diff --git a/indra/newview/llagentlanguage.h b/indra/newview/llagentlanguage.h
index 6bc7250c6e..596c584232 100644
--- a/indra/newview/llagentlanguage.h
+++ b/indra/newview/llagentlanguage.h
@@ -36,7 +36,7 @@
#include "llmemory.h" // LLSingleton<>
#include "llevent.h"
-class LLAgentLanguage: public LLSingleton<LLAgentLanguage>, public LLSimpleListener
+class LLAgentLanguage: public LLSingleton<LLAgentLanguage>, public LLOldEvents::LLSimpleListener
{
public:
LLAgentLanguage();
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 0c34d018a2..b66b514597 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -91,6 +91,7 @@
#include "lltexturecache.h"
#include "lltexturefetch.h"
#include "llimageworker.h"
+#include "llevents.h"
// The files below handle dependencies from cleanup.
#include "llkeyframemotion.h"
@@ -841,7 +842,14 @@ bool LLAppViewer::mainLoop()
LLTimer debugTime;
LLViewerJoystick* joystick(LLViewerJoystick::getInstance());
joystick->setNeedsReset(true);
-
+
+ LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
+ // As we do not (yet) send data on the mainloop LLEventPump that varies
+ // with each frame, no need to instantiate a new LLSD event object each
+ // time. Obviously, if that changes, just instantiate the LLSD at the
+ // point of posting.
+ LLSD newFrame;
+
// Handle messages
while (!LLApp::isExiting())
{
@@ -882,6 +890,9 @@ bool LLAppViewer::mainLoop()
LLFloaterMemLeak::getInstance()->idle() ;
}
+ // canonical per-frame event
+ mainloop.post(newFrame);
+
if (!LLApp::isExiting())
{
pingMainloopTimeout("Main:JoystickKeyboard");
diff --git a/indra/newview/llcapabilitylistener.cpp b/indra/newview/llcapabilitylistener.cpp
new file mode 100644
index 0000000000..3277da8930
--- /dev/null
+++ b/indra/newview/llcapabilitylistener.cpp
@@ -0,0 +1,183 @@
+/**
+ * @file llcapabilitylistener.cpp
+ * @author Nat Goodspeed
+ * @date 2009-01-07
+ * @brief Implementation for llcapabilitylistener.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "llviewerprecompiledheaders.h"
+// associated header
+#include "llcapabilitylistener.h"
+// STL headers
+#include <map>
+// std headers
+// external library headers
+#include <boost/bind.hpp>
+// other Linden headers
+#include "stringize.h"
+#include "llcapabilityprovider.h"
+
+class LLCapabilityListener::CapabilityMappers: public LLSingleton<LLCapabilityListener::CapabilityMappers>
+{
+public:
+ void registerMapper(const LLCapabilityListener::CapabilityMapper*);
+ void unregisterMapper(const LLCapabilityListener::CapabilityMapper*);
+ const LLCapabilityListener::CapabilityMapper* find(const std::string& cap) const;
+
+ struct DupCapMapper: public std::runtime_error
+ {
+ DupCapMapper(const std::string& what):
+ std::runtime_error(std::string("DupCapMapper: ") + what)
+ {}
+ };
+
+private:
+ friend class LLSingleton<LLCapabilityListener::CapabilityMappers>;
+ CapabilityMappers();
+
+ typedef std::map<std::string, const LLCapabilityListener::CapabilityMapper*> CapabilityMap;
+ CapabilityMap mMap;
+};
+
+LLCapabilityListener::LLCapabilityListener(const std::string& name,
+ LLMessageSystem* messageSystem,
+ const LLCapabilityProvider& provider,
+ const LLUUID& agentID,
+ const LLUUID& sessionID):
+ mEventPump(name),
+ mMessageSystem(messageSystem),
+ mProvider(provider),
+ mAgentID(agentID),
+ mSessionID(sessionID)
+{
+ mEventPump.listen("self", boost::bind(&LLCapabilityListener::capListener, this, _1));
+}
+
+bool LLCapabilityListener::capListener(const LLSD& request)
+{
+ // Extract what we want from the request object. We do it all up front
+ // partly to document what we expect.
+ LLSD::String cap(request["message"]);
+ LLSD payload(request["payload"]);
+ LLSD::String reply(request["reply"]);
+ LLSD::String error(request["error"]);
+ LLSD::Real timeout(request["timeout"]);
+ // If the LLSD doesn't even have a "message" key, we doubt it was intended
+ // for this listener.
+ if (cap.empty())
+ {
+ LL_ERRS("capListener") << "capability request event without 'message' key to '"
+ << getCapAPI().getName()
+ << "' on region\n" << mProvider.getDescription()
+ << LL_ENDL;
+ return false; // in case fatal-error function isn't
+ }
+ // Establish default timeout. This test relies on LLSD::asReal() returning
+ // exactly 0.0 for an undef value.
+ if (! timeout)
+ {
+ timeout = HTTP_REQUEST_EXPIRY_SECS;
+ }
+ // Look up the url for the requested capability name.
+ std::string url = mProvider.getCapability(cap);
+ if (! url.empty())
+ {
+ // This capability is supported by the region to which we're talking.
+ LLHTTPClient::post(url, payload,
+ new LLSDMessage::EventResponder(LLEventPumps::instance(),
+ mProvider.getDescription(),
+ cap, reply, error),
+ LLSD(), // headers
+ timeout);
+ }
+ else
+ {
+ // Capability not supported -- do we have a registered mapper?
+ const CapabilityMapper* mapper = CapabilityMappers::instance().find(cap);
+ if (! mapper) // capability neither supported nor mapped
+ {
+ LL_ERRS("capListener") << "unsupported capability '" << cap << "' request to '"
+ << getCapAPI().getName() << "' on region\n"
+ << mProvider.getDescription()
+ << LL_ENDL;
+ }
+ else if (! mapper->getReplyName().empty()) // mapper expects reply support
+ {
+ LL_ERRS("capListener") << "Mapper for capability '" << cap
+ << "' requires unimplemented support for reply message '"
+ << mapper->getReplyName()
+ << "' on '" << getCapAPI().getName() << "' on region\n"
+ << mProvider.getDescription()
+ << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS("capListener") << "fallback invoked for capability '" << cap
+ << "' request to '" << getCapAPI().getName()
+ << "' on region\n" << mProvider.getDescription()
+ << LL_ENDL;
+ mapper->buildMessage(mMessageSystem, mAgentID, mSessionID, cap, payload);
+ mMessageSystem->sendReliable(mProvider.getHost());
+ }
+ }
+ return false;
+}
+
+LLCapabilityListener::CapabilityMapper::CapabilityMapper(const std::string& cap, const std::string& reply):
+ mCapName(cap),
+ mReplyName(reply)
+{
+ LLCapabilityListener::CapabilityMappers::instance().registerMapper(this);
+}
+
+LLCapabilityListener::CapabilityMapper::~CapabilityMapper()
+{
+ LLCapabilityListener::CapabilityMappers::instance().unregisterMapper(this);
+}
+
+LLSD LLCapabilityListener::CapabilityMapper::readResponse(LLMessageSystem* messageSystem) const
+{
+ return LLSD();
+}
+
+LLCapabilityListener::CapabilityMappers::CapabilityMappers() {}
+
+void LLCapabilityListener::CapabilityMappers::registerMapper(const LLCapabilityListener::CapabilityMapper* mapper)
+{
+ // Try to insert a new map entry by which we can look up the passed mapper
+ // instance.
+ std::pair<CapabilityMap::iterator, bool> inserted =
+ mMap.insert(CapabilityMap::value_type(mapper->getCapName(), mapper));
+ // If we already have a mapper for that name, insert() merely located the
+ // existing iterator and returned false. It is a coding error to try to
+ // register more than one mapper for the same capability name.
+ if (! inserted.second)
+ {
+ throw DupCapMapper(std::string("Duplicate capability name ") + mapper->getCapName());
+ }
+}
+
+void LLCapabilityListener::CapabilityMappers::unregisterMapper(const LLCapabilityListener::CapabilityMapper* mapper)
+{
+ CapabilityMap::iterator found = mMap.find(mapper->getCapName());
+ if (found != mMap.end())
+ {
+ mMap.erase(found);
+ }
+}
+
+const LLCapabilityListener::CapabilityMapper*
+LLCapabilityListener::CapabilityMappers::find(const std::string& cap) const
+{
+ CapabilityMap::const_iterator found = mMap.find(cap);
+ if (found != mMap.end())
+ {
+ return found->second;
+ }
+ return NULL;
+}
diff --git a/indra/newview/llcapabilitylistener.h b/indra/newview/llcapabilitylistener.h
new file mode 100644
index 0000000000..061227e04d
--- /dev/null
+++ b/indra/newview/llcapabilitylistener.h
@@ -0,0 +1,113 @@
+/**
+ * @file llcapabilitylistener.h
+ * @author Nat Goodspeed
+ * @date 2009-01-07
+ * @brief Provide an event-based API for capability requests
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLCAPABILITYLISTENER_H)
+#define LL_LLCAPABILITYLISTENER_H
+
+#include "llevents.h" // LLEventPump
+#include "llsdmessage.h" // LLSDMessage::ArgError
+#include "llerror.h" // LOG_CLASS()
+
+class LLCapabilityProvider;
+class LLSD;
+
+class LLCapabilityListener
+{
+ LOG_CLASS(LLCapabilityListener);
+public:
+ LLCapabilityListener(const std::string& name, LLMessageSystem* messageSystem,
+ const LLCapabilityProvider& provider,
+ const LLUUID& agentID, const LLUUID& sessionID);
+
+ /// Capability-request exception
+ typedef LLSDMessage::ArgError ArgError;
+ /// Get LLEventPump on which we listen for capability requests
+ /// (https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes#Capabilities)
+ LLEventPump& getCapAPI() { return mEventPump; }
+
+ /**
+ * Base class for mapping an as-yet-undeployed capability name to a (pair
+ * of) LLMessageSystem message(s). To map a capability name to such
+ * messages, derive a subclass of CapabilityMapper and declare a static
+ * instance in a translation unit known to be loaded. The mapping is not
+ * region-specific. If an LLViewerRegion's capListener() receives a
+ * request for a supported capability, it will use the capability's URL.
+ * If not, it will look for an applicable CapabilityMapper subclass
+ * instance.
+ */
+ class CapabilityMapper
+ {
+ public:
+ /**
+ * Base-class constructor. Typically your subclass constructor will
+ * pass these parameters as literals.
+ * @param cap the capability name handled by this (subclass) instance
+ * @param reply the name of the response LLMessageSystem message. Omit
+ * if the LLMessageSystem message you intend to send doesn't prompt a
+ * reply message, or if you already handle that message in some other
+ * way.
+ */
+ CapabilityMapper(const std::string& cap, const std::string& reply = "");
+ virtual ~CapabilityMapper();
+ /// query the capability name
+ std::string getCapName() const { return mCapName; }
+ /// query the reply message name
+ std::string getReplyName() const { return mReplyName; }
+ /**
+ * Override this method to build the LLMessageSystem message we should
+ * send instead of the requested capability message. DO NOT send that
+ * message: that will be handled by the caller.
+ */
+ virtual void buildMessage(LLMessageSystem* messageSystem,
+ const LLUUID& agentID,
+ const LLUUID& sessionID,
+ const std::string& capabilityName,
+ const LLSD& payload) const = 0;
+ /**
+ * Override this method if you pass a non-empty @a reply
+ * LLMessageSystem message name to the constructor: that is, if you
+ * expect to receive an LLMessageSystem message in response to the
+ * message you constructed in buildMessage(). If you don't pass a @a
+ * reply message name, you need not override this method as it won't
+ * be called.
+ *
+ * Using LLMessageSystem message-reading operations, your
+ * readResponse() override should construct and return an LLSD object
+ * of the form you expect to receive from the real implementation of
+ * the capability you intend to invoke, when it finally goes live.
+ */
+ virtual LLSD readResponse(LLMessageSystem* messageSystem) const;
+
+ private:
+ const std::string mCapName;
+ const std::string mReplyName;
+ };
+
+private:
+ /// Bind the LLCapabilityProvider passed to our ctor
+ const LLCapabilityProvider& mProvider;
+
+ /// Post an event to this LLEventPump to invoke a capability message on
+ /// the bound LLCapabilityProvider's server
+ /// (https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes#Capabilities)
+ LLEventStream mEventPump;
+
+ LLMessageSystem* mMessageSystem;
+ LLUUID mAgentID, mSessionID;
+
+ /// listener to process capability requests
+ bool capListener(const LLSD&);
+
+ /// helper class for capListener()
+ class CapabilityMappers;
+};
+
+#endif /* ! defined(LL_LLCAPABILITYLISTENER_H) */
diff --git a/indra/newview/llcapabilityprovider.h b/indra/newview/llcapabilityprovider.h
new file mode 100644
index 0000000000..0ddb2b6cb9
--- /dev/null
+++ b/indra/newview/llcapabilityprovider.h
@@ -0,0 +1,39 @@
+/**
+ * @file llcapabilityprovider.h
+ * @author Nat Goodspeed
+ * @date 2009-01-07
+ * @brief Interface by which to reference (e.g.) LLViewerRegion to obtain a
+ * capability.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLCAPABILITYPROVIDER_H)
+#define LL_LLCAPABILITYPROVIDER_H
+
+#include "llhost.h"
+#include <string>
+
+/// Interface for obtaining a capability URL, given a capability name
+class LLCapabilityProvider
+{
+public:
+ virtual ~LLCapabilityProvider() {}
+ /**
+ * Get a capability URL, given a capability name. Returns empty string if
+ * no such capability is defined on this provider.
+ */
+ virtual std::string getCapability(const std::string& name) const = 0;
+ /**
+ * Get host to which to send that capability request.
+ */
+ virtual LLHost getHost() const = 0;
+ /**
+ * Describe this LLCapabilityProvider for logging etc.
+ */
+ virtual std::string getDescription() const = 0;
+};
+
+#endif /* ! defined(LL_LLCAPABILITYPROVIDER_H) */
diff --git a/indra/newview/llfloatergroups.cpp b/indra/newview/llfloatergroups.cpp
index 8cd7297447..9e0b0d7230 100644
--- a/indra/newview/llfloatergroups.cpp
+++ b/indra/newview/llfloatergroups.cpp
@@ -57,6 +57,8 @@
#include "llviewerwindow.h"
#include "llimview.h"
+using namespace LLOldEvents;
+
// static
std::map<const LLUUID, LLFloaterGroupPicker*> LLFloaterGroupPicker::sInstances;
diff --git a/indra/newview/llfloatergroups.h b/indra/newview/llfloatergroups.h
index da1c4e23dd..3eb3e5af52 100644
--- a/indra/newview/llfloatergroups.h
+++ b/indra/newview/llfloatergroups.h
@@ -84,14 +84,14 @@ protected:
static instance_map_t sInstances;
};
-class LLPanelGroups : public LLPanel, public LLSimpleListener
+class LLPanelGroups : public LLPanel, public LLOldEvents::LLSimpleListener
{
public:
LLPanelGroups();
virtual ~LLPanelGroups();
//LLEventListener
- /*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
+ /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
// clear the group list, and get a fresh set of info.
void reset();
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index e4459e38bd..5d09d8748f 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -87,6 +87,8 @@
#include "llselectmgr.h"
#include "llfloateropenobject.h"
+using namespace LLOldEvents;
+
// Helpers
// bug in busy count inc/dec right now, logic is complex... do we really need it?
void inc_busy_count()
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
index 2eee54df40..23d32fee81 100644
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -68,6 +68,8 @@
#include "llglheaders.h"
+using namespace LLOldEvents;
+
const F32 MAP_SCALE_MIN = 64;
const F32 MAP_SCALE_MID = 172;
const F32 MAP_SCALE_MAX = 512;
diff --git a/indra/newview/llnetmap.h b/indra/newview/llnetmap.h
index a5bf9f0fb5..9fbd5097fd 100644
--- a/indra/newview/llnetmap.h
+++ b/indra/newview/llnetmap.h
@@ -109,31 +109,31 @@ private:
class LLScaleMap : public LLMemberListener<LLNetMap>
{
public:
- /*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
+ /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
};
class LLStopTracking : public LLMemberListener<LLNetMap>
{
public:
- /*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
+ /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
};
class LLEnableTracking : public LLMemberListener<LLNetMap>
{
public:
- /*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
+ /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
};
class LLShowAgentProfile : public LLMemberListener<LLNetMap>
{
public:
- /*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
+ /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
};
class LLEnableProfile : public LLMemberListener<LLNetMap>
{
public:
- /*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
+ /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
};
};
diff --git a/indra/newview/llviewercontrol.h b/indra/newview/llviewercontrol.h
index d0dc80cb9a..c4003111d3 100644
--- a/indra/newview/llviewercontrol.h
+++ b/indra/newview/llviewercontrol.h
@@ -84,7 +84,7 @@ class LLCachedControl
{
T mCachedValue;
LLPointer<LLControlVariable> mControl;
- boost::signals::connection mConnection;
+ boost::signals2::scoped_connection mConnection;
public:
LLCachedControl(const std::string& name,
@@ -109,17 +109,13 @@ public:
}
// Add a listener to the controls signal...
- mControl->getSignal()->connect(
+ mConnection = mControl->getSignal()->connect(
boost::bind(&LLCachedControl<T>::handleValueChange, this, _1)
);
}
~LLCachedControl()
{
- if(mConnection.connected())
- {
- mConnection.disconnect();
- }
}
LLCachedControl& operator =(const T& newvalue)
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index 7f1f0fe6d0..f47d0777b0 100644
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -764,56 +764,39 @@ void move_inventory_item(
gAgent.sendReliableMessage();
}
-class LLCopyInventoryFromNotecardResponder : public LLHTTPClient::Responder
-{
-public:
- //If we get back a normal response, handle it here
- virtual void result(const LLSD& content)
- {
- // What do we do here?
- llinfos << "CopyInventoryFromNotecard request successful." << llendl;
- }
-
- //If we get back an error (not found, etc...), handle it here
- virtual void error(U32 status, const std::string& reason)
- {
- llinfos << "LLCopyInventoryFromNotecardResponder::error "
- << status << ": " << reason << llendl;
- }
-};
-
void copy_inventory_from_notecard(const LLUUID& object_id, const LLUUID& notecard_inv_id, const LLInventoryItem *src, U32 callback_id)
{
- LLSD body;
LLViewerRegion* viewer_region = NULL;
- if(object_id.notNull())
- {
- LLViewerObject* vo = gObjectList.findObject(object_id);
- if(vo)
- {
- viewer_region = vo->getRegion();
- }
+ LLViewerObject* vo = NULL;
+ if (object_id.notNull() && (vo = gObjectList.findObject(object_id)) != NULL)
+ {
+ viewer_region = vo->getRegion();
}
// Fallback to the agents region if for some reason the
// object isn't found in the viewer.
- if(!viewer_region)
+ if (! viewer_region)
{
viewer_region = gAgent.getRegion();
}
- if(viewer_region)
+ if (! viewer_region)
{
- std::string url = viewer_region->getCapability("CopyInventoryFromNotecard");
- if (!url.empty())
- {
- body["notecard-id"] = notecard_inv_id;
- body["object-id"] = object_id;
- body["item-id"] = src->getUUID();
- body["folder-id"] = gInventory.findCategoryUUIDForType(src->getType());
- body["callback-id"] = (LLSD::Integer)callback_id;
-
- LLHTTPClient::post(url, body, new LLCopyInventoryFromNotecardResponder());
- }
- }
+ LL_WARNS("copy_inventory_from_notecard") << "Can't find region from object_id "
+ << object_id << " or gAgent"
+ << LL_ENDL;
+ return;
+ }
+
+ LLSD request, body;
+ body["notecard-id"] = notecard_inv_id;
+ body["object-id"] = object_id;
+ body["item-id"] = src->getUUID();
+ body["folder-id"] = gInventory.findCategoryUUIDForType(src->getType());
+ body["callback-id"] = (LLSD::Integer)callback_id;
+
+ request["message"] = "CopyInventoryFromNotecard";
+ request["payload"] = body;
+
+ viewer_region->getCapAPI().post(request);
}
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 70fc78d277..82f8359732 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -46,7 +46,7 @@
#include "lluuid.h"
#include <boost/bind.hpp> // for SkinFolder listener
-#include <boost/signal.hpp>
+#include <boost/signals2.hpp>
// Implementation functions not exported into header file
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index fe91da05fa..ac36cf7bb6 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -1,4 +1,3 @@
-
/**
* @file llviewermenu.cpp
* @brief Builds menus out of items.
@@ -217,6 +216,8 @@
#include "lltexlayer.h"
using namespace LLVOAvatarDefines;
+using namespace LLOldEvents;
+
void init_client_menu(LLMenuGL* menu);
void init_server_menu(LLMenuGL* menu);
diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index 5240fd3211..d1f50212db 100644
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -71,6 +71,8 @@
// system libraries
#include <boost/tokenizer.hpp>
+using namespace LLOldEvents;
+
typedef LLMemberListener<LLView> view_listener_t;
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 12d9c1a992..d4ee7a7e50 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -65,6 +65,11 @@
#include "llvoclouds.h"
#include "llworld.h"
#include "llspatialpartition.h"
+#include "stringize.h"
+
+#ifdef LL_WINDOWS
+ #pragma warning(disable:4355)
+#endif
// Viewer object cache version, change if object update
// format changes. JC
@@ -172,7 +177,18 @@ LLViewerRegion::LLViewerRegion(const U64 &handle,
mCacheEntriesCount(0),
mCacheID(),
mEventPoll(NULL),
- mReleaseNotesRequested(FALSE)
+ mReleaseNotesRequested(FALSE),
+ // I'd prefer to set the LLCapabilityListener name to match the region
+ // name -- it's disappointing that's not available at construction time.
+ // We could instead store an LLCapabilityListener*, making
+ // setRegionNameAndZone() replace the instance. Would that pose
+ // consistency problems? Can we even request a capability before calling
+ // setRegionNameAndZone()?
+ // For testability -- the new Michael Feathers paradigm --
+ // LLCapabilityListener binds all the globals it expects to need at
+ // construction time.
+ mCapabilityListener(host.getString(), gMessageSystem, *this,
+ gAgent.getID(), gAgent.getSessionID())
{
mWidth = region_width_meters;
mOriginGlobal = from_region_handle(handle);
@@ -223,7 +239,6 @@ LLViewerRegion::LLViewerRegion(const U64 &handle,
mObjectPartition.push_back(new LLBridgePartition()); //PARTITION_BRIDGE
mObjectPartition.push_back(new LLHUDParticlePartition());//PARTITION_HUD_PARTICLE
mObjectPartition.push_back(NULL); //PARTITION_NONE
-
}
@@ -774,6 +789,15 @@ std::ostream& operator<<(std::ostream &s, const LLViewerRegion &region)
s << "{ ";
s << region.mHost;
s << " mOriginGlobal = " << region.getOriginGlobal()<< "\n";
+ std::string name(region.getName()), zone(region.getZoning());
+ if (! name.empty())
+ {
+ s << " mName = " << name << '\n';
+ }
+ if (! zone.empty())
+ {
+ s << " mZoning = " << zone << '\n';
+ }
s << "}";
return s;
}
@@ -1518,3 +1542,8 @@ void LLViewerRegion::showReleaseNotes()
LLWeb::loadURL(url);
mReleaseNotesRequested = FALSE;
}
+
+std::string LLViewerRegion::getDescription() const
+{
+ return stringize(*this);
+}
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index b0a98a99b8..4d345c811a 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -49,6 +49,8 @@
#include "lldatapacker.h"
#include "llvocache.h"
#include "llweb.h"
+#include "llcapabilityprovider.h"
+#include "llcapabilitylistener.h"
// Surface id's
#define LAND 1
@@ -66,8 +68,9 @@ class LLSurface;
class LLVOCache;
class LLVOCacheEntry;
class LLSpatialPartition;
+class LLEventPump;
-class LLViewerRegion
+class LLViewerRegion: public LLCapabilityProvider // implements this interface
{
public:
//MUST MATCH THE ORDER OF DECLARATION IN CONSTRUCTOR
@@ -226,11 +229,19 @@ public:
// Get/set named capability URLs for this region.
void setSeedCapability(const std::string& url);
void setCapability(const std::string& name, const std::string& url);
- std::string getCapability(const std::string& name) const;
+ // implements LLCapabilityProvider
+ virtual std::string getCapability(const std::string& name) const;
static bool isSpecialCapabilityName(const std::string &name);
void logActiveCapabilities() const;
- const LLHost &getHost() const { return mHost; }
+ /// Capability-request exception
+ typedef LLCapabilityListener::ArgError ArgError;
+ /// Get LLEventPump on which we listen for capability requests
+ /// (https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes#Capabilities)
+ LLEventPump& getCapAPI() { return mCapabilityListener.getCapAPI(); }
+
+ /// implements LLCapabilityProvider
+ virtual LLHost getHost() const { return mHost; }
const U64 &getHandle() const { return mHandle; }
LLSurface &getLand() const { return *mLandp; }
@@ -274,6 +285,8 @@ public:
void calculateCameraDistance();
friend std::ostream& operator<<(std::ostream &s, const LLViewerRegion &region);
+ /// implements LLCapabilityProvider
+ virtual std::string getDescription() const;
// used by LCD to get details for debug screen
U32 getNetDetailsForLCD();
@@ -394,6 +407,11 @@ private:
LLEventPoll* mEventPoll;
+ /// Post an event to this LLCapabilityListener to invoke a capability message on
+ /// this LLViewerRegion's server
+ /// (https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes#Capabilities)
+ LLCapabilityListener mCapabilityListener;
+
private:
bool mAlive; // can become false if circuit disconnects
@@ -461,5 +479,3 @@ inline BOOL LLViewerRegion::getReleaseNotesRequested() const
}
#endif
-
-
diff --git a/indra/newview/llviewerthrottle.cpp b/indra/newview/llviewerthrottle.cpp
index bf779c427a..73065c5c00 100644
--- a/indra/newview/llviewerthrottle.cpp
+++ b/indra/newview/llviewerthrottle.cpp
@@ -40,6 +40,8 @@
#include "llviewerstats.h"
#include "lldatapacker.h"
+using namespace LLOldEvents;
+
// consts
// The viewer is allowed to set the under-the-hood bandwidth to 50%
diff --git a/indra/newview/tests/llcapabilitylistener_test.cpp b/indra/newview/tests/llcapabilitylistener_test.cpp
new file mode 100644
index 0000000000..f66ae9404f
--- /dev/null
+++ b/indra/newview/tests/llcapabilitylistener_test.cpp
@@ -0,0 +1,274 @@
+/**
+ * @file llcapabilitylistener_test.cpp
+ * @author Nat Goodspeed
+ * @date 2008-12-31
+ * @brief Test for llcapabilitylistener.cpp.
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ * Copyright (c) 2008, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "../llviewerprecompiledheaders.h"
+// Own header
+#include "../llcapabilitylistener.h"
+// STL headers
+#include <stdexcept>
+#include <map>
+#include <vector>
+// std headers
+// external library headers
+#include "boost/bind.hpp"
+// other Linden headers
+#include "../test/lltut.h"
+#include "../llcapabilityprovider.h"
+#include "lluuid.h"
+#include "llerrorcontrol.h"
+#include "tests/networkio.h"
+#include "tests/commtest.h"
+#include "stringize.h"
+
+#if defined(LL_WINDOWS)
+#pragma warning(disable: 4355) // using 'this' in base-class ctor initializer expr
+#endif
+
+/*****************************************************************************
+* TestCapabilityProvider
+*****************************************************************************/
+struct TestCapabilityProvider: public LLCapabilityProvider
+{
+ TestCapabilityProvider(const LLHost& host):
+ mHost(host)
+ {}
+
+ std::string getCapability(const std::string& cap) const
+ {
+ CapMap::const_iterator found = mCaps.find(cap);
+ if (found != mCaps.end())
+ return found->second;
+ // normal LLViewerRegion lookup failure mode
+ return "";
+ }
+ void setCapability(const std::string& cap, const std::string& url)
+ {
+ mCaps[cap] = url;
+ }
+ LLHost getHost() const { return mHost; }
+ std::string getDescription() const { return "TestCapabilityProvider"; }
+
+ LLHost mHost;
+ typedef std::map<std::string, std::string> CapMap;
+ CapMap mCaps;
+};
+
+/*****************************************************************************
+* Dummy LLMessageSystem methods
+*****************************************************************************/
+/*==========================================================================*|
+// This doesn't work because we're already linking in llmessage.a, and we get
+// duplicate-symbol errors from the linker. Perhaps if I wanted to go through
+// the exercise of providing dummy versions of every single symbol defined in
+// message.o -- maybe some day.
+typedef std::vector< std::pair<std::string, std::string> > StringPairVector;
+StringPairVector call_history;
+
+S32 LLMessageSystem::sendReliable(const LLHost& host)
+{
+ call_history.push_back(StringPairVector::value_type("sendReliable", stringize(host)));
+ return 0;
+}
+|*==========================================================================*/
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct llcapears_data: public commtest_data
+ {
+ TestCapabilityProvider provider;
+ LLCapabilityListener regionListener;
+ LLEventPump& regionPump;
+
+ llcapears_data():
+ provider(host),
+ regionListener("testCapabilityListener", NULL, provider, LLUUID(), LLUUID()),
+ regionPump(regionListener.getCapAPI())
+ {
+ provider.setCapability("good", server + "capability-test");
+ provider.setCapability("fail", server + "fail");
+ }
+ };
+ typedef test_group<llcapears_data> llcapears_group;
+ typedef llcapears_group::object llcapears_object;
+ llcapears_group llsdmgr("llcapabilitylistener");
+
+ struct CaptureError: public LLError::OverrideFatalFunction
+ {
+ CaptureError():
+ LLError::OverrideFatalFunction(boost::bind(&CaptureError::operator(), this, _1))
+ {
+ LLError::setPrintLocation(false);
+ }
+
+ struct FatalException: public std::runtime_error
+ {
+ FatalException(const std::string& what): std::runtime_error(what) {}
+ };
+
+ void operator()(const std::string& message)
+ {
+ error = message;
+ throw FatalException(message);
+ }
+
+ std::string error;
+ };
+
+ template<> template<>
+ void llcapears_object::test<1>()
+ {
+ LLSD request, body;
+ body["data"] = "yes";
+ request["payload"] = body;
+ request["reply"] = replyPump.getName();
+ request["error"] = errorPump.getName();
+ std::string threw;
+ try
+ {
+ CaptureError capture;
+ regionPump.post(request);
+ }
+ catch (const CaptureError::FatalException& e)
+ {
+ threw = e.what();
+ }
+ ensure_contains("missing capability name", threw, "without 'message' key");
+ }
+
+ template<> template<>
+ void llcapears_object::test<2>()
+ {
+ LLSD request, body;
+ body["data"] = "yes";
+ request["message"] = "good";
+ request["payload"] = body;
+ request["reply"] = replyPump.getName();
+ request["error"] = errorPump.getName();
+ regionPump.post(request);
+ ensure("got response", netio.pump());
+ ensure("success response", success);
+ ensure_equals(result.asString(), "success");
+
+ body["status"] = 499;
+ body["reason"] = "custom error message";
+ request["message"] = "fail";
+ request["payload"] = body;
+ regionPump.post(request);
+ ensure("got response", netio.pump());
+ ensure("failure response", ! success);
+ ensure_equals(result["status"].asInteger(), body["status"].asInteger());
+ ensure_equals(result["reason"].asString(), body["reason"].asString());
+ }
+
+ template<> template<>
+ void llcapears_object::test<3>()
+ {
+ LLSD request, body;
+ body["data"] = "yes";
+ request["message"] = "unknown";
+ request["payload"] = body;
+ request["reply"] = replyPump.getName();
+ request["error"] = errorPump.getName();
+ std::string threw;
+ try
+ {
+ CaptureError capture;
+ regionPump.post(request);
+ }
+ catch (const CaptureError::FatalException& e)
+ {
+ threw = e.what();
+ }
+ ensure_contains("bad capability name", threw, "unsupported capability");
+ }
+
+ struct TestMapper: public LLCapabilityListener::CapabilityMapper
+ {
+ // Instantiator gets to specify whether mapper expects a reply.
+ // I'd really like to be able to test CapabilityMapper::buildMessage()
+ // functionality, too, but -- even though LLCapabilityListener accepts
+ // the LLMessageSystem* that it passes to CapabilityMapper --
+ // LLMessageSystem::sendReliable(const LLHost&) isn't virtual, so it's
+ // not helpful to pass a subclass instance. I suspect that making any
+ // LLMessageSystem methods virtual would provoke howls of outrage,
+ // given how heavily it's used. Nor can I just provide a local
+ // definition of LLMessageSystem::sendReliable(const LLHost&) because
+ // we're already linking in the rest of message.o via llmessage.a, and
+ // that produces duplicate-symbol link errors.
+ TestMapper(const std::string& replyMessage = std::string()):
+ LLCapabilityListener::CapabilityMapper("test", replyMessage)
+ {}
+ virtual void buildMessage(LLMessageSystem* msg,
+ const LLUUID& agentID,
+ const LLUUID& sessionID,
+ const std::string& capabilityName,
+ const LLSD& payload) const
+ {
+ msg->newMessageFast(_PREHASH_SetStartLocationRequest);
+ msg->nextBlockFast( _PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, agentID);
+ msg->addUUIDFast(_PREHASH_SessionID, sessionID);
+ msg->nextBlockFast( _PREHASH_StartLocationData);
+ // corrected by sim
+ msg->addStringFast(_PREHASH_SimName, "");
+ msg->addU32Fast(_PREHASH_LocationID, payload["HomeLocation"]["LocationId"].asInteger());
+/*==========================================================================*|
+ msg->addVector3Fast(_PREHASH_LocationPos,
+ ll_vector3_from_sdmap(payload["HomeLocation"]["LocationPos"]));
+ msg->addVector3Fast(_PREHASH_LocationLookAt,
+ ll_vector3_from_sdmap(payload["HomeLocation"]["LocationLookAt"]));
+|*==========================================================================*/
+ }
+ };
+
+ template<> template<>
+ void llcapears_object::test<4>()
+ {
+ TestMapper testMapper("WantReply");
+ LLSD request, body;
+ body["data"] = "yes";
+ request["message"] = "test";
+ request["payload"] = body;
+ request["reply"] = replyPump.getName();
+ request["error"] = errorPump.getName();
+ std::string threw;
+ try
+ {
+ CaptureError capture;
+ regionPump.post(request);
+ }
+ catch (const CaptureError::FatalException& e)
+ {
+ threw = e.what();
+ }
+ ensure_contains("capability mapper wants reply", threw, "unimplemented support for reply message");
+ }
+
+ template<> template<>
+ void llcapears_object::test<5>()
+ {
+ TestMapper testMapper;
+ std::string threw;
+ try
+ {
+ TestMapper testMapper2;
+ }
+ catch (const std::runtime_error& e)
+ {
+ threw = e.what();
+ }
+ ensure_contains("no dup cap mapper", threw, "DupCapMapper");
+ }
+}