/** * @file llviewermessage.cpp * @brief Dumping ground for viewer-side message system callbacks. * * $LicenseInfo:firstyear=2002&license=viewergpl$ * * Copyright (c) 2002-2009, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "llviewermessage.h" #include #include "audioengine.h" #include "indra_constants.h" #include "lscript_byteformat.h" #include "mean_collision_data.h" #include "llfloaterbump.h" #include "llassetstorage.h" #include "llcachename.h" #include "llchat.h" #include "lldbstrings.h" #include "lleconomy.h" #include "llfilepicker.h" #include "llfocusmgr.h" #include "llfollowcamparams.h" #include "llinstantmessage.h" #include "llquantize.h" #include "llregionflags.h" #include "llregionhandle.h" #include "llsdserialize.h" #include "llstring.h" #include "llteleportflags.h" #include "lltracker.h" #include "lltransactionflags.h" #include "llxfermanager.h" #include "message.h" #include "sound_ids.h" #include "lltimer.h" #include "llmd5.h" #include "llagent.h" #include "llcallingcard.h" #include "llconsole.h" #include "llvieweraudio.h" #include "llviewercontrol.h" #include "lldrawpool.h" #include "llfirstuse.h" #include "llfloateractivespeakers.h" #include "llfloateranimpreview.h" #include "llfloaterbuycurrency.h" #include "llfloaterbuyland.h" #include "llfloaterchat.h" #include "llfloatergroupinfo.h" #include "llfloaterimagepreview.h" #include "llfloaterland.h" #include "llfloaterregioninfo.h" #include "llfloaterlandholdings.h" #include "llurldispatcher.h" #include "llfloatermute.h" #include "llfloaterpostcard.h" #include "llfloaterpreference.h" #include "llfollowcam.h" #include "llgroupnotify.h" #include "llhudeffect.h" #include "llhudeffecttrail.h" #include "llhudmanager.h" #include "llimpanel.h" #include "llinventorymodel.h" #include "llinventoryview.h" #include "llmenugl.h" #include "llmutelist.h" #include "llnotifications.h" #include "llnotify.h" #include "llpanelgrouplandmoney.h" #include "llselectmgr.h" #include "llstartup.h" #include "llsky.h" #include "llstatenums.h" #include "llstatusbar.h" #include "llimview.h" #include "lltool.h" #include "lltoolbar.h" #include "lltoolmgr.h" #include "lltrans.h" #include "llui.h" // for make_ui_sound #include "lluploaddialog.h" #include "llviewercamera.h" #include "llviewergenericmessage.h" #include "llviewerinventory.h" #include "llviewermenu.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" #include "llviewerparcelmgr.h" #include "llviewerpartsource.h" #include "llviewerregion.h" #include "llviewerstats.h" #include "llviewertexteditor.h" #include "llviewerthrottle.h" #include "llviewerwindow.h" #include "llvlmanager.h" #include "llvoavatar.h" #include "llvotextbubble.h" #include "llweb.h" #include "llworld.h" #include "pipeline.h" #include "llappviewer.h" #include "llfloaterworldmap.h" #include "llviewerdisplay.h" #include "llkeythrottle.h" #include #if LL_WINDOWS // For Windows specific error handler #include "llwindebug.h" // For the invalid message handler #endif // // Constants // const F32 BIRD_AUDIBLE_RADIUS = 32.0f; const F32 SIT_DISTANCE_FROM_TARGET = 0.25f; static const F32 LOGOUT_REPLY_TIME = 3.f; // Wait this long after LogoutReply before quitting. // Determine how quickly residents' scripts can issue question dialogs // Allow bursts of up to 5 dialogs in 10 seconds. 10*2=20 seconds recovery if throttle kicks in static const U32 LLREQUEST_PERMISSION_THROTTLE_LIMIT = 5; // requests static const F32 LLREQUEST_PERMISSION_THROTTLE_INTERVAL = 10.0f; // seconds extern BOOL gDebugClicks; // function prototypes void open_offer(const std::vector& items, const std::string& from_name); bool check_offer_throttle(const std::string& from_name, bool check_only); void callbackCacheEstateOwnerName(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void*); //inventory offer throttle globals LLFrameTimer gThrottleTimer; const U32 OFFER_THROTTLE_MAX_COUNT=5; //number of items per time period const F32 OFFER_THROTTLE_TIME=10.f; //time period in seconds //script permissions const std::string SCRIPT_QUESTIONS[SCRIPT_PERMISSION_EOF] = { "ScriptTakeMoney", "ActOnControlInputs", "RemapControlInputs", "AnimateYourAvatar", "AttachToYourAvatar", "ReleaseOwnership", "LinkAndDelink", "AddAndRemoveJoints", "ChangePermissions", "TrackYourCamera", "ControlYourCamera" }; const BOOL SCRIPT_QUESTION_IS_CAUTION[SCRIPT_PERMISSION_EOF] = { TRUE, // ScriptTakeMoney, FALSE, // ActOnControlInputs FALSE, // RemapControlInputs FALSE, // AnimateYourAvatar FALSE, // AttachToYourAvatar FALSE, // ReleaseOwnership, FALSE, // LinkAndDelink, FALSE, // AddAndRemoveJoints FALSE, // ChangePermissions FALSE, // TrackYourCamera, FALSE // ControlYourCamera }; bool friendship_offer_callback(const LLSD& notification, const LLSD& response) { S32 option = LLNotification::getSelectedOption(notification, response); LLUUID fid; LLMessageSystem* msg = gMessageSystem; const LLSD& payload = notification["payload"]; switch(option) { case 0: // accept LLAvatarTracker::formFriendship(payload["from_id"]); fid = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD); // This will also trigger an onlinenotification if the user is online msg->newMessageFast(_PREHASH_AcceptFriendship); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_TransactionBlock); msg->addUUIDFast(_PREHASH_TransactionID, payload["session_id"]); msg->nextBlockFast(_PREHASH_FolderData); msg->addUUIDFast(_PREHASH_FolderID, fid); msg->sendReliable(LLHost(payload["sender"].asString())); break; case 1: // decline // We no longer notify other viewers, but we DO still send // the rejection to the simulator to delete the pending userop. msg->newMessageFast(_PREHASH_DeclineFriendship); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_TransactionBlock); msg->addUUIDFast(_PREHASH_TransactionID, payload["session_id"]); msg->sendReliable(LLHost(payload["sender"].asString())); break; default: // close button probably, possibly timed out break; } return false; } static LLNotificationFunctorRegistration friendship_offer_callback_reg("OfferFriendship", friendship_offer_callback); static LLNotificationFunctorRegistration friendship_offer_callback_reg_nm("OfferFriendshipNoMessage", friendship_offer_callback); //const char BUSY_AUTO_RESPONSE[] = "The Resident you messaged is in 'busy mode' which means they have " // "requested not to be disturbed. Your message will still be shown in their IM " // "panel for later viewing."; // // Functions // void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_group, S32 trx_type, const std::string& desc) { if(0 == amount || !region) return; amount = abs(amount); LL_INFOS("Messaging") << "give_money(" << uuid << "," << amount << ")"<< LL_ENDL; if(can_afford_transaction(amount)) { // gStatusBar->debitBalance(amount); LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_MoneyTransferRequest); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_MoneyData); msg->addUUIDFast(_PREHASH_SourceID, gAgent.getID() ); msg->addUUIDFast(_PREHASH_DestID, uuid); msg->addU8Fast(_PREHASH_Flags, pack_transaction_flags(FALSE, is_group)); msg->addS32Fast(_PREHASH_Amount, amount); msg->addU8Fast(_PREHASH_AggregatePermNextOwner, (U8)LLAggregatePermissions::AP_EMPTY); msg->addU8Fast(_PREHASH_AggregatePermInventory, (U8)LLAggregatePermissions::AP_EMPTY); msg->addS32Fast(_PREHASH_TransactionType, trx_type ); msg->addStringFast(_PREHASH_Description, desc); msg->sendReliable(region->getHost()); } else { LLFloaterBuyCurrency::buyCurrency("Giving", amount); } } void send_complete_agent_movement(const LLHost& sim_host) { LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_CompleteAgentMovement); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->addU32Fast(_PREHASH_CircuitCode, msg->mOurCircuitCode); msg->sendReliable(sim_host); } void process_logout_reply(LLMessageSystem* msg, void**) { // The server has told us it's ok to quit. LL_DEBUGS("Messaging") << "process_logout_reply" << LL_ENDL; LLUUID agent_id; msg->getUUID("AgentData", "AgentID", agent_id); LLUUID session_id; msg->getUUID("AgentData", "SessionID", session_id); if((agent_id != gAgent.getID()) || (session_id != gAgent.getSessionID())) { LL_WARNS("Messaging") << "Bogus Logout Reply" << LL_ENDL; } LLInventoryModel::update_map_t parents; S32 count = msg->getNumberOfBlocksFast( _PREHASH_InventoryData ); for(S32 i = 0; i < count; ++i) { LLUUID item_id; msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i); if( (1 == count) && item_id.isNull() ) { // Detect dummy item. Indicates an empty list. break; } // We do not need to track the asset ids, just account for an // updated inventory version. LL_INFOS("Messaging") << "process_logout_reply itemID=" << item_id << LL_ENDL; LLInventoryItem* item = gInventory.getItem( item_id ); if( item ) { parents[item->getParentUUID()] = 0; gInventory.addChangedMask(LLInventoryObserver::INTERNAL, item_id); } else { LL_INFOS("Messaging") << "process_logout_reply item not found: " << item_id << LL_ENDL; } } LLAppViewer::instance()->forceQuit(); } void process_layer_data(LLMessageSystem *mesgsys, void **user_data) { LLViewerRegion *regionp = LLWorld::getInstance()->getRegion(mesgsys->getSender()); if (!regionp || gNoRender) { return; } S32 size; S8 type; mesgsys->getS8Fast(_PREHASH_LayerID, _PREHASH_Type, type); size = mesgsys->getSizeFast(_PREHASH_LayerData, _PREHASH_Data); if (0 == size) { LL_WARNS("Messaging") << "Layer data has zero size." << LL_ENDL; return; } if (size < 0) { // getSizeFast() is probably trying to tell us about an error LL_WARNS("Messaging") << "getSizeFast() returned negative result: " << size << LL_ENDL; return; } U8 *datap = new U8[size]; mesgsys->getBinaryDataFast(_PREHASH_LayerData, _PREHASH_Data, datap, size); LLVLData *vl_datap = new LLVLData(regionp, type, datap, size); if (mesgsys->getReceiveCompressedSize()) { gVLManager.addLayerData(vl_datap, mesgsys->getReceiveCompressedSize()); } else { gVLManager.addLayerData(vl_datap, mesgsys->getReceiveSize()); } } // S32 exported_object_count = 0; // S32 exported_image_count = 0; // S32 current_object_count = 0; // S32 current_image_count = 0; // extern LLNotifyBox *gExporterNotify; // extern LLUUID gExporterRequestID; // extern std::string gExportDirectory; // extern LLUploadDialog *gExportDialog; // std::string gExportedFile; // std::map gImageChecksums; // void export_complete() // { // LLUploadDialog::modalUploadFinished(); // gExporterRequestID.setNull(); // gExportDirectory = ""; // LLFILE* fXML = LLFile::fopen(gExportedFile, "rb"); /* Flawfinder: ignore */ // fseek(fXML, 0, SEEK_END); // long length = ftell(fXML); // fseek(fXML, 0, SEEK_SET); // U8 *buffer = new U8[length + 1]; // size_t nread = fread(buffer, 1, length, fXML); // if (nread < (size_t) length) // { // LL_WARNS("Messaging") << "Short read" << LL_ENDL; // } // buffer[nread] = '\0'; // fclose(fXML); // char *pos = (char *)buffer; // while ((pos = strstr(pos+1, ""); // if (pos_uuid) // { // char image_uuid_str[UUID_STR_SIZE]; /* Flawfinder: ignore */ // memcpy(image_uuid_str, pos_uuid+2, UUID_STR_SIZE-1); /* Flawfinder: ignore */ // image_uuid_str[UUID_STR_SIZE-1] = 0; // LLUUID image_uuid(image_uuid_str); // LL_INFOS("Messaging") << "Found UUID: " << image_uuid << LL_ENDL; // std::map::iterator itor = gImageChecksums.find(image_uuid); // if (itor != gImageChecksums.end()) // { // LL_INFOS("Messaging") << "Replacing with checksum: " << itor->second << LL_ENDL; // if (!itor->second.empty()) // { // memcpy(&pos_check[10], itor->second.c_str(), 32); /* Flawfinder: ignore */ // } // } // } // } // } // LLFILE* fXMLOut = LLFile::fopen(gExportedFile, "wb"); /* Flawfinder: ignore */ // if (fwrite(buffer, 1, length, fXMLOut) != length) // { // LL_WARNS("Messaging") << "Short write" << LL_ENDL; // } // fclose(fXMLOut); // delete [] buffer; // } // void exported_item_complete(const LLTSCode status, void *user_data) // { // //std::string *filename = (std::string *)user_data; // if (status < LLTS_OK) // { // LL_WARNS("Messaging") << "Export failed!" << LL_ENDL; // } // else // { // ++current_object_count; // if (current_image_count == exported_image_count && current_object_count == exported_object_count) // { // LL_INFOS("Messaging") << "*** Export complete ***" << LL_ENDL; // export_complete(); // } // else // { // gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count)); // } // } // } // struct exported_image_info // { // LLUUID image_id; // std::string filename; // U32 image_num; // }; // void exported_j2c_complete(const LLTSCode status, void *user_data) // { // exported_image_info *info = (exported_image_info *)user_data; // LLUUID image_id = info->image_id; // U32 image_num = info->image_num; // std::string filename = info->filename; // delete info; // if (status < LLTS_OK) // { // LL_WARNS("Messaging") << "Image download failed!" << LL_ENDL; // } // else // { // LLFILE* fIn = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */ // if (fIn) // { // LLPointer ImageUtility = new LLImageJ2C; // LLPointer TargaUtility = new LLImageTGA; // fseek(fIn, 0, SEEK_END); // S32 length = ftell(fIn); // fseek(fIn, 0, SEEK_SET); // U8 *buffer = ImageUtility->allocateData(length); // if (fread(buffer, 1, length, fIn) != length) // { // LL_WARNS("Messaging") << "Short read" << LL_ENDL; // } // fclose(fIn); // LLFile::remove(filename); // // Convert to TGA // LLPointer image = new LLImageRaw(); // ImageUtility->updateData(); // ImageUtility->decode(image, 100000.0f); // TargaUtility->encode(image); // U8 *data = TargaUtility->getData(); // S32 data_size = TargaUtility->getDataSize(); // std::string file_path = gDirUtilp->getDirName(filename); // std::string output_file = llformat("%s/image-%03d.tga", file_path.c_str(), image_num);//filename; // //S32 name_len = output_file.length(); // //strcpy(&output_file[name_len-3], "tga"); // LLFILE* fOut = LLFile::fopen(output_file, "wb"); /* Flawfinder: ignore */ // char md5_hash_string[33]; /* Flawfinder: ignore */ // strcpy(md5_hash_string, "00000000000000000000000000000000"); /* Flawfinder: ignore */ // if (fOut) // { // if (fwrite(data, 1, data_size, fOut) != data_size) // { // LL_WARNS("Messaging") << "Short write" << LL_ENDL; // } // fseek(fOut, 0, SEEK_SET); // fclose(fOut); // fOut = LLFile::fopen(output_file, "rb"); /* Flawfinder: ignore */ // LLMD5 my_md5_hash(fOut); // my_md5_hash.hex_digest(md5_hash_string); // } // gImageChecksums.insert(std::pair(image_id, md5_hash_string)); // } // } // ++current_image_count; // if (current_image_count == exported_image_count && current_object_count == exported_object_count) // { // LL_INFOS("Messaging") << "*** Export textures complete ***" << LL_ENDL; // export_complete(); // } // else // { // gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count)); // } //} void process_derez_ack(LLMessageSystem*, void**) { if(gViewerWindow) gViewerWindow->getWindow()->decBusyCount(); } void process_places_reply(LLMessageSystem* msg, void** data) { LLUUID query_id; msg->getUUID("AgentData", "QueryID", query_id); if (query_id.isNull()) { LLFloaterLandHoldings::processPlacesReply(msg, data); } else if(gAgent.isInGroup(query_id)) { LLPanelGroupLandMoney::processPlacesReply(msg, data); } else { LL_WARNS("Messaging") << "Got invalid PlacesReply message" << LL_ENDL; } } void send_sound_trigger(const LLUUID& sound_id, F32 gain) { if (sound_id.isNull() || gAgent.getRegion() == NULL) { // disconnected agent or zero guids don't get sent (no sound) return; } LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_SoundTrigger); msg->nextBlockFast(_PREHASH_SoundData); msg->addUUIDFast(_PREHASH_SoundID, sound_id); // Client untrusted, ids set on sim msg->addUUIDFast(_PREHASH_OwnerID, LLUUID::null ); msg->addUUIDFast(_PREHASH_ObjectID, LLUUID::null ); msg->addUUIDFast(_PREHASH_ParentID, LLUUID::null ); msg->addU64Fast(_PREHASH_Handle, gAgent.getRegion()->getHandle()); LLVector3 position = gAgent.getPositionAgent(); msg->addVector3Fast(_PREHASH_Position, position); msg->addF32Fast(_PREHASH_Gain, gain); gAgent.sendMessage(); } bool join_group_response(const LLSD& notification, const LLSD& response) { S32 option = LLNotification::getSelectedOption(notification, response); BOOL delete_context_data = TRUE; bool accept_invite = false; LLUUID group_id = notification["payload"]["group_id"].asUUID(); LLUUID transaction_id = notification["payload"]["transaction_id"].asUUID(); std::string name = notification["payload"]["name"].asString(); std::string message = notification["payload"]["message"].asString(); S32 fee = notification["payload"]["fee"].asInteger(); if (option == 2 && !group_id.isNull()) { LLFloaterGroupInfo::showFromUUID(group_id); LLSD args; args["MESSAGE"] = message; LLNotifications::instance().add("JoinGroup", args, notification["payload"]); return false; } if(option == 0 && !group_id.isNull()) { // check for promotion or demotion. S32 max_groups = MAX_AGENT_GROUPS; if(gAgent.isInGroup(group_id)) ++max_groups; if(gAgent.mGroups.count() < max_groups) { accept_invite = true; } else { delete_context_data = FALSE; LLSD args; args["NAME"] = name; args["INVITE"] = message; LLNotifications::instance().add("JoinedTooManyGroupsMember", args, notification["payload"]); } } if (accept_invite) { // If there is a fee to join this group, make // sure the user is sure they want to join. if (fee > 0) { delete_context_data = FALSE; LLSD args; args["COST"] = llformat("%d", fee); // Set the fee for next time to 0, so that we don't keep // asking about a fee. LLSD next_payload = notification["payload"]; next_payload["fee"] = 0; LLNotifications::instance().add("JoinGroupCanAfford", args, next_payload); } else { send_improved_im(group_id, std::string("name"), std::string("message"), IM_ONLINE, IM_GROUP_INVITATION_ACCEPT, transaction_id); } } else { send_improved_im(group_id, std::string("name"), std::string("message"), IM_ONLINE, IM_GROUP_INVITATION_DECLINE, transaction_id); } return false; } static LLNotificationFunctorRegistration jgr_1("JoinGroup", join_group_response); static LLNotificationFunctorRegistration jgr_2("JoinedTooManyGroupsMember", join_group_response); static LLNotificationFunctorRegistration jgr_3("JoinGroupCanAfford", join_group_response); //----------------------------------------------------------------------------- // Instant Message //----------------------------------------------------------------------------- class LLOpenAgentOffer : public LLInventoryFetchObserver { public: LLOpenAgentOffer(const std::string& from_name) : mFromName(from_name) {} /*virtual*/ void done() { open_offer(mComplete, mFromName); gInventory.removeObserver(this); delete this; } private: std::string mFromName; }; //unlike the FetchObserver for AgentOffer, we only make one //instance of the AddedObserver for TaskOffers //and it never dies. We do this because we don't know the UUID of //task offers until they are accepted, so we don't wouldn't //know what to watch for, so instead we just watch for all additions. class LLOpenTaskOffer : public LLInventoryAddedObserver { protected: /*virtual*/ void done() { open_offer(mAdded, ""); mAdded.clear(); } }; //one global instance to bind them LLOpenTaskOffer* gNewInventoryObserver=NULL; void start_new_inventory_observer() { if (!gNewInventoryObserver) //task offer observer { // Observer is deleted by gInventory gNewInventoryObserver = new LLOpenTaskOffer; gInventory.addObserver(gNewInventoryObserver); } } class LLDiscardAgentOffer : public LLInventoryFetchComboObserver { public: LLDiscardAgentOffer(const LLUUID& folder_id, const LLUUID& object_id) : mFolderID(folder_id), mObjectID(object_id) {} virtual ~LLDiscardAgentOffer() {} virtual void done() { LL_DEBUGS("Messaging") << "LLDiscardAgentOffer::done()" << LL_ENDL; LLUUID trash_id; trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH); bool notify = false; if(trash_id.notNull() && mObjectID.notNull()) { LLInventoryModel::update_list_t update; LLInventoryModel::LLCategoryUpdate old_folder(mFolderID, -1); update.push_back(old_folder); LLInventoryModel::LLCategoryUpdate new_folder(trash_id, 1); update.push_back(new_folder); gInventory.accountForUpdate(update); gInventory.moveObject(mObjectID, trash_id); LLInventoryObject* obj = gInventory.getObject(mObjectID); if(obj) { // no need to restamp since this is already a freshly // stamped item. obj->updateParentOnServer(FALSE); notify = true; } } else { LL_WARNS("Messaging") << "DiscardAgentOffer unable to find: " << (trash_id.isNull() ? "trash " : "") << (mObjectID.isNull() ? "object" : "") << LL_ENDL; } gInventory.removeObserver(this); if(notify) { gInventory.notifyObservers(); } delete this; } protected: LLUUID mFolderID; LLUUID mObjectID; }; //Returns TRUE if we are OK, FALSE if we are throttled //Set check_only true if you want to know the throttle status //without registering a hit bool check_offer_throttle(const std::string& from_name, bool check_only) { static U32 throttle_count; static bool throttle_logged; LLChat chat; std::string log_message; if (!gSavedSettings.getBOOL("ShowNewInventory")) return false; if (check_only) { return gThrottleTimer.hasExpired(); } if(gThrottleTimer.checkExpirationAndReset(OFFER_THROTTLE_TIME)) { LL_DEBUGS("Messaging") << "Throttle Expired" << LL_ENDL; throttle_count=1; throttle_logged=false; return true; } else //has not expired { LL_DEBUGS("Messaging") << "Throttle Not Expired, Count: " << throttle_count << LL_ENDL; // When downloading the initial inventory we get a lot of new items // coming in and can't tell that from spam. if (LLStartUp::getStartupState() >= STATE_STARTED && throttle_count >= OFFER_THROTTLE_MAX_COUNT) { if (!throttle_logged) { // Use the name of the last item giver, who is probably the person // spamming you. std::ostringstream message; message << LLAppViewer::instance()->getSecondLifeTitle(); if (!from_name.empty()) { message << ": Items coming in too fast from " << from_name; } else { message << ": Items coming in too fast"; } message << ", automatic preview disabled for " << OFFER_THROTTLE_TIME << " seconds."; chat.mText = message.str(); //this is kinda important, so actually put it on screen LLFloaterChat::addChat(chat, FALSE, FALSE); throttle_logged=true; } return false; } else { throttle_count++; return true; } } } void open_offer(const std::vector& items, const std::string& from_name) { std::vector::const_iterator it = items.begin(); std::vector::const_iterator end = items.end(); LLUUID trash_id(gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH)); LLInventoryItem* item; for(; it != end; ++it) { item = gInventory.getItem(*it); if(!item) { LL_WARNS("Messaging") << "Unable to show inventory item: " << *it << LL_ENDL; continue; } if(gInventory.isObjectDescendentOf(*it, trash_id)) { continue; } LLAssetType::EType asset_type = item->getType(); //if we are throttled, don't display them if (check_offer_throttle(from_name, false)) { // I'm not sure this is a good idea. bool show_keep_discard = item->getPermissions().getCreator() != gAgent.getID(); //bool show_keep_discard = true; switch(asset_type) { case LLAssetType::AT_NOTECARD: open_notecard((LLViewerInventoryItem*)item, std::string("Note: ") + item->getName(), LLUUID::null, show_keep_discard, LLUUID::null, FALSE); break; case LLAssetType::AT_LANDMARK: open_landmark((LLViewerInventoryItem*)item, std::string("Landmark: ") + item->getName(), show_keep_discard, LLUUID::null, FALSE); break; case LLAssetType::AT_TEXTURE: open_texture(*it, std::string("Texture: ") + item->getName(), show_keep_discard, LLUUID::null, FALSE); break; default: break; } } //highlight item, if it's not in the trash or lost+found // Don't auto-open the inventory floater LLInventoryView* view = LLInventoryView::getActiveInventory(); if(!view) { return; } if(gSavedSettings.getBOOL("ShowInInventory") && asset_type != LLAssetType::AT_CALLINGCARD && item->getInventoryType() != LLInventoryType::IT_ATTACHMENT && !from_name.empty()) { LLInventoryView::showAgentInventory(TRUE); } //Trash Check LLUUID trash_id; trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH); if(gInventory.isObjectDescendentOf(item->getUUID(), trash_id)) { return; } LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND); //BOOL inventory_has_focus = gFocusMgr.childHasKeyboardFocus(view); BOOL user_is_away = gAwayTimer.getStarted(); // don't select lost and found items if the user is active if (gInventory.isObjectDescendentOf(item->getUUID(), lost_and_found_id) && !user_is_away) { return; } //Not sure about this check. Could make it easy to miss incoming items. //don't dick with highlight while the user is working //if(inventory_has_focus && !user_is_away) // break; LL_DEBUGS("Messaging") << "Highlighting" << item->getUUID() << LL_ENDL; //highlight item LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus(); view->getPanel()->setSelection(item->getUUID(), TAKE_FOCUS_NO); gFocusMgr.setKeyboardFocus(focus_ctrl); } } void inventory_offer_mute_callback(const LLUUID& blocked_id, const std::string& first_name, const std::string& last_name, BOOL is_group, void* user_data) { std::string from_name; LLMute::EType type; if (is_group) { type = LLMute::GROUP; from_name = first_name; } else { type = LLMute::AGENT; from_name = first_name + " " + last_name; } LLMute mute(blocked_id, from_name, type); if (LLMuteList::getInstance()->add(mute)) { LLFloaterMute::showInstance(); LLFloaterMute::getInstance()->selectMute(blocked_id); } // purge the message queue of any previously queued inventory offers from the same source. class OfferMatcher : public LLNotifyBoxView::Matcher { public: OfferMatcher(const LLUUID& to_block) : blocked_id(to_block) {} BOOL matches(const LLNotificationPtr notification) const { if(notification->getName() == "ObjectGiveItem" || notification->getName() == "ObjectGiveItemUnknownUser" || notification->getName() == "UserGiveItem") { return (notification->getPayload()["from_id"].asUUID() == blocked_id); } return FALSE; } private: const LLUUID& blocked_id; }; gNotifyBoxView->purgeMessagesMatching(OfferMatcher(blocked_id)); } LLOfferInfo::LLOfferInfo(const LLSD& sd) { mIM = (EInstantMessage)sd["im_type"].asInteger(); mFromID = sd["from_id"].asUUID(); mFromGroup = sd["from_group"].asBoolean(); mFromObject = sd["from_object"].asBoolean(); mTransactionID = sd["transaction_id"].asUUID(); mFolderID = sd["folder_id"].asUUID(); mObjectID = sd["object_id"].asUUID(); mType = LLAssetType::lookup(sd["type"].asString().c_str()); mFromName = sd["from_name"].asString(); mDesc = sd["description"].asString(); mHost = LLHost(sd["sender"].asString()); } LLSD LLOfferInfo::asLLSD() { LLSD sd; sd["im_type"] = mIM; sd["from_id"] = mFromID; sd["from_group"] = mFromGroup; sd["from_object"] = mFromObject; sd["transaction_id"] = mTransactionID; sd["folder_id"] = mFolderID; sd["object_id"] = mObjectID; sd["type"] = LLAssetType::lookup(mType); sd["from_name"] = mFromName; sd["description"] = mDesc; sd["sender"] = mHost.getIPandPort(); return sd; } bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& response) { LLChat chat; std::string log_message; S32 button = LLNotification::getSelectedOption(notification, response); // For muting, we need to add the mute, then decline the offer. // This must be done here because: // * callback may be called immediately, // * adding the mute sends a message, // * we can't build two messages at once. if (2 == button) { gCacheName->getNameFromUUID(mFromID, mFromGroup, inventory_offer_mute_callback, this); } LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_ImprovedInstantMessage); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_MessageBlock); msg->addBOOLFast(_PREHASH_FromGroup, FALSE); msg->addUUIDFast(_PREHASH_ToAgentID, mFromID); msg->addU8Fast(_PREHASH_Offline, IM_ONLINE); msg->addUUIDFast(_PREHASH_ID, mTransactionID); msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary std::string name; gAgent.buildFullname(name); msg->addStringFast(_PREHASH_FromAgentName, name); msg->addStringFast(_PREHASH_Message, ""); msg->addU32Fast(_PREHASH_ParentEstateID, 0); msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null); msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent()); LLInventoryObserver* opener = NULL; LLViewerInventoryCategory* catp = NULL; catp = (LLViewerInventoryCategory*)gInventory.getCategory(mObjectID); LLViewerInventoryItem* itemp = NULL; if(!catp) { itemp = (LLViewerInventoryItem*)gInventory.getItem(mObjectID); } // *TODO:translate std::string from_string; // Used in the pop-up. std::string chatHistory_string; // Used in chat history. if (mFromObject == TRUE) { if (mFromGroup) { std::string group_name; if (gCacheName->getGroupName(mFromID, group_name)) { from_string = std::string("An object named '") + mFromName + "' owned by the group '" + group_name + "'"; chatHistory_string = mFromName + " owned by the group '" + group_name + "'"; } else { from_string = std::string("An object named '") + mFromName + "' owned by an unknown group"; chatHistory_string = mFromName + " owned by an unknown group"; } } else { std::string first_name, last_name; if (gCacheName->getName(mFromID, first_name, last_name)) { from_string = std::string("An object named '") + mFromName + "' owned by " + first_name + " " + last_name; chatHistory_string = mFromName + " owned by " + first_name + " " + last_name; } else { from_string = std::string("An object named '") + mFromName + "' owned by an unknown user"; chatHistory_string = mFromName + " owned by an unknown user"; } } } else { from_string = chatHistory_string = mFromName; } bool busy=FALSE; switch(button) { case IOR_ACCEPT: // ACCEPT. The math for the dialog works, because the accept // for inventory_offered, task_inventory_offer or // group_notice_inventory is 1 greater than the offer integer value. // Generates IM_INVENTORY_ACCEPTED, IM_TASK_INVENTORY_ACCEPTED, // or IM_GROUP_NOTICE_INVENTORY_ACCEPTED msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 1)); msg->addBinaryDataFast(_PREHASH_BinaryBucket, &(mFolderID.mData), sizeof(mFolderID.mData)); // send the message msg->sendReliable(mHost); //don't spam them if they are getting flooded if (check_offer_throttle(mFromName, true)) { log_message = chatHistory_string + " gave you " + mDesc + "."; chat.mText = log_message; LLFloaterChat::addChatHistory(chat); } // we will want to open this item when it comes back. LL_DEBUGS("Messaging") << "Initializing an opener for tid: " << mTransactionID << LL_ENDL; switch (mIM) { case IM_INVENTORY_OFFERED: { // This is an offer from an agent. In this case, the back // end has already copied the items into your inventory, // so we can fetch it out of our inventory. LLInventoryFetchObserver::item_ref_t items; items.push_back(mObjectID); LLOpenAgentOffer* open_agent_offer = new LLOpenAgentOffer(from_string); open_agent_offer->fetchItems(items); if(catp || (itemp && itemp->isComplete())) { open_agent_offer->done(); } else { opener = open_agent_offer; } } break; case IM_TASK_INVENTORY_OFFERED: case IM_GROUP_NOTICE: case IM_GROUP_NOTICE_REQUESTED: { // This is an offer from a task or group. // We don't use a new instance of an opener // We instead use the singular observer gOpenTaskOffer // Since it already exists, we don't need to actually do anything } break; default: LL_WARNS("Messaging") << "inventory_offer_callback: unknown offer type" << LL_ENDL; break; } // end switch (mIM) break; case IOR_BUSY: //Busy falls through to decline. Says to make busy message. busy=TRUE; case IOR_MUTE: // MUTE falls through to decline case IOR_DECLINE: // DECLINE. The math for the dialog works, because the decline // for inventory_offered, task_inventory_offer or // group_notice_inventory is 2 greater than the offer integer value. // Generates IM_INVENTORY_DECLINED, IM_TASK_INVENTORY_DECLINED, // or IM_GROUP_NOTICE_INVENTORY_DECLINED default: // close button probably (or any of the fall-throughs from above) msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 2)); msg->addBinaryDataFast(_PREHASH_BinaryBucket, EMPTY_BINARY_BUCKET, EMPTY_BINARY_BUCKET_SIZE); // send the message msg->sendReliable(mHost); log_message = "You decline " + mDesc + " from " + mFromName + "."; chat.mText = log_message; if( LLMuteList::getInstance()->isMuted(mFromID ) && ! LLMuteList::getInstance()->isLinden(mFromName) ) // muting for SL-42269 { chat.mMuted = TRUE; } LLFloaterChat::addChatHistory(chat); // If it's from an agent, we have to fetch the item to throw // it away. If it's from a task or group, just denying the // request will suffice to discard the item. if(IM_INVENTORY_OFFERED == mIM) { LLInventoryFetchComboObserver::folder_ref_t folders; LLInventoryFetchComboObserver::item_ref_t items; items.push_back(mObjectID); LLDiscardAgentOffer* discard_agent_offer; discard_agent_offer = new LLDiscardAgentOffer(mFolderID, mObjectID); discard_agent_offer->fetch(folders, items); if(catp || (itemp && itemp->isComplete())) { discard_agent_offer->done(); } else { opener = discard_agent_offer; } } if (busy && (!mFromGroup && !mFromObject)) { busy_message(msg,mFromID); } break; } if(opener) { gInventory.addObserver(opener); } // Allow these to stack up, but once you deal with one, reset the // position. gFloaterView->resetStartingFloaterPosition(); delete this; return false; } void inventory_offer_handler(LLOfferInfo* info, BOOL from_task) { //Until throttling is implmented, busy mode should reject inventory instead of silently //accepting it. SEE SL-39554 if (gAgent.getBusy()) { info->forceResponse(IOR_BUSY); return; } //If muted, don't even go through the messaging stuff. Just curtail the offer here. if (LLMuteList::getInstance()->isMuted(info->mFromID, info->mFromName)) { info->forceResponse(IOR_MUTE); return; } // Avoid the Accept/Discard dialog if the user so desires. JC if (gSavedSettings.getBOOL("AutoAcceptNewInventory") && (info->mType == LLAssetType::AT_NOTECARD || info->mType == LLAssetType::AT_LANDMARK || info->mType == LLAssetType::AT_TEXTURE)) { // For certain types, just accept the items into the inventory, // and possibly open them on receipt depending upon "ShowNewInventory". info->forceResponse(IOR_ACCEPT); return; } // Strip any SLURL from the message display. (DEV-2754) std::string msg = info->mDesc; int indx = msg.find(" ( http://slurl.com/secondlife/"); if(indx >= 0) { LLStringUtil::truncate(msg, indx); } LLSD args; args["[OBJECTNAME]"] = msg; LLSD payload; // must protect against a NULL return from lookupHumanReadable() std::string typestr = ll_safe_string(LLAssetType::lookupHumanReadable(info->mType)); if (!typestr.empty()) { args["OBJECTTYPE"] = typestr; } else { LL_WARNS("Messaging") << "LLAssetType::lookupHumanReadable() returned NULL - probably bad asset type: " << info->mType << LL_ENDL; args["OBJECTTYPE"] = ""; // This seems safest, rather than propagating bogosity LL_WARNS("Messaging") << "Forcing an inventory-decline for probably-bad asset type." << LL_ENDL; info->forceResponse(IOR_DECLINE); return; } // Name cache callbacks don't store userdata, so can't save // off the LLOfferInfo. Argh. BOOL name_found = FALSE; if (info->mFromGroup) { std::string group_name; if (gCacheName->getGroupName(info->mFromID, group_name)) { args["FIRST"] = group_name; args["LAST"] = ""; name_found = TRUE; } } else { std::string first_name, last_name; if (gCacheName->getName(info->mFromID, first_name, last_name)) { args["FIRST"] = first_name; args["LAST"] = last_name; name_found = TRUE; } } payload["from_id"] = info->mFromID; args["OBJECTFROMNAME"] = info->mFromName; args["NAME"] = info->mFromName; LLNotification::Params p("ObjectGiveItem"); p.substitutions(args).payload(payload).functor(boost::bind(&LLOfferInfo::inventory_offer_callback, info, _1, _2)); if (from_task) { p.name = name_found ? "ObjectGiveItem" : "ObjectGiveItemUnknownUser"; } else { p.name = "UserGiveItem"; } LLNotifications::instance().add(p); } bool lure_callback(const LLSD& notification, const LLSD& response) { S32 option = 0; if (response.isInteger()) { option = response.asInteger(); } else { option = LLNotification::getSelectedOption(notification, response); } LLUUID from_id = notification["payload"]["from_id"].asUUID(); LLUUID lure_id = notification["payload"]["lure_id"].asUUID(); BOOL godlike = notification["payload"]["godlike"].asBoolean(); switch(option) { case 0: { // accept gAgent.teleportViaLure(lure_id, godlike); } break; case 1: default: // decline send_simple_im(from_id, LLStringUtil::null, IM_LURE_DECLINED, lure_id); break; } return false; } static LLNotificationFunctorRegistration lure_callback_reg("TeleportOffered", lure_callback); bool goto_url_callback(const LLSD& notification, const LLSD& response) { std::string url = notification["payload"]["url"].asString(); S32 option = LLNotification::getSelectedOption(notification, response); if(1 == option) { LLWeb::loadURL(url); } return false; } static LLNotificationFunctorRegistration goto_url_callback_reg("GotoURL", goto_url_callback); void process_improved_im(LLMessageSystem *msg, void **user_data) { if (gNoRender) { return; } LLUUID from_id; BOOL from_group; LLUUID to_id; U8 offline; U8 d = 0; LLUUID session_id; U32 timestamp; std::string name; std::string message; U32 parent_estate_id = 0; LLUUID region_id; LLVector3 position; U8 binary_bucket[MTUBYTES]; S32 binary_bucket_size; LLChat chat; std::string buffer; // *TODO:translate - need to fix the full name to first/last (maybe) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, from_id); msg->getBOOLFast(_PREHASH_MessageBlock, _PREHASH_FromGroup, from_group); msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ToAgentID, to_id); msg->getU8Fast( _PREHASH_MessageBlock, _PREHASH_Offline, offline); msg->getU8Fast( _PREHASH_MessageBlock, _PREHASH_Dialog, d); msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ID, session_id); msg->getU32Fast( _PREHASH_MessageBlock, _PREHASH_Timestamp, timestamp); //msg->getData("MessageBlock", "Count", &count); msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_FromAgentName, name); msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_Message, message); msg->getU32Fast(_PREHASH_MessageBlock, _PREHASH_ParentEstateID, parent_estate_id); msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_RegionID, region_id); msg->getVector3Fast(_PREHASH_MessageBlock, _PREHASH_Position, position); msg->getBinaryDataFast( _PREHASH_MessageBlock, _PREHASH_BinaryBucket, binary_bucket, 0, 0, MTUBYTES); binary_bucket_size = msg->getSizeFast(_PREHASH_MessageBlock, _PREHASH_BinaryBucket); EInstantMessage dialog = (EInstantMessage)d; BOOL is_busy = gAgent.getBusy(); BOOL is_muted = LLMuteList::getInstance()->isMuted(from_id, name, LLMute::flagTextChat); BOOL is_linden = LLMuteList::getInstance()->isLinden(name); BOOL is_owned_by_me = FALSE; chat.mMuted = is_muted && !is_linden; chat.mFromID = from_id; chat.mFromName = name; chat.mSourceType = (from_id.isNull() || (name == std::string(SYSTEM_FROM))) ? CHAT_SOURCE_SYSTEM : CHAT_SOURCE_AGENT; LLViewerObject *source = gObjectList.findObject(session_id); //Session ID is probably the wrong thing. if (source) { is_owned_by_me = source->permYouOwner(); } std::string separator_string(": "); int message_offset = 0; //Handle IRC styled /me messages. std::string prefix = message.substr(0, 4); if (prefix == "/me " || prefix == "/me'") { separator_string = ""; message_offset = 3; } LLSD args; switch(dialog) { case IM_CONSOLE_AND_CHAT_HISTORY: // These are used for system messages, hence don't need the name, // as it is always "Second Life". // *TODO:translate args["MESSAGE"] = message; // Note: don't put the message in the IM history, even though was sent // via the IM mechanism. LLNotifications::instance().add("SystemMessageTip",args); break; case IM_NOTHING_SPECIAL: // Don't show dialog, just do IM if (!gAgent.isGodlike() && gAgent.getRegion()->isPrelude() && to_id.isNull() ) { // do nothing -- don't distract newbies in // Prelude with global IMs } else if (offline == IM_ONLINE && !is_linden && is_busy && name != SYSTEM_FROM) { // return a standard "busy" message, but only do it to online IM // (i.e. not other auto responses and not store-and-forward IM) if (!gIMMgr->hasSession(session_id)) { // if there is not a panel for this conversation (i.e. it is a new IM conversation // initiated by the other party) then... std::string my_name; gAgent.buildFullname(my_name); std::string response = gSavedPerAccountSettings.getString("BusyModeResponse2"); pack_instant_message( gMessageSystem, gAgent.getID(), FALSE, gAgent.getSessionID(), from_id, my_name, response, IM_ONLINE, IM_BUSY_AUTO_RESPONSE, session_id); gAgent.sendReliableMessage(); } // now store incoming IM in chat history buffer = separator_string + message.substr(message_offset); LL_INFOS("Messaging") << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << LL_ENDL; // add to IM panel, but do not bother the user gIMMgr->addMessage( session_id, from_id, name, buffer, LLStringUtil::null, dialog, parent_estate_id, region_id, position, true); // pretend this is chat generated by self, so it does not show up on screen chat.mText = std::string("IM: ") + name + separator_string + message.substr(message_offset); LLFloaterChat::addChat( chat, TRUE, TRUE ); } else if (from_id.isNull()) { // Messages from "Second Life" ID don't go to IM history // messages which should be routed to IM window come from a user ID with name=SYSTEM_NAME chat.mText = name + ": " + message; LLFloaterChat::addChat(chat, FALSE, FALSE); } else if (to_id.isNull()) { // Message to everyone from GOD args["NAME"] = name; args["MESSAGE"] = message; LLNotifications::instance().add("GodMessage", args); // Treat like a system message and put in chat history. // Claim to be from a local agent so it doesn't go into // console. chat.mText = name + separator_string + message.substr(message_offset); BOOL local_agent = TRUE; LLFloaterChat::addChat(chat, FALSE, local_agent); } else { // standard message, not from system std::string saved; if(offline == IM_OFFLINE) { saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str()); } buffer = separator_string + saved + message.substr(message_offset); LL_INFOS("Messaging") << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << LL_ENDL; if (!is_muted || is_linden) { gIMMgr->addMessage( session_id, from_id, name, buffer, LLStringUtil::null, dialog, parent_estate_id, region_id, position, true); chat.mText = std::string("IM: ") + name + separator_string + saved + message.substr(message_offset); BOOL local_agent = FALSE; LLFloaterChat::addChat( chat, TRUE, local_agent ); } else { // muted user, so don't start an IM session, just record line in chat // history. Pretend the chat is from a local agent, // so it will go into the history but not be shown on screen. chat.mText = buffer; BOOL local_agent = TRUE; LLFloaterChat::addChat( chat, TRUE, local_agent ); } } break; case IM_TYPING_START: { LLPointer im_info = new LLIMInfo(gMessageSystem); gIMMgr->processIMTypingStart(im_info); } break; case IM_TYPING_STOP: { LLPointer im_info = new LLIMInfo(gMessageSystem); gIMMgr->processIMTypingStop(im_info); } break; case IM_MESSAGEBOX: { // This is a block, modeless dialog. //*TODO:translate args["MESSAGE"] = message; LLNotifications::instance().add("SystemMessage", args); } break; case IM_GROUP_NOTICE: case IM_GROUP_NOTICE_REQUESTED: { LL_INFOS("Messaging") << "Received IM_GROUP_NOTICE message." << LL_ENDL; // Read the binary bucket for more information. struct notice_bucket_header_t { U8 has_inventory; U8 asset_type; LLUUID group_id; }; struct notice_bucket_full_t { struct notice_bucket_header_t header; U8 item_name[DB_INV_ITEM_NAME_BUF_SIZE]; }* notice_bin_bucket; // Make sure the binary bucket is big enough to hold the header // and a null terminated item name. if ( (binary_bucket_size < (S32)((sizeof(notice_bucket_header_t) + sizeof(U8)))) || (binary_bucket[binary_bucket_size - 1] != '\0') ) { LL_WARNS("Messaging") << "Malformed group notice binary bucket" << LL_ENDL; break; } notice_bin_bucket = (struct notice_bucket_full_t*) &binary_bucket[0]; U8 has_inventory = notice_bin_bucket->header.has_inventory; U8 asset_type = notice_bin_bucket->header.asset_type; LLUUID group_id = notice_bin_bucket->header.group_id; std::string item_name = ll_safe_string((const char*) notice_bin_bucket->item_name); // If there is inventory, give the user the inventory offer. LLOfferInfo* info = NULL; if (has_inventory) { info = new LLOfferInfo; info->mIM = IM_GROUP_NOTICE; info->mFromID = from_id; info->mFromGroup = from_group; info->mTransactionID = session_id; info->mType = (LLAssetType::EType) asset_type; info->mFolderID = gInventory.findCategoryUUIDForType(info->mType); std::string from_name; from_name += "A group member named "; from_name += name; info->mFromName = from_name; info->mDesc = item_name; info->mHost = msg->getSender(); } std::string str(message); // Tokenize the string. // TODO: Support escaped tokens ("||" -> "|") typedef boost::tokenizer > tokenizer; boost::char_separator sep("|","",boost::keep_empty_tokens); tokenizer tokens(str, sep); tokenizer::iterator iter = tokens.begin(); std::string subj(*iter++); std::string mes(*iter++); // Send the notification down the new path. // For requested notices, we don't want to send the popups. if (dialog != IM_GROUP_NOTICE_REQUESTED) { LLSD payload; payload["subject"] = subj; payload["message"] = mes; payload["sender_name"] = name; payload["group_id"] = group_id; payload["inventory_name"] = item_name; payload["inventory_offer"] = info ? info->asLLSD() : LLSD(); LLSD args; args["SUBJECT"] = subj; args["MESSAGE"] = mes; LLNotifications::instance().add(LLNotification::Params("GroupNotice").substitutions(args).payload(payload).timestamp(timestamp)); } // Also send down the old path for now. if (IM_GROUP_NOTICE_REQUESTED == dialog) { LLFloaterGroupInfo::showNotice(subj,mes,group_id,has_inventory,item_name,info); } } break; case IM_GROUP_INVITATION: { //if (!is_linden && (is_busy || is_muted)) if ((is_busy || is_muted)) { LLMessageSystem *msg = gMessageSystem; busy_message(msg,from_id); } else { LL_INFOS("Messaging") << "Received IM_GROUP_INVITATION message." << LL_ENDL; // Read the binary bucket for more information. struct invite_bucket_t { S32 membership_fee; LLUUID role_id; }* invite_bucket; // Make sure the binary bucket is the correct size. if (binary_bucket_size != sizeof(invite_bucket_t)) { LL_WARNS("Messaging") << "Malformed group invite binary bucket" << LL_ENDL; break; } invite_bucket = (struct invite_bucket_t*) &binary_bucket[0]; S32 membership_fee = ntohl(invite_bucket->membership_fee); LLSD payload; payload["transaction_id"] = session_id; payload["group_id"] = from_id; payload["name"] = name; payload["message"] = message; payload["fee"] = membership_fee; LLSD args; args["MESSAGE"] = message; LLNotifications::instance().add("JoinGroup", args, payload, join_group_response); } } break; case IM_INVENTORY_OFFERED: case IM_TASK_INVENTORY_OFFERED: // Someone has offered us some inventory. { LLOfferInfo* info = new LLOfferInfo; if (IM_INVENTORY_OFFERED == dialog) { struct offer_agent_bucket_t { S8 asset_type; LLUUID object_id; }* bucketp; if (sizeof(offer_agent_bucket_t) != binary_bucket_size) { LL_WARNS("Messaging") << "Malformed inventory offer from agent" << LL_ENDL; break; } bucketp = (struct offer_agent_bucket_t*) &binary_bucket[0]; info->mType = (LLAssetType::EType) bucketp->asset_type; info->mObjectID = bucketp->object_id; } else { if (sizeof(S8) != binary_bucket_size) { LL_WARNS("Messaging") << "Malformed inventory offer from object" << LL_ENDL; break; } info->mType = (LLAssetType::EType) binary_bucket[0]; info->mObjectID = LLUUID::null; } info->mIM = dialog; info->mFromID = from_id; info->mFromGroup = from_group; info->mTransactionID = session_id; info->mFolderID = gInventory.findCategoryUUIDForType(info->mType); if (dialog == IM_TASK_INVENTORY_OFFERED) { info->mFromObject = TRUE; } else { info->mFromObject = FALSE; } info->mFromName = name; info->mDesc = message; info->mHost = msg->getSender(); //if (((is_busy && !is_owned_by_me) || is_muted)) if ( is_muted ) { // Same as closing window info->forceResponse(IOR_DECLINE); } else { inventory_offer_handler(info, dialog == IM_TASK_INVENTORY_OFFERED); } } break; case IM_INVENTORY_ACCEPTED: { args["NAME"] = name; LLNotifications::instance().add("InventoryAccepted", args); break; } case IM_INVENTORY_DECLINED: { args["NAME"] = name; LLNotifications::instance().add("InventoryDeclined", args); break; } // TODO: _DEPRECATED suffix as part of vote removal - DEV-24856 case IM_GROUP_VOTE: { LL_WARNS("Messaging") << "Received IM: IM_GROUP_VOTE_DEPRECATED" << LL_ENDL; } break; case IM_GROUP_ELECTION_DEPRECATED: { LL_WARNS("Messaging") << "Received IM: IM_GROUP_ELECTION_DEPRECATED" << LL_ENDL; } break; case IM_SESSION_SEND: { if (!is_linden && is_busy) { return; } // Only show messages if we have a session open (which // should happen after you get an "invitation" if ( !gIMMgr->hasSession(session_id) ) { return; } // standard message, not from system std::string saved; if(offline == IM_OFFLINE) { saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str()); } buffer = separator_string + saved + message.substr(message_offset); BOOL is_this_agent = FALSE; if(from_id == gAgentID) { is_this_agent = TRUE; } gIMMgr->addMessage( session_id, from_id, name, buffer, ll_safe_string((char*)binary_bucket), IM_SESSION_INVITE, parent_estate_id, region_id, position, true); chat.mText = std::string("IM: ") + name + separator_string + saved + message.substr(message_offset); LLFloaterChat::addChat(chat, TRUE, is_this_agent); } break; case IM_FROM_TASK: { if (is_busy && !is_owned_by_me) { return; } chat.mText = name + separator_string + message.substr(message_offset); chat.mFromName = name; // Build a link to open the object IM info window. std::string location = ll_safe_string((char*)binary_bucket,binary_bucket_size); LLSD query_string; query_string["owner"] = from_id; query_string["slurl"] = location.c_str(); query_string["name"] = name; if (from_group) { query_string["groupowned"] = "true"; } if (session_id.notNull()) { chat.mFromID = session_id; } else { // This message originated on a region without the updated code for task id and slurl information. // We just need a unique ID for this object that isn't the owner ID. // If it is the owner ID it will overwrite the style that contains the link to that owner's profile. // This isn't ideal - it will make 1 style for all objects owned by the the same person/group. // This works because the only thing we can really do in this case is show the owner name and link to their profile. chat.mFromID = from_id ^ gAgent.getSessionID(); } std::ostringstream link; link << "secondlife:///app/objectim/" << session_id << LLURI::mapToQueryString(query_string); chat.mURL = link.str(); chat.mText = name + separator_string + message.substr(message_offset); // Note: lie to LLFloaterChat::addChat(), pretending that this is NOT an IM, because // IMs from objcts don't open IM sessions. chat.mSourceType = CHAT_SOURCE_OBJECT; LLFloaterChat::addChat(chat, FALSE, FALSE); } break; case IM_FROM_TASK_AS_ALERT: if (is_busy && !is_owned_by_me) { return; } { // Construct a viewer alert for this message. args["NAME"] = name; args["MESSAGE"] = message; LLNotifications::instance().add("ObjectMessage", args); } break; case IM_BUSY_AUTO_RESPONSE: if (is_muted) { LL_DEBUGS("Messaging") << "Ignoring busy response from " << from_id << LL_ENDL; return; } else { // TODO: after LLTrans hits release, get "busy response" into translatable file buffer = llformat("%s (%s): %s", name.c_str(), "busy response", message.substr(message_offset).c_str()); gIMMgr->addMessage(session_id, from_id, name, buffer); } break; case IM_LURE_USER: { if (is_muted) { return; } else if (is_busy) { busy_message(msg,from_id); } else { LLSD args; // *TODO:translate -> [FIRST] [LAST] (maybe) args["NAME"] = name; args["MESSAGE"] = message; LLSD payload; payload["from_id"] = from_id; payload["lure_id"] = session_id; payload["godlike"] = FALSE; LLNotifications::instance().add("TeleportOffered", args, payload); } } break; case IM_GODLIKE_LURE_USER: { LLSD payload; payload["from_id"] = from_id; payload["lure_id"] = session_id; payload["godlike"] = TRUE; // do not show a message box, because you're about to be // teleported. LLNotifications::instance().forceResponse(LLNotification::Params("TeleportOffered").payload(payload), 0); } break; case IM_GOTO_URL: { LLSD args; // n.b. this is for URLs sent by the system, not for // URLs sent by scripts (i.e. llLoadURL) if (binary_bucket_size <= 0) { LL_WARNS("Messaging") << "bad binary_bucket_size: " << binary_bucket_size << " - aborting function." << LL_ENDL; return; } std::string url; url.assign((char*)binary_bucket, binary_bucket_size-1); args["MESSAGE"] = message; args["URL"] = url; LLSD payload; payload["url"] = url; LLNotifications::instance().add("GotoURL", args, payload ); } break; case IM_FRIENDSHIP_OFFERED: { LLSD payload; payload["from_id"] = from_id; payload["session_id"] = session_id;; payload["online"] = (offline == IM_ONLINE); payload["sender"] = msg->getSender().getIPandPort(); if (is_busy) { busy_message(msg, from_id); LLNotifications::instance().forceResponse(LLNotification::Params("OfferFriendship").payload(payload), 1); } else if (is_muted) { LLNotifications::instance().forceResponse(LLNotification::Params("OfferFriendship").payload(payload), 1); } else { args["[NAME]"] = name; if(message.empty()) { //support for frienship offers from clients before July 2008 LLNotifications::instance().add("OfferFriendshipNoMessage", args, payload); } else { args["[MESSAGE]"] = message; LLNotifications::instance().add("OfferFriendship", args, payload); } } } break; case IM_FRIENDSHIP_ACCEPTED: { // In the case of an offline IM, the formFriendship() may be extraneous // as the database should already include the relationship. But it // doesn't hurt for dupes. LLAvatarTracker::formFriendship(from_id); std::vector strings; strings.push_back(from_id.asString()); send_generic_message("requestonlinenotification", strings); args["NAME"] = name; LLNotifications::instance().add("FriendshipAccepted", args); } break; case IM_FRIENDSHIP_DECLINED_DEPRECATED: default: LL_WARNS("Messaging") << "Instant message calling for unknown dialog " << (S32)dialog << LL_ENDL; break; } LLWindow* viewer_window = gViewerWindow->getWindow(); if (viewer_window && viewer_window->getMinimized()) { viewer_window->flashIcon(5.f); } } void busy_message (LLMessageSystem* msg, LLUUID from_id) { if (gAgent.getBusy()) { std::string my_name; gAgent.buildFullname(my_name); std::string response = gSavedPerAccountSettings.getString("BusyModeResponse2"); pack_instant_message( gMessageSystem, gAgent.getID(), FALSE, gAgent.getSessionID(), from_id, my_name, response, IM_ONLINE, IM_BUSY_AUTO_RESPONSE); gAgent.sendReliableMessage(); } } bool callingcard_offer_callback(const LLSD& notification, const LLSD& response) { S32 option = LLNotification::getSelectedOption(notification, response); LLUUID fid; LLUUID from_id; LLMessageSystem* msg = gMessageSystem; switch(option) { case 0: // accept msg->newMessageFast(_PREHASH_AcceptCallingCard); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_TransactionBlock); msg->addUUIDFast(_PREHASH_TransactionID, notification["payload"]["transaction_id"].asUUID()); fid = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD); msg->nextBlockFast(_PREHASH_FolderData); msg->addUUIDFast(_PREHASH_FolderID, fid); msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); break; case 1: // decline msg->newMessageFast(_PREHASH_DeclineCallingCard); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_TransactionBlock); msg->addUUIDFast(_PREHASH_TransactionID, notification["payload"]["transaction_id"].asUUID()); msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); busy_message(msg, notification["payload"]["source_id"].asUUID()); break; default: // close button probably, possibly timed out break; } return false; } static LLNotificationFunctorRegistration callingcard_offer_cb_reg("OfferCallingCard", callingcard_offer_callback); void process_offer_callingcard(LLMessageSystem* msg, void**) { // someone has offered to form a friendship LL_DEBUGS("Messaging") << "callingcard offer" << LL_ENDL; LLUUID source_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, source_id); LLUUID tid; msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_TransactionID, tid); LLSD payload; payload["transaction_id"] = tid; payload["source_id"] = source_id; payload["sender"] = msg->getSender().getIPandPort(); LLViewerObject* source = gObjectList.findObject(source_id); LLSD args; std::string source_name; if(source && source->isAvatar()) { LLNameValue* nvfirst = source->getNVPair("FirstName"); LLNameValue* nvlast = source->getNVPair("LastName"); if (nvfirst && nvlast) { args["FIRST"] = nvfirst->getString(); args["LAST"] = nvlast->getString(); source_name = std::string(nvfirst->getString()) + " " + nvlast->getString(); } } if(!source_name.empty()) { if (gAgent.getBusy() || LLMuteList::getInstance()->isMuted(source_id, source_name, LLMute::flagTextChat)) { // automatically decline offer LLNotifications::instance().forceResponse(LLNotification::Params("OfferCallingCard").payload(payload), 1); } else { LLNotifications::instance().add("OfferCallingCard", args, payload); } } else { LL_WARNS("Messaging") << "Calling card offer from an unknown source." << LL_ENDL; } } void process_accept_callingcard(LLMessageSystem* msg, void**) { LLNotifications::instance().add("CallingCardAccepted"); } void process_decline_callingcard(LLMessageSystem* msg, void**) { LLNotifications::instance().add("CallingCardDeclined"); } void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) { LLChat chat; std::string mesg; std::string from_name; U8 source_temp; U8 type_temp; U8 audible_temp; LLColor4 color(1.0f, 1.0f, 1.0f, 1.0f); LLUUID from_id; LLUUID owner_id; BOOL is_owned_by_me = FALSE; LLViewerObject* chatter; msg->getString("ChatData", "FromName", from_name); chat.mFromName = from_name; msg->getUUID("ChatData", "SourceID", from_id); chat.mFromID = from_id; // Object owner for objects msg->getUUID("ChatData", "OwnerID", owner_id); msg->getU8Fast(_PREHASH_ChatData, _PREHASH_SourceType, source_temp); chat.mSourceType = (EChatSourceType)source_temp; msg->getU8("ChatData", "ChatType", type_temp); chat.mChatType = (EChatType)type_temp; msg->getU8Fast(_PREHASH_ChatData, _PREHASH_Audible, audible_temp); chat.mAudible = (EChatAudible)audible_temp; chat.mTime = LLFrameTimer::getElapsedSeconds(); BOOL is_busy = gAgent.getBusy(); BOOL is_muted = FALSE; BOOL is_linden = FALSE; is_muted = LLMuteList::getInstance()->isMuted( from_id, from_name, LLMute::flagTextChat) || LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagTextChat); is_linden = chat.mSourceType != CHAT_SOURCE_OBJECT && LLMuteList::getInstance()->isLinden(from_name); BOOL is_audible = (CHAT_AUDIBLE_FULLY == chat.mAudible); chatter = gObjectList.findObject(from_id); if (chatter) { chat.mPosAgent = chatter->getPositionAgent(); // Make swirly things only for talking objects. (not script debug messages, though) if (chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mChatType != CHAT_TYPE_DEBUG_MSG) { LLPointer psc = new LLViewerPartSourceChat(chatter->getPositionAgent()); psc->setSourceObject(chatter); psc->setColor(color); //We set the particles to be owned by the object's owner, //just in case they should be muted by the mute list psc->setOwnerUUID(owner_id); LLViewerPartSim::getInstance()->addPartSource(psc); } // record last audible utterance if (is_audible && (is_linden || (!is_muted && !is_busy))) { if (chat.mChatType != CHAT_TYPE_START && chat.mChatType != CHAT_TYPE_STOP) { gAgent.heardChat(chat.mFromID); } } is_owned_by_me = chatter->permYouOwner(); } if (is_audible) { BOOL visible_in_chat_bubble = FALSE; std::string verb; color.setVec(1.f,1.f,1.f,1.f); msg->getStringFast(_PREHASH_ChatData, _PREHASH_Message, mesg); BOOL ircstyle = FALSE; // Look for IRC-style emotes here so chatbubbles work std::string prefix = mesg.substr(0, 4); if (prefix == "/me " || prefix == "/me'") { chat.mText = from_name; chat.mText += mesg.substr(3); ircstyle = TRUE; } else { chat.mText = mesg; } // Look for the start of typing so we can put "..." in the bubbles. if (CHAT_TYPE_START == chat.mChatType) { LLLocalSpeakerMgr::getInstance()->setSpeakerTyping(from_id, TRUE); // Might not have the avatar constructed yet, eg on login. if (chatter && chatter->isAvatar()) { ((LLVOAvatar*)chatter)->startTyping(); } return; } else if (CHAT_TYPE_STOP == chat.mChatType) { LLLocalSpeakerMgr::getInstance()->setSpeakerTyping(from_id, FALSE); // Might not have the avatar constructed yet, eg on login. if (chatter && chatter->isAvatar()) { ((LLVOAvatar*)chatter)->stopTyping(); } return; } // We have a real utterance now, so can stop showing "..." and proceed. if (chatter && chatter->isAvatar()) { LLLocalSpeakerMgr::getInstance()->setSpeakerTyping(from_id, FALSE); ((LLVOAvatar*)chatter)->stopTyping(); if (!is_muted && !is_busy) { visible_in_chat_bubble = gSavedSettings.getBOOL("UseChatBubbles"); ((LLVOAvatar*)chatter)->addChat(chat); } } // Look for IRC-style emotes if (ircstyle) { // Do nothing, ircstyle is fixed above for chat bubbles } else { switch(chat.mChatType) { case CHAT_TYPE_WHISPER: verb = " " + LLTrans::getString("whisper") + " "; break; case CHAT_TYPE_DEBUG_MSG: case CHAT_TYPE_OWNER: case CHAT_TYPE_NORMAL: verb = ": "; break; case CHAT_TYPE_SHOUT: verb = " " + LLTrans::getString("shout") + " "; break; case CHAT_TYPE_START: case CHAT_TYPE_STOP: LL_WARNS("Messaging") << "Got chat type start/stop in main chat processing." << LL_ENDL; break; default: LL_WARNS("Messaging") << "Unknown type " << chat.mChatType << " in chat!" << LL_ENDL; verb = " say, "; break; } chat.mText = from_name; chat.mText += verb; chat.mText += mesg; } if (chatter) { chat.mPosAgent = chatter->getPositionAgent(); } // truth table: // LINDEN BUSY MUTED OWNED_BY_YOU TASK DISPLAY STORE IN HISTORY // F F F F * Yes Yes // F F F T * Yes Yes // F F T F * No No // F F T T * No No // F T F F * No Yes // F T F T * Yes Yes // F T T F * No No // F T T T * No No // T * * * F Yes Yes chat.mMuted = is_muted && !is_linden; if (!visible_in_chat_bubble && (is_linden || !is_busy || is_owned_by_me)) { // show on screen and add to history LLFloaterChat::addChat(chat, FALSE, FALSE); } else { // just add to chat history LLFloaterChat::addChatHistory(chat); } } } // Simulator we're on is informing the viewer that the agent // is starting to teleport (perhaps to another sim, perhaps to the // same sim). If we initiated the teleport process by sending some kind // of TeleportRequest, then this info is redundant, but if the sim // initiated the teleport (via a script call, being killed, etc.) // then this info is news to us. void process_teleport_start(LLMessageSystem *msg, void**) { U32 teleport_flags = 0x0; msg->getU32("Info", "TeleportFlags", teleport_flags); if (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL) { gViewerWindow->setProgressCancelButtonVisible(FALSE); } else { gViewerWindow->setProgressCancelButtonVisible(TRUE, std::string("Cancel")); // *TODO: Translate } // Freeze the UI and show progress bar // Note: could add data here to differentiate between normal teleport and death. if( gAgent.getTeleportState() == LLAgent::TELEPORT_NONE ) { gTeleportDisplay = TRUE; gAgent.setTeleportState( LLAgent::TELEPORT_START ); make_ui_sound("UISndTeleportOut"); // Don't call LLFirstUse::useTeleport here because this could be // due to being killed, which would send you home, not to a Telehub } } void process_teleport_progress(LLMessageSystem* msg, void**) { LLUUID agent_id; msg->getUUID("AgentData", "AgentID", agent_id); if((gAgent.getID() != agent_id) || (gAgent.getTeleportState() == LLAgent::TELEPORT_NONE)) { LL_WARNS("Messaging") << "Unexpected teleport progress message." << LL_ENDL; return; } U32 teleport_flags = 0x0; msg->getU32("Info", "TeleportFlags", teleport_flags); if (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL) { gViewerWindow->setProgressCancelButtonVisible(FALSE); } else { gViewerWindow->setProgressCancelButtonVisible(TRUE, std::string("Cancel")); //TODO: Translate } std::string buffer; msg->getString("Info", "Message", buffer); LL_DEBUGS("Messaging") << "teleport progress: " << buffer << LL_ENDL; //Sorta hacky...default to using simulator raw messages //if we don't find the coresponding mapping in our progress mappings std::string message = buffer; if (LLAgent::sTeleportProgressMessages.find(buffer) != LLAgent::sTeleportProgressMessages.end() ) { message = LLAgent::sTeleportProgressMessages[buffer]; } gAgent.setTeleportMessage(LLAgent::sTeleportProgressMessages[message]); } class LLFetchInWelcomeArea : public LLInventoryFetchDescendentsObserver { public: LLFetchInWelcomeArea() {} virtual void done() { LLIsType is_landmark(LLAssetType::AT_LANDMARK); LLIsType is_card(LLAssetType::AT_CALLINGCARD); LLInventoryModel::cat_array_t card_cats; LLInventoryModel::item_array_t card_items; LLInventoryModel::cat_array_t land_cats; LLInventoryModel::item_array_t land_items; folder_ref_t::iterator it = mCompleteFolders.begin(); folder_ref_t::iterator end = mCompleteFolders.end(); for(; it != end; ++it) { gInventory.collectDescendentsIf( (*it), land_cats, land_items, LLInventoryModel::EXCLUDE_TRASH, is_landmark); gInventory.collectDescendentsIf( (*it), card_cats, card_items, LLInventoryModel::EXCLUDE_TRASH, is_card); } LLSD args; if ( land_items.count() > 0 ) { // Show notification that they can now teleport to landmarks. Use a random landmark from the inventory S32 random_land = ll_rand( land_items.count() - 1 ); args["NAME"] = land_items[random_land]->getName(); LLNotifications::instance().add("TeleportToLandmark",args); } if ( card_items.count() > 0 ) { // Show notification that they can now contact people. Use a random calling card from the inventory S32 random_card = ll_rand( card_items.count() - 1 ); args["NAME"] = card_items[random_card]->getName(); LLNotifications::instance().add("TeleportToPerson",args); } gInventory.removeObserver(this); delete this; } }; class LLPostTeleportNotifiers : public LLEventTimer { public: LLPostTeleportNotifiers(); virtual ~LLPostTeleportNotifiers(); //function to be called at the supplied frequency virtual BOOL tick(); }; LLPostTeleportNotifiers::LLPostTeleportNotifiers() : LLEventTimer( 2.0 ) { }; LLPostTeleportNotifiers::~LLPostTeleportNotifiers() { } BOOL LLPostTeleportNotifiers::tick() { BOOL all_done = FALSE; if ( gAgent.getTeleportState() == LLAgent::TELEPORT_NONE ) { // get callingcards and landmarks available to the user arriving. LLInventoryFetchDescendentsObserver::folder_ref_t folders; LLUUID folder_id; folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD); if(folder_id.notNull()) folders.push_back(folder_id); folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK); if(folder_id.notNull()) folders.push_back(folder_id); if(!folders.empty()) { LLFetchInWelcomeArea* fetcher = new LLFetchInWelcomeArea; fetcher->fetchDescendents(folders); if(fetcher->isEverythingComplete()) { fetcher->done(); } else { gInventory.addObserver(fetcher); } } all_done = TRUE; } return all_done; } // Teleport notification from the simulator // We're going to pretend to be a new agent void process_teleport_finish(LLMessageSystem* msg, void**) { LL_DEBUGS("Messaging") << "Got teleport location message" << LL_ENDL; LLUUID agent_id; msg->getUUIDFast(_PREHASH_Info, _PREHASH_AgentID, agent_id); if (agent_id != gAgent.getID()) { LL_WARNS("Messaging") << "Got teleport notification for wrong agent!" << LL_ENDL; return; } // Do teleport effect for where you're leaving // VEFFECT: TeleportStart LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); effectp->setPositionGlobal(gAgent.getPositionGlobal()); effectp->setColor(LLColor4U(gAgent.getEffectColor())); LLHUDManager::getInstance()->sendEffects(); U32 location_id; U32 sim_ip; U16 sim_port; LLVector3 pos, look_at; U64 region_handle; msg->getU32Fast(_PREHASH_Info, _PREHASH_LocationID, location_id); msg->getIPAddrFast(_PREHASH_Info, _PREHASH_SimIP, sim_ip); msg->getIPPortFast(_PREHASH_Info, _PREHASH_SimPort, sim_port); //msg->getVector3Fast(_PREHASH_Info, _PREHASH_Position, pos); //msg->getVector3Fast(_PREHASH_Info, _PREHASH_LookAt, look_at); msg->getU64Fast(_PREHASH_Info, _PREHASH_RegionHandle, region_handle); U32 teleport_flags; msg->getU32Fast(_PREHASH_Info, _PREHASH_TeleportFlags, teleport_flags); std::string seedCap; msg->getStringFast(_PREHASH_Info, _PREHASH_SeedCapability, seedCap); // update home location if we are teleporting out of prelude - specific to teleporting to welcome area if((teleport_flags & TELEPORT_FLAGS_SET_HOME_TO_TARGET) && (!gAgent.isGodlike())) { gAgent.setHomePosRegion(region_handle, pos); // Create a timer that will send notices when teleporting is all finished. Since this is // based on the LLEventTimer class, it will be managed by that class and not orphaned or leaked. new LLPostTeleportNotifiers(); } LLHost sim_host(sim_ip, sim_port); // Viewer trusts the simulator. gMessageSystem->enableCircuit(sim_host, TRUE); LLViewerRegion* regionp = LLWorld::getInstance()->addRegion(region_handle, sim_host); /* // send camera update to new region gAgent.updateCamera(); // likewise make sure the camera is behind the avatar gAgent.resetView(TRUE); LLVector3 shift_vector = regionp->getPosRegionFromGlobal(gAgent.getRegion()->getOriginGlobal()); gAgent.setRegion(regionp); gObjectList.shiftObjects(shift_vector); if (gAgent.getAvatarObject()) { gAgent.getAvatarObject()->clearChatText(); gAgent.slamLookAt(look_at); } gAgent.setPositionAgent(pos); gAssetStorage->setUpstream(sim); gCacheName->setUpstream(sim); */ // now, use the circuit info to tell simulator about us! LL_INFOS("Messaging") << "process_teleport_finish() Enabling " << sim_host << " with code " << msg->mOurCircuitCode << LL_ENDL; msg->newMessageFast(_PREHASH_UseCircuitCode); msg->nextBlockFast(_PREHASH_CircuitCode); msg->addU32Fast(_PREHASH_Code, msg->getOurCircuitCode()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->addUUIDFast(_PREHASH_ID, gAgent.getID()); msg->sendReliable(sim_host); send_complete_agent_movement(sim_host); gAgent.setTeleportState( LLAgent::TELEPORT_MOVING ); gAgent.setTeleportMessage(LLAgent::sTeleportProgressMessages["contacting"]); regionp->setSeedCapability(seedCap); // Don't send camera updates to the new region until we're // actually there... // Now do teleport effect for where you're going. // VEFFECT: TeleportEnd effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); effectp->setPositionGlobal(gAgent.getPositionGlobal()); effectp->setColor(LLColor4U(gAgent.getEffectColor())); LLHUDManager::getInstance()->sendEffects(); // gTeleportDisplay = TRUE; // gTeleportDisplayTimer.reset(); // gViewerWindow->setShowProgress(TRUE); } // stuff we have to do every time we get an AvatarInitComplete from a sim /* void process_avatar_init_complete(LLMessageSystem* msg, void**) { LLVector3 agent_pos; msg->getVector3Fast(_PREHASH_AvatarData, _PREHASH_Position, agent_pos); agent_movement_complete(msg->getSender(), agent_pos); } */ void process_agent_movement_complete(LLMessageSystem* msg, void**) { gAgentMovementCompleted = true; LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); LLUUID session_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); if((gAgent.getID() != agent_id) || (gAgent.getSessionID() != session_id)) { LL_WARNS("Messaging") << "Incorrect id in process_agent_movement_complete()" << LL_ENDL; return; } LL_DEBUGS("Messaging") << "process_agent_movement_complete()" << LL_ENDL; // *TODO: check timestamp to make sure the movement compleation // makes sense. LLVector3 agent_pos; msg->getVector3Fast(_PREHASH_Data, _PREHASH_Position, agent_pos); LLVector3 look_at; msg->getVector3Fast(_PREHASH_Data, _PREHASH_LookAt, look_at); U64 region_handle; msg->getU64Fast(_PREHASH_Data, _PREHASH_RegionHandle, region_handle); std::string version_channel; msg->getString("SimData", "ChannelVersion", version_channel); LLVOAvatar* avatarp = gAgent.getAvatarObject(); if (!avatarp) { // Could happen if you were immediately god-teleported away on login, // maybe other cases. Continue, but warn. LL_WARNS("Messaging") << "agent_movement_complete() with NULL avatarp." << LL_ENDL; } F32 x, y; from_region_handle(region_handle, &x, &y); LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromHandle(region_handle); if (!regionp) { if (gAgent.getRegion()) { LL_WARNS("Messaging") << "current region " << gAgent.getRegion()->getOriginGlobal() << LL_ENDL; } LL_WARNS("Messaging") << "Agent being sent to invalid home region: " << x << ":" << y << " current pos " << gAgent.getPositionGlobal() << LL_ENDL; LLAppViewer::instance()->forceDisconnect("You were sent to an invalid region."); return; } LL_INFOS("Messaging") << "Changing home region to " << x << ":" << y << LL_ENDL; // set our upstream host the new simulator and shuffle things as // appropriate. LLVector3 shift_vector = regionp->getPosRegionFromGlobal( gAgent.getRegion()->getOriginGlobal()); gAgent.setRegion(regionp); gObjectList.shiftObjects(shift_vector); gAssetStorage->setUpstream(msg->getSender()); gCacheName->setUpstream(msg->getSender()); gViewerThrottle.sendToSim(); gViewerWindow->sendShapeToSim(); bool is_teleport = gAgent.getTeleportState() == LLAgent::TELEPORT_MOVING; if( is_teleport ) { // Force the camera back onto the agent, don't animate. gAgent.setFocusOnAvatar(TRUE, FALSE); gAgent.slamLookAt(look_at); gAgent.updateCamera(); gAgent.setTeleportState( LLAgent::TELEPORT_START_ARRIVAL ); // set the appearance on teleport since the new sim does not // know what you look like. gAgent.sendAgentSetAppearance(); if (avatarp) { // Chat the "back" SLURL. (DEV-4907) LLChat chat("Teleport completed from " + gAgent.getTeleportSourceSLURL()); chat.mSourceType = CHAT_SOURCE_SYSTEM; LLFloaterChat::addChatHistory(chat); // Set the new position avatarp->setPositionAgent(agent_pos); avatarp->clearChat(); avatarp->slamPosition(); } } else { // This is likely just the initial logging in phase. gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); } if ( LLTracker::isTracking(NULL) ) { // Check distance to beacon, if < 5m, remove beacon LLVector3d beacon_pos = LLTracker::getTrackedPositionGlobal(); LLVector3 beacon_dir(agent_pos.mV[VX] - (F32)fmod(beacon_pos.mdV[VX], 256.0), agent_pos.mV[VY] - (F32)fmod(beacon_pos.mdV[VY], 256.0), 0); if (beacon_dir.magVecSquared() < 25.f) { LLTracker::stopTracking(NULL); } else if ( is_teleport ) { //look at the beacon LLVector3 global_agent_pos = agent_pos; global_agent_pos[0] += x; global_agent_pos[1] += y; look_at = (LLVector3)beacon_pos - global_agent_pos; look_at.normVec(); gAgent.slamLookAt(look_at); } } // TODO: Put back a check for flying status! DK 12/19/05 // Sim tells us whether the new position is off the ground /* if (teleport_flags & TELEPORT_FLAGS_IS_FLYING) { gAgent.setFlying(TRUE); } else { gAgent.setFlying(FALSE); } */ send_agent_update(TRUE, TRUE); if (gAgent.getRegion()->getBlockFly()) { gAgent.setFlying(gAgent.canFly()); } // force simulator to recognize busy state if (gAgent.getBusy()) { gAgent.setBusy(); } else { gAgent.clearBusy(); } if (avatarp) { avatarp->mFootPlane.clearVec(); } // send walk-vs-run status gAgent.sendWalkRun(gAgent.getRunning() || gAgent.getAlwaysRun()); // If the server version has changed, display an info box and offer // to display the release notes, unless this is the initial log in. if (gLastVersionChannel == version_channel) { return; } if (!gLastVersionChannel.empty()) { LLSD payload; payload["message"] = version_channel; LLNotifications::instance().add("ServerVersionChanged", LLSD(), payload); } gLastVersionChannel = version_channel; } void process_crossed_region(LLMessageSystem* msg, void**) { LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); LLUUID session_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); if((gAgent.getID() != agent_id) || (gAgent.getSessionID() != session_id)) { LL_WARNS("Messaging") << "Incorrect id in process_crossed_region()" << LL_ENDL; return; } LL_INFOS("Messaging") << "process_crossed_region()" << LL_ENDL; U32 sim_ip; msg->getIPAddrFast(_PREHASH_RegionData, _PREHASH_SimIP, sim_ip); U16 sim_port; msg->getIPPortFast(_PREHASH_RegionData, _PREHASH_SimPort, sim_port); LLHost sim_host(sim_ip, sim_port); U64 region_handle; msg->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle); std::string seedCap; msg->getStringFast(_PREHASH_RegionData, _PREHASH_SeedCapability, seedCap); send_complete_agent_movement(sim_host); LLViewerRegion* regionp = LLWorld::getInstance()->addRegion(region_handle, sim_host); regionp->setSeedCapability(seedCap); } // Sends avatar and camera information to simulator. // Sent roughly once per frame, or 20 times per second, whichever is less often const F32 THRESHOLD_HEAD_ROT_QDOT = 0.9997f; // ~= 2.5 degrees -- if its less than this we need to update head_rot const F32 MAX_HEAD_ROT_QDOT = 0.99999f; // ~= 0.5 degrees -- if its greater than this then no need to update head_rot // between these values we delay the updates (but no more than one second) void send_agent_update(BOOL force_send, BOOL send_reliable) { if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE) { // We don't care if they want to send an agent update, they're not allowed to until the simulator // that's the target is ready to receive them (after avatar_init_complete is received) return; } // We have already requested to log out. Don't send agent updates. if(LLAppViewer::instance()->logoutRequestSent()) { return; } // no region to send update to if(gAgent.getRegion() == NULL) { return; } const F32 TRANSLATE_THRESHOLD = 0.01f; // NOTA BENE: This is (intentionally?) using the small angle sine approximation to test for rotation // Plus, there is an extra 0.5 in the mix since the perpendicular between last_camera_at and getAtAxis() bisects cam_rot_change // Thus, we're actually testing against 0.2 degrees const F32 ROTATION_THRESHOLD = 0.1f * 2.f*F_PI/360.f; // Rotation thresh 0.2 deg, see note above const U8 DUP_MSGS = 1; // HACK! number of times to repeat data on motionless agent // Store data on last sent update so that if no changes, no send static LLVector3 last_camera_pos_agent, last_camera_at, last_camera_left, last_camera_up; static LLVector3 cam_center_chg, cam_rot_chg; static LLQuaternion last_head_rot; static U32 last_control_flags = 0; static U8 last_render_state; static U8 duplicate_count = 0; static F32 head_rot_chg = 1.0; static U8 last_flags; LLMessageSystem *msg = gMessageSystem; LLVector3 camera_pos_agent; // local to avatar's region U8 render_state; LLQuaternion body_rotation = gAgent.getFrameAgent().getQuaternion(); LLQuaternion head_rotation = gAgent.getHeadRotation(); camera_pos_agent = gAgent.getCameraPositionAgent(); render_state = gAgent.getRenderState(); U32 control_flag_change = 0; U8 flag_change = 0; cam_center_chg = last_camera_pos_agent - camera_pos_agent; cam_rot_chg = last_camera_at - LLViewerCamera::getInstance()->getAtAxis(); // If a modifier key is held down, turn off // LBUTTON and ML_LBUTTON so that using the camera (alt-key) doesn't // trigger a control event. U32 control_flags = gAgent.getControlFlags(); MASK key_mask = gKeyboard->currentMask(TRUE); if (key_mask & MASK_ALT || key_mask & MASK_CONTROL) { control_flags &= ~( AGENT_CONTROL_LBUTTON_DOWN | AGENT_CONTROL_ML_LBUTTON_DOWN ); control_flags |= AGENT_CONTROL_LBUTTON_UP | AGENT_CONTROL_ML_LBUTTON_UP ; } control_flag_change = last_control_flags ^ control_flags; U8 flags = AU_FLAGS_NONE; if (gAgent.isGroupTitleHidden()) { flags |= AU_FLAGS_HIDETITLE; } flag_change = last_flags ^ flags; head_rot_chg = dot(last_head_rot, head_rotation); if (force_send || (cam_center_chg.magVec() > TRANSLATE_THRESHOLD) || (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT) || (last_render_state != render_state) || (cam_rot_chg.magVec() > ROTATION_THRESHOLD) || control_flag_change != 0 || flag_change != 0) { /* if (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT) { //LL_INFOS("Messaging") << "head rot " << head_rotation << LL_ENDL; LL_INFOS("Messaging") << "head_rot_chg = " << head_rot_chg << LL_ENDL; } if (cam_rot_chg.magVec() > ROTATION_THRESHOLD) { LL_INFOS("Messaging") << "cam rot " << cam_rot_chg.magVec() << LL_ENDL; } if (cam_center_chg.magVec() > TRANSLATE_THRESHOLD) { LL_INFOS("Messaging") << "cam center " << cam_center_chg.magVec() << LL_ENDL; } // if (drag_delta_chg.magVec() > TRANSLATE_THRESHOLD) // { // LL_INFOS("Messaging") << "drag delta " << drag_delta_chg.magVec() << LL_ENDL; // } if (control_flag_change) { LL_INFOS("Messaging") << "dcf = " << control_flag_change << LL_ENDL; } */ duplicate_count = 0; } else { duplicate_count++; if (head_rot_chg < MAX_HEAD_ROT_QDOT && duplicate_count < AGENT_UPDATES_PER_SECOND) { // The head_rotation is sent for updating things like attached guns. // We only trigger a new update when head_rotation deviates beyond // some threshold from the last update, however this can break fine // adjustments when trying to aim an attached gun, so what we do here // (where we would normally skip sending an update when nothing has changed) // is gradually reduce the threshold to allow a better update to // eventually get sent... should update to within 0.5 degrees in less // than a second. if (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT + (MAX_HEAD_ROT_QDOT - THRESHOLD_HEAD_ROT_QDOT) * duplicate_count / AGENT_UPDATES_PER_SECOND) { duplicate_count = 0; } else { return; } } else { return; } } if (duplicate_count < DUP_MSGS && !gDisconnected) { // Build the message msg->newMessageFast(_PREHASH_AgentUpdate); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->addQuatFast(_PREHASH_BodyRotation, body_rotation); msg->addQuatFast(_PREHASH_HeadRotation, head_rotation); msg->addU8Fast(_PREHASH_State, render_state); msg->addU8Fast(_PREHASH_Flags, flags); // if (camera_pos_agent.mV[VY] > 255.f) // { // LL_INFOS("Messaging") << "Sending camera center " << camera_pos_agent << LL_ENDL; // } msg->addVector3Fast(_PREHASH_CameraCenter, camera_pos_agent); msg->addVector3Fast(_PREHASH_CameraAtAxis, LLViewerCamera::getInstance()->getAtAxis()); msg->addVector3Fast(_PREHASH_CameraLeftAxis, LLViewerCamera::getInstance()->getLeftAxis()); msg->addVector3Fast(_PREHASH_CameraUpAxis, LLViewerCamera::getInstance()->getUpAxis()); msg->addF32Fast(_PREHASH_Far, gAgent.mDrawDistance); msg->addU32Fast(_PREHASH_ControlFlags, control_flags); if (gDebugClicks) { if (control_flags & AGENT_CONTROL_LBUTTON_DOWN) { LL_INFOS("Messaging") << "AgentUpdate left button down" << LL_ENDL; } if (control_flags & AGENT_CONTROL_LBUTTON_UP) { LL_INFOS("Messaging") << "AgentUpdate left button up" << LL_ENDL; } } gAgent.enableControlFlagReset(); if (!send_reliable) { gAgent.sendMessage(); } else { gAgent.sendReliableMessage(); } // LL_DEBUGS("Messaging") << "agent " << avatar_pos_agent << " cam " << camera_pos_agent << LL_ENDL; // Copy the old data last_head_rot = head_rotation; last_render_state = render_state; last_camera_pos_agent = camera_pos_agent; last_camera_at = LLViewerCamera::getInstance()->getAtAxis(); last_camera_left = LLViewerCamera::getInstance()->getLeftAxis(); last_camera_up = LLViewerCamera::getInstance()->getUpAxis(); last_control_flags = control_flags; last_flags = flags; } } // *TODO: Remove this dependency, or figure out a better way to handle // this hack. extern U32 gObjectBits; void process_object_update(LLMessageSystem *mesgsys, void **user_data) { LLMemType mt(LLMemType::MTYPE_OBJECT); // Update the data counters if (mesgsys->getReceiveCompressedSize()) { gObjectBits += mesgsys->getReceiveCompressedSize() * 8; } else { gObjectBits += mesgsys->getReceiveSize() * 8; } // Update the object... gObjectList.processObjectUpdate(mesgsys, user_data, OUT_FULL); stop_glerror(); } void process_compressed_object_update(LLMessageSystem *mesgsys, void **user_data) { LLMemType mt(LLMemType::MTYPE_OBJECT); // Update the data counters if (mesgsys->getReceiveCompressedSize()) { gObjectBits += mesgsys->getReceiveCompressedSize() * 8; } else { gObjectBits += mesgsys->getReceiveSize() * 8; } // Update the object... gObjectList.processCompressedObjectUpdate(mesgsys, user_data, OUT_FULL_COMPRESSED); stop_glerror(); } void process_cached_object_update(LLMessageSystem *mesgsys, void **user_data) { LLMemType mt(LLMemType::MTYPE_OBJECT); // Update the data counters if (mesgsys->getReceiveCompressedSize()) { gObjectBits += mesgsys->getReceiveCompressedSize() * 8; } else { gObjectBits += mesgsys->getReceiveSize() * 8; } // Update the object... gObjectList.processCachedObjectUpdate(mesgsys, user_data, OUT_FULL_CACHED); stop_glerror(); } void process_terse_object_update_improved(LLMessageSystem *mesgsys, void **user_data) { LLMemType mt(LLMemType::MTYPE_OBJECT); if (mesgsys->getReceiveCompressedSize()) { gObjectBits += mesgsys->getReceiveCompressedSize() * 8; } else { gObjectBits += mesgsys->getReceiveSize() * 8; } gObjectList.processCompressedObjectUpdate(mesgsys, user_data, OUT_TERSE_IMPROVED); } void process_kill_object(LLMessageSystem *mesgsys, void **user_data) { LLFastTimer t(LLFastTimer::FTM_PROCESS_OBJECTS); LLUUID id; U32 local_id; S32 i; S32 num_objects; num_objects = mesgsys->getNumberOfBlocksFast(_PREHASH_ObjectData); for (i = 0; i < num_objects; i++) { mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i); LLViewerObjectList::getUUIDFromLocal(id, local_id, gMessageSystem->getSenderIP(), gMessageSystem->getSenderPort()); if (id == LLUUID::null) { LL_DEBUGS("Messaging") << "Unknown kill for local " << local_id << LL_ENDL; gObjectList.mNumUnknownKills++; continue; } else { LL_DEBUGS("Messaging") << "Kill message for local " << local_id << LL_ENDL; } LLSelectMgr::getInstance()->removeObjectFromSelections(id); // ...don't kill the avatar if (!(id == gAgentID)) { LLViewerObject *objectp = gObjectList.findObject(id); if (objectp) { // Display green bubble on kill if ( gShowObjectUpdates ) { LLViewerObject* newobject; newobject = gObjectList.createObjectViewer(LL_PCODE_LEGACY_TEXT_BUBBLE, objectp->getRegion()); LLVOTextBubble* bubble = (LLVOTextBubble*) newobject; bubble->mColor.setVec(0.f, 1.f, 0.f, 1.f); bubble->setScale( 2.0f * bubble->getScale() ); bubble->setPositionGlobal(objectp->getPositionGlobal()); gPipeline.addObject(bubble); } // Do the kill gObjectList.killObject(objectp); } else { LL_WARNS("Messaging") << "Object in UUID lookup, but not on object list in kill!" << LL_ENDL; gObjectList.mNumUnknownKills++; } } } } void process_time_synch(LLMessageSystem *mesgsys, void **user_data) { LLVector3 sun_direction; LLVector3 sun_ang_velocity; F32 phase; U64 space_time_usec; U32 seconds_per_day; U32 seconds_per_year; // "SimulatorViewerTimeMessage" mesgsys->getU64Fast(_PREHASH_TimeInfo, _PREHASH_UsecSinceStart, space_time_usec); mesgsys->getU32Fast(_PREHASH_TimeInfo, _PREHASH_SecPerDay, seconds_per_day); mesgsys->getU32Fast(_PREHASH_TimeInfo, _PREHASH_SecPerYear, seconds_per_year); // This should eventually be moved to an "UpdateHeavenlyBodies" message mesgsys->getF32Fast(_PREHASH_TimeInfo, _PREHASH_SunPhase, phase); mesgsys->getVector3Fast(_PREHASH_TimeInfo, _PREHASH_SunDirection, sun_direction); mesgsys->getVector3Fast(_PREHASH_TimeInfo, _PREHASH_SunAngVelocity, sun_ang_velocity); LLWorld::getInstance()->setSpaceTimeUSec(space_time_usec); //LL_DEBUGS("Messaging") << "time_synch() - " << sun_direction << ", " << sun_ang_velocity // << ", " << phase << LL_ENDL; gSky.setSunPhase(phase); gSky.setSunTargetDirection(sun_direction, sun_ang_velocity); if (!gNoRender && !(gSavedSettings.getBOOL("SkyOverrideSimSunPosition") || gSky.getOverrideSun())) { gSky.setSunDirection(sun_direction, sun_ang_velocity); } } void process_sound_trigger(LLMessageSystem *msg, void **) { if (!gAudiop) return; U64 region_handle = 0; F32 gain = 0; LLUUID sound_id; LLUUID owner_id; LLUUID object_id; LLUUID parent_id; LLVector3 pos_local; msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_SoundID, sound_id); msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_OwnerID, owner_id); msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_ObjectID, object_id); msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_ParentID, parent_id); msg->getU64Fast(_PREHASH_SoundData, _PREHASH_Handle, region_handle); msg->getVector3Fast(_PREHASH_SoundData, _PREHASH_Position, pos_local); msg->getF32Fast(_PREHASH_SoundData, _PREHASH_Gain, gain); // adjust sound location to true global coords LLVector3d pos_global = from_region_handle(region_handle); pos_global.mdV[VX] += pos_local.mV[VX]; pos_global.mdV[VY] += pos_local.mV[VY]; pos_global.mdV[VZ] += pos_local.mV[VZ]; // Don't play a trigger sound if you can't hear it due // to parcel "local audio only" settings. if (!LLViewerParcelMgr::getInstance()->canHearSound(pos_global)) return; // Don't play sounds triggered by someone you muted. if (LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagObjectSounds)) return; // Don't play sounds from an object you muted if (LLMuteList::getInstance()->isMuted(object_id)) return; // Don't play sounds from an object whose parent you muted if (parent_id.notNull() && LLMuteList::getInstance()->isMuted(parent_id)) { return; } // Don't play sounds from a region with maturity above current agent maturity if( !gAgent.canAccessMaturityInRegion( region_handle ) ) { return; } gAudiop->triggerSound(sound_id, owner_id, gain, LLAudioEngine::AUDIO_TYPE_SFX, pos_global); } void process_preload_sound(LLMessageSystem *msg, void **user_data) { if (!gAudiop) { return; } LLUUID sound_id; LLUUID object_id; LLUUID owner_id; msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_SoundID, sound_id); msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_id); msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_OwnerID, owner_id); LLViewerObject *objectp = gObjectList.findObject(object_id); if (!objectp) return; if (LLMuteList::getInstance()->isMuted(object_id)) return; if (LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagObjectSounds)) return; LLAudioSource *sourcep = objectp->getAudioSource(owner_id); if (!sourcep) return; LLAudioData *datap = gAudiop->getAudioData(sound_id); // Note that I don't actually do any loading of the // audio data into a buffer at this point, as it won't actually // help us out. // Don't play sounds from a region with maturity above current agent maturity LLVector3d pos_global = objectp->getPositionGlobal(); if( !gAgent.canAccessMaturityAtGlobal( pos_global ) ) { return; } // Add audioData starts a transfer internally. sourcep->addAudioData(datap, FALSE); } void process_attached_sound(LLMessageSystem *msg, void **user_data) { F32 gain = 0; LLUUID sound_id; LLUUID object_id; LLUUID owner_id; U8 flags; msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_SoundID, sound_id); msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_id); msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_OwnerID, owner_id); msg->getF32Fast(_PREHASH_DataBlock, _PREHASH_Gain, gain); msg->getU8Fast(_PREHASH_DataBlock, _PREHASH_Flags, flags); LLViewerObject *objectp = gObjectList.findObject(object_id); if (!objectp) { // we don't know about this object, just bail return; } if (LLMuteList::getInstance()->isMuted(object_id)) return; if (LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagObjectSounds)) return; // Don't play sounds from a region with maturity above current agent maturity LLVector3d pos = objectp->getPositionGlobal(); if( !gAgent.canAccessMaturityAtGlobal(pos) ) { return; } objectp->setAttachedSound(sound_id, owner_id, gain, flags); } void process_attached_sound_gain_change(LLMessageSystem *mesgsys, void **user_data) { F32 gain = 0; LLUUID object_guid; LLViewerObject *objectp = NULL; mesgsys->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_guid); if (!((objectp = gObjectList.findObject(object_guid)))) { // we don't know about this object, just bail return; } mesgsys->getF32Fast(_PREHASH_DataBlock, _PREHASH_Gain, gain); objectp->adjustAudioGain(gain); } void process_health_message(LLMessageSystem *mesgsys, void **user_data) { F32 health; mesgsys->getF32Fast(_PREHASH_HealthData, _PREHASH_Health, health); if (gStatusBar) { gStatusBar->setHealth((S32)health); } } void process_sim_stats(LLMessageSystem *msg, void **user_data) { S32 count = msg->getNumberOfBlocks("Stat"); for (S32 i = 0; i < count; ++i) { U32 stat_id; F32 stat_value; msg->getU32("Stat", "StatID", stat_id, i); msg->getF32("Stat", "StatValue", stat_value, i); switch (stat_id) { case LL_SIM_STAT_TIME_DILATION: LLViewerStats::getInstance()->mSimTimeDilation.addValue(stat_value); break; case LL_SIM_STAT_FPS: LLViewerStats::getInstance()->mSimFPS.addValue(stat_value); break; case LL_SIM_STAT_PHYSFPS: LLViewerStats::getInstance()->mSimPhysicsFPS.addValue(stat_value); break; case LL_SIM_STAT_AGENTUPS: LLViewerStats::getInstance()->mSimAgentUPS.addValue(stat_value); break; case LL_SIM_STAT_FRAMEMS: LLViewerStats::getInstance()->mSimFrameMsec.addValue(stat_value); break; case LL_SIM_STAT_NETMS: LLViewerStats::getInstance()->mSimNetMsec.addValue(stat_value); break; case LL_SIM_STAT_SIMOTHERMS: LLViewerStats::getInstance()->mSimSimOtherMsec.addValue(stat_value); break; case LL_SIM_STAT_SIMPHYSICSMS: LLViewerStats::getInstance()->mSimSimPhysicsMsec.addValue(stat_value); break; case LL_SIM_STAT_AGENTMS: LLViewerStats::getInstance()->mSimAgentMsec.addValue(stat_value); break; case LL_SIM_STAT_IMAGESMS: LLViewerStats::getInstance()->mSimImagesMsec.addValue(stat_value); break; case LL_SIM_STAT_SCRIPTMS: LLViewerStats::getInstance()->mSimScriptMsec.addValue(stat_value); break; case LL_SIM_STAT_NUMTASKS: LLViewerStats::getInstance()->mSimObjects.addValue(stat_value); break; case LL_SIM_STAT_NUMTASKSACTIVE: LLViewerStats::getInstance()->mSimActiveObjects.addValue(stat_value); break; case LL_SIM_STAT_NUMAGENTMAIN: LLViewerStats::getInstance()->mSimMainAgents.addValue(stat_value); break; case LL_SIM_STAT_NUMAGENTCHILD: LLViewerStats::getInstance()->mSimChildAgents.addValue(stat_value); break; case LL_SIM_STAT_NUMSCRIPTSACTIVE: LLViewerStats::getInstance()->mSimActiveScripts.addValue(stat_value); break; case LL_SIM_STAT_SCRIPT_EPS: LLViewerStats::getInstance()->mSimScriptEPS.addValue(stat_value); break; case LL_SIM_STAT_INPPS: LLViewerStats::getInstance()->mSimInPPS.addValue(stat_value); break; case LL_SIM_STAT_OUTPPS: LLViewerStats::getInstance()->mSimOutPPS.addValue(stat_value); break; case LL_SIM_STAT_PENDING_DOWNLOADS: LLViewerStats::getInstance()->mSimPendingDownloads.addValue(stat_value); break; case LL_SIM_STAT_PENDING_UPLOADS: LLViewerStats::getInstance()->mSimPendingUploads.addValue(stat_value); break; case LL_SIM_STAT_PENDING_LOCAL_UPLOADS: LLViewerStats::getInstance()->mSimPendingLocalUploads.addValue(stat_value); break; case LL_SIM_STAT_TOTAL_UNACKED_BYTES: LLViewerStats::getInstance()->mSimTotalUnackedBytes.addValue(stat_value / 1024.f); break; case LL_SIM_STAT_PHYSICS_PINNED_TASKS: LLViewerStats::getInstance()->mPhysicsPinnedTasks.addValue(stat_value); break; case LL_SIM_STAT_PHYSICS_LOD_TASKS: LLViewerStats::getInstance()->mPhysicsLODTasks.addValue(stat_value); break; case LL_SIM_STAT_SIMPHYSICSSTEPMS: LLViewerStats::getInstance()->mSimSimPhysicsStepMsec.addValue(stat_value); break; case LL_SIM_STAT_SIMPHYSICSSHAPEMS: LLViewerStats::getInstance()->mSimSimPhysicsShapeUpdateMsec.addValue(stat_value); break; case LL_SIM_STAT_SIMPHYSICSOTHERMS: LLViewerStats::getInstance()->mSimSimPhysicsOtherMsec.addValue(stat_value); break; case LL_SIM_STAT_SIMPHYSICSMEMORY: LLViewerStats::getInstance()->mPhysicsMemoryAllocated.addValue(stat_value); break; case LL_SIM_STAT_SIMSPARETIME: LLViewerStats::getInstance()->mSimSpareMsec.addValue(stat_value); break; case LL_SIM_STAT_SIMSLEEPTIME: LLViewerStats::getInstance()->mSimSleepMsec.addValue(stat_value); break; case LL_SIM_STAT_IOPUMPTIME: LLViewerStats::getInstance()->mSimPumpIOMsec.addValue(stat_value); break; default: // Used to be a commented out warning. LL_DEBUGS("Messaging") << "Unknown stat id" << stat_id << LL_ENDL; break; } } /* msg->getF32Fast(_PREHASH_Statistics, _PREHASH_PhysicsTimeDilation, time_dilation); LLViewerStats::getInstance()->mSimTDStat.addValue(time_dilation); // Process information // { CpuUsage F32 } // { SimMemTotal F32 } // { SimMemRSS F32 } // { ProcessUptime F32 } F32 cpu_usage; F32 sim_mem_total; F32 sim_mem_rss; F32 process_uptime; msg->getF32Fast(_PREHASH_Statistics, _PREHASH_CpuUsage, cpu_usage); msg->getF32Fast(_PREHASH_Statistics, _PREHASH_SimMemTotal, sim_mem_total); msg->getF32Fast(_PREHASH_Statistics, _PREHASH_SimMemRSS, sim_mem_rss); msg->getF32Fast(_PREHASH_Statistics, _PREHASH_ProcessUptime, process_uptime); LLViewerStats::getInstance()->mSimCPUUsageStat.addValue(cpu_usage); LLViewerStats::getInstance()->mSimMemTotalStat.addValue(sim_mem_total); LLViewerStats::getInstance()->mSimMemRSSStat.addValue(sim_mem_rss); */ // // Various hacks that aren't statistics, but are being handled here. // U32 max_tasks_per_region; U32 region_flags; msg->getU32("Region", "ObjectCapacity", max_tasks_per_region); msg->getU32("Region", "RegionFlags", region_flags); LLViewerRegion* regionp = gAgent.getRegion(); if (regionp) { BOOL was_flying = gAgent.getFlying(); regionp->setRegionFlags(region_flags); regionp->setMaxTasks(max_tasks_per_region); // HACK: This makes agents drop from the sky if the region is // set to no fly while people are still in the sim. if (was_flying && regionp->getBlockFly()) { gAgent.setFlying(gAgent.canFly()); } } } void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data) { LLUUID animation_id; LLUUID uuid; S32 anim_sequence_id; LLVOAvatar *avatarp; mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid); //clear animation flags avatarp = (LLVOAvatar *)gObjectList.findObject(uuid); if (!avatarp) { // no agent by this ID...error? LL_WARNS("Messaging") << "Received animation state for unknown avatar" << uuid << LL_ENDL; return; } S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList); S32 num_source_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationSourceList); avatarp->mSignaledAnimations.clear(); if (avatarp->isSelf()) { LLUUID object_id; for( S32 i = 0; i < num_blocks; i++ ) { mesgsys->getUUIDFast(_PREHASH_AnimationList, _PREHASH_AnimID, animation_id, i); mesgsys->getS32Fast(_PREHASH_AnimationList, _PREHASH_AnimSequenceID, anim_sequence_id, i); LL_DEBUGS("Messaging") << "Anim sequence ID: " << anim_sequence_id << LL_ENDL; avatarp->mSignaledAnimations[animation_id] = anim_sequence_id; if (i < num_source_blocks) { mesgsys->getUUIDFast(_PREHASH_AnimationSourceList, _PREHASH_ObjectID, object_id, i); LLViewerObject* object = gObjectList.findObject(object_id); if (object) { object->mFlags |= FLAGS_ANIM_SOURCE; BOOL anim_found = FALSE; LLVOAvatar::AnimSourceIterator anim_it = avatarp->mAnimationSources.find(object_id); for (;anim_it != avatarp->mAnimationSources.end(); ++anim_it) { if (anim_it->second == animation_id) { anim_found = TRUE; break; } } if (!anim_found) { avatarp->mAnimationSources.insert(LLVOAvatar::AnimationSourceMap::value_type(object_id, animation_id)); } } } } } else { for( S32 i = 0; i < num_blocks; i++ ) { mesgsys->getUUIDFast(_PREHASH_AnimationList, _PREHASH_AnimID, animation_id, i); mesgsys->getS32Fast(_PREHASH_AnimationList, _PREHASH_AnimSequenceID, anim_sequence_id, i); avatarp->mSignaledAnimations[animation_id] = anim_sequence_id; } } if (num_blocks) { avatarp->processAnimationStateChanges(); } } void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data) { LLUUID uuid; mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid); LLVOAvatar* avatarp = (LLVOAvatar *)gObjectList.findObject(uuid); if( avatarp ) { avatarp->processAvatarAppearance( mesgsys ); } else { LL_WARNS("Messaging") << "avatar_appearance sent for unknown avatar " << uuid << LL_ENDL; } } void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data) { LLVector4 cameraCollidePlane; mesgsys->getVector4Fast(_PREHASH_CameraCollidePlane, _PREHASH_Plane, cameraCollidePlane); gAgent.setCameraCollidePlane(cameraCollidePlane); } void near_sit_object(BOOL success, void *data) { if (success) { // Send message to sit on object gMessageSystem->newMessageFast(_PREHASH_AgentSit); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); gAgent.sendReliableMessage(); } } void process_avatar_sit_response(LLMessageSystem *mesgsys, void **user_data) { LLVector3 sitPosition; LLQuaternion sitRotation; LLUUID sitObjectID; BOOL use_autopilot; mesgsys->getUUIDFast(_PREHASH_SitObject, _PREHASH_ID, sitObjectID); mesgsys->getBOOLFast(_PREHASH_SitTransform, _PREHASH_AutoPilot, use_autopilot); mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_SitPosition, sitPosition); mesgsys->getQuatFast(_PREHASH_SitTransform, _PREHASH_SitRotation, sitRotation); LLVector3 camera_eye; mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_CameraEyeOffset, camera_eye); LLVector3 camera_at; mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_CameraAtOffset, camera_at); BOOL force_mouselook; mesgsys->getBOOLFast(_PREHASH_SitTransform, _PREHASH_ForceMouselook, force_mouselook); LLVOAvatar* avatar = gAgent.getAvatarObject(); if (avatar && dist_vec_squared(camera_eye, camera_at) > 0.0001f) { gAgent.setSitCamera(sitObjectID, camera_eye, camera_at); } gAgent.mForceMouselook = force_mouselook; LLViewerObject* object = gObjectList.findObject(sitObjectID); if (object) { LLVector3 sit_spot = object->getPositionAgent() + (sitPosition * object->getRotation()); if (!use_autopilot || (avatar && avatar->mIsSitting && avatar->getRoot() == object->getRoot())) { //we're already sitting on this object, so don't autopilot } else { gAgent.startAutoPilotGlobal(gAgent.getPosGlobalFromAgent(sit_spot), "Sit", &sitRotation, near_sit_object, NULL, 0.5f); } } else { LL_WARNS("Messaging") << "Received sit approval for unknown object " << sitObjectID << LL_ENDL; } } void process_clear_follow_cam_properties(LLMessageSystem *mesgsys, void **user_data) { LLUUID source_id; mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, source_id); LLFollowCamMgr::removeFollowCamParams(source_id); } void process_set_follow_cam_properties(LLMessageSystem *mesgsys, void **user_data) { S32 type; F32 value; bool settingPosition = false; bool settingFocus = false; bool settingFocusOffset = false; LLVector3 position; LLVector3 focus; LLVector3 focus_offset; LLUUID source_id; mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, source_id); LLViewerObject* objectp = gObjectList.findObject(source_id); if (objectp) { objectp->mFlags |= FLAGS_CAMERA_SOURCE; } S32 num_objects = mesgsys->getNumberOfBlocks("CameraProperty"); for (S32 block_index = 0; block_index < num_objects; block_index++) { mesgsys->getS32("CameraProperty", "Type", type, block_index); mesgsys->getF32("CameraProperty", "Value", value, block_index); switch(type) { case FOLLOWCAM_PITCH: LLFollowCamMgr::setPitch(source_id, value); break; case FOLLOWCAM_FOCUS_OFFSET_X: focus_offset.mV[VX] = value; settingFocusOffset = true; break; case FOLLOWCAM_FOCUS_OFFSET_Y: focus_offset.mV[VY] = value; settingFocusOffset = true; break; case FOLLOWCAM_FOCUS_OFFSET_Z: focus_offset.mV[VZ] = value; settingFocusOffset = true; break; case FOLLOWCAM_POSITION_LAG: LLFollowCamMgr::setPositionLag(source_id, value); break; case FOLLOWCAM_FOCUS_LAG: LLFollowCamMgr::setFocusLag(source_id, value); break; case FOLLOWCAM_DISTANCE: LLFollowCamMgr::setDistance(source_id, value); break; case FOLLOWCAM_BEHINDNESS_ANGLE: LLFollowCamMgr::setBehindnessAngle(source_id, value); break; case FOLLOWCAM_BEHINDNESS_LAG: LLFollowCamMgr::setBehindnessLag(source_id, value); break; case FOLLOWCAM_POSITION_THRESHOLD: LLFollowCamMgr::setPositionThreshold(source_id, value); break; case FOLLOWCAM_FOCUS_THRESHOLD: LLFollowCamMgr::setFocusThreshold(source_id, value); break; case FOLLOWCAM_ACTIVE: //if 1, set using followcam,. LLFollowCamMgr::setCameraActive(source_id, value != 0.f); break; case FOLLOWCAM_POSITION_X: settingPosition = true; position.mV[ 0 ] = value; break; case FOLLOWCAM_POSITION_Y: settingPosition = true; position.mV[ 1 ] = value; break; case FOLLOWCAM_POSITION_Z: settingPosition = true; position.mV[ 2 ] = value; break; case FOLLOWCAM_FOCUS_X: settingFocus = true; focus.mV[ 0 ] = value; break; case FOLLOWCAM_FOCUS_Y: settingFocus = true; focus.mV[ 1 ] = value; break; case FOLLOWCAM_FOCUS_Z: settingFocus = true; focus.mV[ 2 ] = value; break; case FOLLOWCAM_POSITION_LOCKED: LLFollowCamMgr::setPositionLocked(source_id, value != 0.f); break; case FOLLOWCAM_FOCUS_LOCKED: LLFollowCamMgr::setFocusLocked(source_id, value != 0.f); break; default: break; } } if ( settingPosition ) { LLFollowCamMgr::setPosition(source_id, position); } if ( settingFocus ) { LLFollowCamMgr::setFocus(source_id, focus); } if ( settingFocusOffset ) { LLFollowCamMgr::setFocusOffset(source_id, focus_offset); } } //end Ventrella // Culled from newsim lltask.cpp void process_name_value(LLMessageSystem *mesgsys, void **user_data) { std::string temp_str; LLUUID id; S32 i, num_blocks; mesgsys->getUUIDFast(_PREHASH_TaskData, _PREHASH_ID, id); LLViewerObject* object = gObjectList.findObject(id); if (object) { num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_NameValueData); for (i = 0; i < num_blocks; i++) { mesgsys->getStringFast(_PREHASH_NameValueData, _PREHASH_NVPair, temp_str, i); LL_INFOS("Messaging") << "Added to object Name Value: " << temp_str << LL_ENDL; object->addNVPair(temp_str); } } else { LL_INFOS("Messaging") << "Can't find object " << id << " to add name value pair" << LL_ENDL; } } void process_remove_name_value(LLMessageSystem *mesgsys, void **user_data) { std::string temp_str; LLUUID id; S32 i, num_blocks; mesgsys->getUUIDFast(_PREHASH_TaskData, _PREHASH_ID, id); LLViewerObject* object = gObjectList.findObject(id); if (object) { num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_NameValueData); for (i = 0; i < num_blocks; i++) { mesgsys->getStringFast(_PREHASH_NameValueData, _PREHASH_NVPair, temp_str, i); LL_INFOS("Messaging") << "Removed from object Name Value: " << temp_str << LL_ENDL; object->removeNVPair(temp_str); } } else { LL_INFOS("Messaging") << "Can't find object " << id << " to remove name value pair" << LL_ENDL; } } void process_kick_user(LLMessageSystem *msg, void** /*user_data*/) { std::string message; msg->getStringFast(_PREHASH_UserInfo, _PREHASH_Reason, message); LLAppViewer::instance()->forceDisconnect(message); } /* void process_user_list_reply(LLMessageSystem *msg, void **user_data) { LLUserList::processUserListReply(msg, user_data); return; char firstname[MAX_STRING+1]; char lastname[MAX_STRING+1]; U8 status; S32 user_count; user_count = msg->getNumberOfBlocks("UserBlock"); for (S32 i = 0; i < user_count; i++) { msg->getData("UserBlock", i, "FirstName", firstname); msg->getData("UserBlock", i, "LastName", lastname); msg->getData("UserBlock", i, "Status", &status); if (status & 0x01) { dialog_friends_add_friend(buffer, TRUE); } else { dialog_friends_add_friend(buffer, FALSE); } } dialog_friends_done_adding(); } */ // this is not handled in processUpdateMessage /* void process_time_dilation(LLMessageSystem *msg, void **user_data) { // get the time_dilation U16 foo; msg->getData("TimeDilation", "TimeDilation", &foo); F32 time_dilation = ((F32) foo) / 65535.f; // get the pointer to the right region U32 ip = msg->getSenderIP(); U32 port = msg->getSenderPort(); LLViewerRegion *regionp = LLWorld::getInstance()->getRegion(ip, port); if (regionp) { regionp->setTimeDilation(time_dilation); } } */ void process_money_balance_reply( LLMessageSystem* msg, void** ) { S32 balance = 0; S32 credit = 0; S32 committed = 0; std::string desc; msg->getS32("MoneyData", "MoneyBalance", balance); msg->getS32("MoneyData", "SquareMetersCredit", credit); msg->getS32("MoneyData", "SquareMetersCommitted", committed); msg->getStringFast(_PREHASH_MoneyData, _PREHASH_Description, desc); LL_INFOS("Messaging") << "L$, credit, committed: " << balance << " " << credit << " " << committed << LL_ENDL; if (gStatusBar) { S32 old_balance = gStatusBar->getBalance(); // This is an update, not the first transmission of balance if (old_balance != 0) { // this is actually an update if (balance > old_balance) { LLFirstUse::useBalanceIncrease(balance - old_balance); } else if (balance < old_balance) { LLFirstUse::useBalanceDecrease(balance - old_balance); } } gStatusBar->setBalance(balance); gStatusBar->setLandCredit(credit); gStatusBar->setLandCommitted(committed); } LLUUID tid; msg->getUUID("MoneyData", "TransactionID", tid); static std::deque recent; if(!desc.empty() && gSavedSettings.getBOOL("NotifyMoneyChange") && (std::find(recent.rbegin(), recent.rend(), tid) == recent.rend())) { // Make the user confirm the transaction, since they might // have missed something during an event. // *TODO:translate LLSD args; args["MESSAGE"] = desc; LLNotifications::instance().add("SystemMessage", args); // Once the 'recent' container gets large enough, chop some // off the beginning. const U32 MAX_LOOKBACK = 30; const S32 POP_FRONT_SIZE = 12; if(recent.size() > MAX_LOOKBACK) { LL_DEBUGS("Messaging") << "Removing oldest transaction records" << LL_ENDL; recent.erase(recent.begin(), recent.begin() + POP_FRONT_SIZE); } //LL_DEBUGS("Messaging") << "Pushing back transaction " << tid << LL_ENDL; recent.push_back(tid); } } bool handle_special_notification_callback(const LLSD& notification, const LLSD& response) { S32 option = LLNotification::getSelectedOption(notification, response); if (0 == option) { // set the preference to the maturity of the region we're calling int preferredMaturity = notification["payload"]["_region_access"].asInteger(); gSavedSettings.setU32("PreferredMaturity", preferredMaturity); gAgent.sendMaturityPreferenceToServer(preferredMaturity); } return false; } // some of the server notifications need special handling. This is where we do that. bool handle_special_notification(std::string notificationID, LLSD& llsdBlock) { int regionAccess = llsdBlock["_region_access"].asInteger(); llsdBlock["REGIONMATURITY"] = LLViewerRegion::accessToString(regionAccess); // we're going to throw the LLSD in there in case anyone ever wants to use it LLNotifications::instance().add(notificationID+"_Notify", llsdBlock); if (regionAccess == SIM_ACCESS_MATURE) { if (gAgent.isTeen()) { LLNotifications::instance().add(notificationID+"_KB", llsdBlock); return true; } else if (gAgent.prefersPG()) { LLNotifications::instance().add(notificationID+"_Change", llsdBlock, llsdBlock, handle_special_notification_callback); return true; } } else if (regionAccess == SIM_ACCESS_ADULT) { if (!gAgent.isAdult()) { LLNotifications::instance().add(notificationID+"_KB", llsdBlock); return true; } else if (gAgent.prefersPG() || gAgent.prefersMature()) { LLNotifications::instance().add(notificationID+"_Change", llsdBlock, llsdBlock, handle_special_notification_callback); return true; } } return false; } bool attempt_standard_notification(LLMessageSystem* msgsystem) { // if we have additional alert data if (msgsystem->getNumberOfBlocksFast(_PREHASH_AlertInfo) > 0) { // notification was specified using the new mechanism, so we can just handle it here std::string notificationID; std::string llsdRaw; LLSD llsdBlock; msgsystem->getStringFast(_PREHASH_AlertInfo, _PREHASH_Message, notificationID); msgsystem->getStringFast(_PREHASH_AlertInfo, _PREHASH_ExtraParams, llsdRaw); if (llsdRaw.length()) { std::istringstream llsdData(llsdRaw); if (!LLSDSerialize::deserialize(llsdBlock, llsdData, llsdRaw.length())) { llwarns << "attempt_standard_notification: Attempted to read notification parameter data into LLSD but failed:" << llsdRaw << llendl; } } if ( (notificationID == "RegionEntryAccessBlocked") || (notificationID == "LandClaimAccessBlocked") || (notificationID == "LandBuyAccessBlocked") ) { /*--------------------------------------------------------------------- (Commented so a grep will find the notification strings, since we construct them on the fly; if you add additional notifications, please update the comment.) Could throw any of the following notifications: RegionEntryAccessBlocked RegionEntryAccessBlocked_Notify RegionEntryAccessBlocked_Change RegionEntryAccessBlocked_KB LandClaimAccessBlocked LandClaimAccessBlocked_Notify LandClaimAccessBlocked_Change LandClaimAccessBlocked_KB LandBuyAccessBlocked LandBuyAccessBlocked_Notify LandBuyAccessBlocked_Change LandBuyAccessBlocked_KB -----------------------------------------------------------------------*/ if (handle_special_notification(notificationID, llsdBlock)) { return true; } } LLNotifications::instance().add(notificationID, llsdBlock); return true; } return false; } void process_agent_alert_message(LLMessageSystem* msgsystem, void** user_data) { // make sure the cursor is back to the usual default since the // alert is probably due to some kind of error. gViewerWindow->getWindow()->resetBusyCount(); if (!attempt_standard_notification(msgsystem)) { BOOL modal = FALSE; msgsystem->getBOOL("AlertData", "Modal", modal); std::string buffer; msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, buffer); process_alert_core(buffer, modal); } } // The only difference between this routine and the previous is the fact that // for this routine, the modal parameter is always false. Sadly, for the message // handled by this routine, there is no "Modal" parameter on the message, and // there's no API to tell if a message has the given parameter or not. // So we can't handle the messages with the same handler. void process_alert_message(LLMessageSystem *msgsystem, void **user_data) { // make sure the cursor is back to the usual default since the // alert is probably due to some kind of error. gViewerWindow->getWindow()->resetBusyCount(); if (!attempt_standard_notification(msgsystem)) { BOOL modal = FALSE; std::string buffer; msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, buffer); process_alert_core(buffer, modal); } } void process_alert_core(const std::string& message, BOOL modal) { // HACK -- handle callbacks for specific alerts if ( message == "You died and have been teleported to your home location") { LLViewerStats::getInstance()->incStat(LLViewerStats::ST_KILLED_COUNT); } else if( message == "Home position set." ) { // save the home location image to disk std::string snap_filename = gDirUtilp->getLindenUserDir(); snap_filename += gDirUtilp->getDirDelimiter(); snap_filename += SCREEN_HOME_FILENAME; gViewerWindow->saveSnapshot(snap_filename, gViewerWindow->getWindowDisplayWidth(), gViewerWindow->getWindowDisplayHeight(), FALSE, FALSE); } const std::string ALERT_PREFIX("ALERT: "); const std::string NOTIFY_PREFIX("NOTIFY: "); if (message.find(ALERT_PREFIX) == 0) { // Allow the server to spawn a named alert so that server alerts can be // translated out of English. std::string alert_name(message.substr(ALERT_PREFIX.length())); LLNotifications::instance().add(alert_name); } else if (message.find(NOTIFY_PREFIX) == 0) { // Allow the server to spawn a named notification so that server notifications can be // translated out of English. std::string notify_name(message.substr(NOTIFY_PREFIX.length())); LLNotifications::instance().add(notify_name); } else if (message[0] == '/') { // System message is important, show in upper-right box not tip std::string text(message.substr(1)); LLSD args; if (text.substr(0,17) == "RESTART_X_MINUTES") { S32 mins = 0; LLStringUtil::convertToS32(text.substr(18), mins); args["MINUTES"] = llformat("%d",mins); LLNotifications::instance().add("RegionRestartMinutes", args); } else if (text.substr(0,17) == "RESTART_X_SECONDS") { S32 secs = 0; LLStringUtil::convertToS32(text.substr(18), secs); args["SECONDS"] = llformat("%d",secs); LLNotifications::instance().add("RegionRestartSeconds", args); } else { // *TODO:translate args["MESSAGE"] = text; LLNotifications::instance().add("SystemMessage", args); } } else if (modal) { // *TODO:translate LLSD args; args["ERROR_MESSAGE"] = message; LLNotifications::instance().add("ErrorMessage", args); } else { // *TODO:translate LLSD args; args["MESSAGE"] = message; LLNotifications::instance().add("SystemMessageTip", args); } } mean_collision_list_t gMeanCollisionList; time_t gLastDisplayedTime = 0; void handle_show_mean_events(void *) { if (gNoRender) { return; } LLFloaterBump::show(NULL); } void mean_name_callback(const LLUUID &id, const std::string& first, const std::string& last, BOOL always_false, void* data) { if (gNoRender) { return; } static const U32 max_collision_list_size = 20; if (gMeanCollisionList.size() > max_collision_list_size) { mean_collision_list_t::iterator iter = gMeanCollisionList.begin(); for (U32 i=0; imPerp == id) { mcd->mFirstName = first; mcd->mLastName = last; } } } void process_mean_collision_alert_message(LLMessageSystem *msgsystem, void **user_data) { if (gAgent.inPrelude()) { // In prelude, bumping is OK. This dialog is rather confusing to // newbies, so we don't show it. Drop the packet on the floor. return; } // make sure the cursor is back to the usual default since the // alert is probably due to some kind of error. gViewerWindow->getWindow()->resetBusyCount(); LLUUID perp; U32 time; U8 u8type; EMeanCollisionType type; F32 mag; S32 i, num = msgsystem->getNumberOfBlocks(_PREHASH_MeanCollision); for (i = 0; i < num; i++) { msgsystem->getUUIDFast(_PREHASH_MeanCollision, _PREHASH_Perp, perp); msgsystem->getU32Fast(_PREHASH_MeanCollision, _PREHASH_Time, time); msgsystem->getF32Fast(_PREHASH_MeanCollision, _PREHASH_Mag, mag); msgsystem->getU8Fast(_PREHASH_MeanCollision, _PREHASH_Type, u8type); type = (EMeanCollisionType)u8type; BOOL b_found = FALSE; for (mean_collision_list_t::iterator iter = gMeanCollisionList.begin(); iter != gMeanCollisionList.end(); ++iter) { LLMeanCollisionData *mcd = *iter; if ((mcd->mPerp == perp) && (mcd->mType == type)) { mcd->mTime = time; mcd->mMag = mag; b_found = TRUE; break; } } if (!b_found) { LLMeanCollisionData *mcd = new LLMeanCollisionData(gAgentID, perp, time, type, mag); gMeanCollisionList.push_front(mcd); const BOOL is_group = FALSE; gCacheName->getNameFromUUID(perp, is_group, mean_name_callback); } } } void process_frozen_message(LLMessageSystem *msgsystem, void **user_data) { // make sure the cursor is back to the usual default since the // alert is probably due to some kind of error. gViewerWindow->getWindow()->resetBusyCount(); BOOL b_frozen; msgsystem->getBOOL("FrozenData", "Data", b_frozen); // TODO: make being frozen change view if (b_frozen) { } else { } } // do some extra stuff once we get our economy data void process_economy_data(LLMessageSystem *msg, void** /*user_data*/) { LLGlobalEconomy::processEconomyData(msg, LLGlobalEconomy::Singleton::getInstance()); S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); LL_INFOS_ONCE("Messaging") << "EconomyData message arrived; upload cost is L$" << upload_cost << LL_ENDL; LLFloaterImagePreview::setUploadAmount(upload_cost); LLFloaterAnimPreview::setUploadAmount(upload_cost); gMenuHolder->childSetLabelArg("Upload Image", "[COST]", llformat("%d", upload_cost)); gMenuHolder->childSetLabelArg("Upload Sound", "[COST]", llformat("%d", upload_cost)); gMenuHolder->childSetLabelArg("Upload Animation", "[COST]", llformat("%d", upload_cost)); gMenuHolder->childSetLabelArg("Bulk Upload", "[COST]", llformat("%d", upload_cost)); } void notify_cautioned_script_question(const LLSD& notification, const LLSD& response, S32 orig_questions, BOOL granted) { // only continue if at least some permissions were requested if (orig_questions) { // check to see if the person we are asking // "'[OBJECTNAME]', an object owned by '[OWNERNAME]', // located in [REGIONNAME] at [REGIONPOS], // has been permission to: [PERMISSIONS]." LLUIString notice(LLFloaterChat::getInstance()->getString(granted ? "ScriptQuestionCautionChatGranted" : "ScriptQuestionCautionChatDenied")); // always include the object name and owner name notice.setArg("[OBJECTNAME]", notification["payload"]["object_name"].asString()); notice.setArg("[OWNERNAME]", notification["payload"]["owner_name"].asString()); // try to lookup viewerobject that corresponds to the object that // requested permissions (here, taskid->requesting object id) BOOL foundpos = FALSE; LLViewerObject* viewobj = gObjectList.findObject(notification["payload"]["task_id"].asUUID()); if (viewobj) { // found the viewerobject, get it's position in its region LLVector3 objpos(viewobj->getPosition()); // try to lookup the name of the region the object is in LLViewerRegion* viewregion = viewobj->getRegion(); if (viewregion) { // got the region, so include the region and 3d coordinates of the object notice.setArg("[REGIONNAME]", viewregion->getName()); std::string formatpos = llformat("%.1f, %.1f,%.1f", objpos[VX], objpos[VY], objpos[VZ]); notice.setArg("[REGIONPOS]", formatpos); foundpos = TRUE; } } if (!foundpos) { // unable to determine location of the object notice.setArg("[REGIONNAME]", "(unknown region)"); notice.setArg("[REGIONPOS]", "(unknown position)"); } // check each permission that was requested, and list each // permission that has been flagged as a caution permission BOOL caution = FALSE; S32 count = 0; std::string perms; for (S32 i = 0; i < SCRIPT_PERMISSION_EOF; i++) { if ((orig_questions & LSCRIPTRunTimePermissionBits[i]) && SCRIPT_QUESTION_IS_CAUTION[i]) { count++; caution = TRUE; // add a comma before the permission description if it is not the first permission // added to the list or the last permission to check if ((count > 1) && (i < SCRIPT_PERMISSION_EOF)) { perms.append(", "); } perms.append(LLFloaterChat::getInstance()->getString(SCRIPT_QUESTIONS[i])); } } notice.setArg("[PERMISSIONS]", perms); // log a chat message as long as at least one requested permission // is a caution permission if (caution) { LLChat chat(notice.getString()); LLFloaterChat::addChat(chat, FALSE, FALSE); } } } bool script_question_cb(const LLSD& notification, const LLSD& response) { S32 option = LLNotification::getSelectedOption(notification, response); LLMessageSystem *msg = gMessageSystem; S32 orig = notification["payload"]["questions"].asInteger(); S32 new_questions = orig; // check whether permissions were granted or denied BOOL allowed = TRUE; // the "yes/accept" button is the first button in the template, making it button 0 // if any other button was clicked, the permissions were denied if (option != 0) { new_questions = 0; allowed = FALSE; } LLUUID task_id = notification["payload"]["task_id"].asUUID(); LLUUID item_id = notification["payload"]["item_id"].asUUID(); // reply with the permissions granted or denied msg->newMessageFast(_PREHASH_ScriptAnswerYes); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_Data); msg->addUUIDFast(_PREHASH_TaskID, task_id); msg->addUUIDFast(_PREHASH_ItemID, item_id); msg->addS32Fast(_PREHASH_Questions, new_questions); msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); // only log a chat message if caution prompts are enabled if (gSavedSettings.getBOOL("PermissionsCautionEnabled")) { // log a chat message, if appropriate notify_cautioned_script_question(notification, response, orig, allowed); } if ( response["Mute"] ) // mute { LLMuteList::getInstance()->add(LLMute(item_id, notification["payload"]["object_name"].asString(), LLMute::OBJECT)); // purge the message queue of any previously queued requests from the same source. DEV-4879 class OfferMatcher : public LLNotifyBoxView::Matcher { public: OfferMatcher(const LLUUID& to_block) : blocked_id(to_block) {} BOOL matches(const LLNotificationPtr notification) const { if (notification->getName() == "ScriptQuestionCaution" || notification->getName() == "ScriptQuestion") { return (notification->getPayload()["item_id"].asUUID() == blocked_id); } return FALSE; } private: const LLUUID& blocked_id; }; // should do this via the channel gNotifyBoxView->purgeMessagesMatching(OfferMatcher(item_id)); } if (response["Details"]) { // respawn notification... LLNotifications::instance().add(notification["name"], notification["substitutions"], notification["payload"]); // ...with description on top LLNotifications::instance().add("DebitPermissionDetails"); } return false; } static LLNotificationFunctorRegistration script_question_cb_reg_1("ScriptQuestion", script_question_cb); static LLNotificationFunctorRegistration script_question_cb_reg_2("ScriptQuestionCaution", script_question_cb); void process_script_question(LLMessageSystem *msg, void **user_data) { // *TODO:translate owner name -> [FIRST] [LAST] LLHost sender = msg->getSender(); LLUUID taskid; LLUUID itemid; S32 questions; std::string object_name; std::string owner_name; // taskid -> object key of object requesting permissions msg->getUUIDFast(_PREHASH_Data, _PREHASH_TaskID, taskid ); // itemid -> script asset key of script requesting permissions msg->getUUIDFast(_PREHASH_Data, _PREHASH_ItemID, itemid ); msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectName, object_name); msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectOwner, owner_name); msg->getS32Fast(_PREHASH_Data, _PREHASH_Questions, questions ); // Special case. If the objects are owned by this agent, throttle per-object instead // of per-owner. It's common for residents to reset a ton of scripts that re-request // permissions, as with tier boxes. UUIDs can't be valid agent names and vice-versa, // so we'll reuse the same namespace for both throttle types. std::string throttle_name = owner_name; std::string self_name; gAgent.getName( self_name ); if( owner_name == self_name ) { throttle_name = taskid.getString(); } // don't display permission requests if this object is muted if (LLMuteList::getInstance()->isMuted(taskid)) return; // throttle excessive requests from any specific user's scripts typedef LLKeyThrottle LLStringThrottle; static LLStringThrottle question_throttle( LLREQUEST_PERMISSION_THROTTLE_LIMIT, LLREQUEST_PERMISSION_THROTTLE_INTERVAL ); switch (question_throttle.noteAction(throttle_name)) { case LLStringThrottle::THROTTLE_NEWLY_BLOCKED: LL_INFOS("Messaging") << "process_script_question throttled" << " owner_name:" << owner_name << LL_ENDL; // Fall through case LLStringThrottle::THROTTLE_BLOCKED: // Escape altogether until we recover return; case LLStringThrottle::THROTTLE_OK: break; } std::string script_question; if (questions) { BOOL caution = FALSE; S32 count = 0; LLSD args; args["OBJECTNAME"] = object_name; args["NAME"] = owner_name; // check the received permission flags against each permission for (S32 i = 0; i < SCRIPT_PERMISSION_EOF; i++) { if (questions & LSCRIPTRunTimePermissionBits[i]) { count++; script_question += " " + LLFloaterChat::getInstance()->getString(SCRIPT_QUESTIONS[i]) + "\n"; // check whether permission question should cause special caution dialog caution |= (SCRIPT_QUESTION_IS_CAUTION[i]); } } args["QUESTIONS"] = script_question; LLSD payload; payload["task_id"] = taskid; payload["item_id"] = itemid; payload["sender"] = sender.getIPandPort(); payload["questions"] = questions; payload["object_name"] = object_name; payload["owner_name"] = owner_name; // check whether cautions are even enabled or not if (gSavedSettings.getBOOL("PermissionsCautionEnabled")) { // display the caution permissions prompt LLNotifications::instance().add(caution ? "ScriptQuestionCaution" : "ScriptQuestion", args, payload); } else { // fall back to default behavior if cautions are entirely disabled LLNotifications::instance().add("ScriptQuestion", args, payload); } } } void process_derez_container(LLMessageSystem *msg, void**) { LL_WARNS("Messaging") << "call to deprecated process_derez_container" << LL_ENDL; } void container_inventory_arrived(LLViewerObject* object, InventoryObjectList* inventory, S32 serial_num, void* data) { LL_DEBUGS("Messaging") << "container_inventory_arrived()" << LL_ENDL; if( gAgent.cameraMouselook() ) { gAgent.changeCameraToDefault(); } LLInventoryView* view = LLInventoryView::getActiveInventory(); if (inventory->size() > 2) { // create a new inventory category to put this in LLUUID cat_id; cat_id = gInventory.createNewCategory(gAgent.getInventoryRootID(), LLAssetType::AT_NONE, std::string("Acquired Items")); //TODO: Translate InventoryObjectList::const_iterator it = inventory->begin(); InventoryObjectList::const_iterator end = inventory->end(); for ( ; it != end; ++it) { if ((*it)->getType() != LLAssetType::AT_CATEGORY && (*it)->getType() != LLAssetType::AT_ROOT_CATEGORY) { LLInventoryObject* obj = (LLInventoryObject*)(*it); LLInventoryItem* item = (LLInventoryItem*)(obj); LLUUID item_id; item_id.generate(); time_t creation_date_utc = time_corrected(); LLPointer new_item = new LLViewerInventoryItem(item_id, cat_id, item->getPermissions(), item->getAssetUUID(), item->getType(), item->getInventoryType(), item->getName(), item->getDescription(), LLSaleInfo::DEFAULT, item->getFlags(), creation_date_utc); new_item->updateServer(TRUE); gInventory.updateItem(new_item); } } gInventory.notifyObservers(); if(view) { view->getPanel()->setSelection(cat_id, TAKE_FOCUS_NO); } } else if (inventory->size() == 2) { // we're going to get one fake root category as well as the // one actual object InventoryObjectList::iterator it = inventory->begin(); if ((*it)->getType() == LLAssetType::AT_CATEGORY || (*it)->getType() == LLAssetType::AT_ROOT_CATEGORY) { ++it; } LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it)); LLUUID category = gInventory.findCategoryUUIDForType(item->getType()); LLUUID item_id; item_id.generate(); time_t creation_date_utc = time_corrected(); LLPointer new_item = new LLViewerInventoryItem(item_id, category, item->getPermissions(), item->getAssetUUID(), item->getType(), item->getInventoryType(), item->getName(), item->getDescription(), LLSaleInfo::DEFAULT, item->getFlags(), creation_date_utc); new_item->updateServer(TRUE); gInventory.updateItem(new_item); gInventory.notifyObservers(); if(view) { view->getPanel()->setSelection(item_id, TAKE_FOCUS_NO); } } // we've got the inventory, now delete this object if this was a take BOOL delete_object = (BOOL)(intptr_t)data; LLViewerRegion *region = gAgent.getRegion(); if (delete_object && region) { gMessageSystem->newMessageFast(_PREHASH_ObjectDelete); gMessageSystem->nextBlockFast(_PREHASH_AgentData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); const U8 NO_FORCE = 0; gMessageSystem->addU8Fast(_PREHASH_Force, NO_FORCE); gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID()); gMessageSystem->sendReliable(region->getHost()); } } // method to format the time. std::string formatted_time(const time_t& the_time) { char buffer[30]; /* Flawfinder: ignore */ LLStringUtil::copy(buffer, ctime(&the_time), 30); buffer[24] = '\0'; return std::string(buffer); } void process_teleport_failed(LLMessageSystem *msg, void**) { std::string reason; std::string big_reason; LLSD args; // if we have additional alert data if (msg->getNumberOfBlocksFast(_PREHASH_AlertInfo) > 0) { // Get the message ID msg->getStringFast(_PREHASH_AlertInfo, _PREHASH_Message, reason); big_reason = LLAgent::sTeleportErrorMessages[reason]; if ( big_reason.size() > 0 ) { // Substitute verbose reason from the local map args["REASON"] = big_reason; } else { // Nothing found in the map - use what the server returned in the original message block msg->getStringFast(_PREHASH_Info, _PREHASH_Reason, reason); args["REASON"] = reason; } LLSD llsd_block; std::string llsd_raw; msg->getStringFast(_PREHASH_AlertInfo, _PREHASH_ExtraParams, llsd_raw); if (llsd_raw.length()) { std::istringstream llsd_data(llsd_raw); if (!LLSDSerialize::deserialize(llsd_block, llsd_data, llsd_raw.length())) { llwarns << "process_teleport_failed: Attempted to read alert parameter data into LLSD but failed:" << llsd_raw << llendl; } else { // change notification name in this special case if (handle_special_notification("RegionEntryAccessBlocked", llsd_block)) { if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) { gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); } return; } } } } else { msg->getStringFast(_PREHASH_Info, _PREHASH_Reason, reason); big_reason = LLAgent::sTeleportErrorMessages[reason]; if ( big_reason.size() > 0 ) { // Substitute verbose reason from the local map args["REASON"] = big_reason; } else { // Nothing found in the map - use what the server returned args["REASON"] = reason; } } LLNotifications::instance().add("CouldNotTeleportReason", args); if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) { gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); } } void process_teleport_local(LLMessageSystem *msg,void**) { LLUUID agent_id; msg->getUUIDFast(_PREHASH_Info, _PREHASH_AgentID, agent_id); if (agent_id != gAgent.getID()) { LL_WARNS("Messaging") << "Got teleport notification for wrong agent!" << LL_ENDL; return; } U32 location_id; LLVector3 pos, look_at; U32 teleport_flags; msg->getU32Fast(_PREHASH_Info, _PREHASH_LocationID, location_id); msg->getVector3Fast(_PREHASH_Info, _PREHASH_Position, pos); msg->getVector3Fast(_PREHASH_Info, _PREHASH_LookAt, look_at); msg->getU32Fast(_PREHASH_Info, _PREHASH_TeleportFlags, teleport_flags); if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) { gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); } // Sim tells us whether the new position is off the ground if (teleport_flags & TELEPORT_FLAGS_IS_FLYING) { gAgent.setFlying(TRUE); } else { gAgent.setFlying(FALSE); } gAgent.setPositionAgent(pos); gAgent.slamLookAt(look_at); // likewise make sure the camera is behind the avatar gAgent.resetView(TRUE, TRUE); // send camera update to new region gAgent.updateCamera(); send_agent_update(TRUE, TRUE); } void send_simple_im(const LLUUID& to_id, const std::string& message, EInstantMessage dialog, const LLUUID& id) { std::string my_name; gAgent.buildFullname(my_name); send_improved_im(to_id, my_name, message, IM_ONLINE, dialog, id, NO_TIMESTAMP, (U8*)EMPTY_BINARY_BUCKET, EMPTY_BINARY_BUCKET_SIZE); } void send_group_notice(const LLUUID& group_id, const std::string& subject, const std::string& message, const LLInventoryItem* item) { // Put this notice into an instant message form. // This will mean converting the item to a binary bucket, // and the subject/message into a single field. std::string my_name; gAgent.buildFullname(my_name); // Combine subject + message into a single string. std::ostringstream subject_and_message; // TODO: turn all existing |'s into ||'s in subject and message. subject_and_message << subject << "|" << message; // Create an empty binary bucket. U8 bin_bucket[MAX_INVENTORY_BUFFER_SIZE]; U8* bucket_to_send = bin_bucket; bin_bucket[0] = '\0'; S32 bin_bucket_size = EMPTY_BINARY_BUCKET_SIZE; // If there is an item being sent, pack it into the binary bucket. if (item) { LLSD item_def; item_def["item_id"] = item->getUUID(); item_def["owner_id"] = item->getPermissions().getOwner(); std::ostringstream ostr; LLSDSerialize::serialize(item_def, ostr, LLSDSerialize::LLSD_XML); bin_bucket_size = ostr.str().copy( (char*)bin_bucket, ostr.str().size()); bin_bucket[bin_bucket_size] = '\0'; } else { bucket_to_send = (U8*) EMPTY_BINARY_BUCKET; } send_improved_im( group_id, my_name, subject_and_message.str(), IM_ONLINE, IM_GROUP_NOTICE, LLUUID::null, NO_TIMESTAMP, bucket_to_send, bin_bucket_size); } bool handle_lure_callback(const LLSD& notification, const LLSD& response) { std::string text = response["message"].asString(); S32 option = LLNotification::getSelectedOption(notification, response); if(0 == option) { LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_StartLure); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_Info); msg->addU8Fast(_PREHASH_LureType, (U8)0); // sim will fill this in. msg->addStringFast(_PREHASH_Message, text); for(LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray(); it != notification["payload"]["ids"].endArray(); ++it) { msg->nextBlockFast(_PREHASH_TargetData); msg->addUUIDFast(_PREHASH_TargetID, it->asUUID()); } gAgent.sendReliableMessage(); } return false; } void handle_lure(const LLUUID& invitee) { LLDynamicArray ids; ids.push_back(invitee); handle_lure(ids); } // Prompt for a message to the invited user. void handle_lure(LLDynamicArray& ids) { LLSD edit_args; edit_args["REGION"] = gAgent.getRegion()->getName(); LLSD payload; for (LLDynamicArray::iterator it = ids.begin(); it != ids.end(); ++it) { payload["ids"].append(*it); } if (gAgent.isGodlike()) { LLNotifications::instance().add("OfferTeleportFromGod", edit_args, payload, handle_lure_callback); } else { LLNotifications::instance().add("OfferTeleport", edit_args, payload, handle_lure_callback); } } void send_improved_im(const LLUUID& to_id, const std::string& name, const std::string& message, U8 offline, EInstantMessage dialog, const LLUUID& id, U32 timestamp, const U8* binary_bucket, S32 binary_bucket_size) { pack_instant_message( gMessageSystem, gAgent.getID(), FALSE, gAgent.getSessionID(), to_id, name, message, offline, dialog, id, 0, LLUUID::null, gAgent.getPositionAgent(), timestamp, binary_bucket, binary_bucket_size); gAgent.sendReliableMessage(); } void send_places_query(const LLUUID& query_id, const LLUUID& trans_id, const std::string& query_text, U32 query_flags, S32 category, const std::string& sim_name) { LLMessageSystem* msg = gMessageSystem; msg->newMessage("PlacesQuery"); msg->nextBlock("AgentData"); msg->addUUID("AgentID", gAgent.getID()); msg->addUUID("SessionID", gAgent.getSessionID()); msg->addUUID("QueryID", query_id); msg->nextBlock("TransactionData"); msg->addUUID("TransactionID", trans_id); msg->nextBlock("QueryData"); msg->addString("QueryText", query_text); msg->addU32("QueryFlags", query_flags); msg->addS8("Category", (S8)category); msg->addString("SimName", sim_name); gAgent.sendReliableMessage(); } void process_user_info_reply(LLMessageSystem* msg, void**) { LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { LL_WARNS("Messaging") << "process_user_info_reply - " << "wrong agent id." << LL_ENDL; } BOOL im_via_email; msg->getBOOLFast(_PREHASH_UserData, _PREHASH_IMViaEMail, im_via_email); std::string email; msg->getStringFast(_PREHASH_UserData, _PREHASH_EMail, email); std::string dir_visibility; msg->getString( "UserData", "DirectoryVisibility", dir_visibility); LLFloaterPreference::updateUserInfo(dir_visibility, im_via_email, email); LLFloaterPostcard::updateUserInfo(email); } //--------------------------------------------------------------------------- // Script Dialog //--------------------------------------------------------------------------- const S32 SCRIPT_DIALOG_MAX_BUTTONS = 12; const S32 SCRIPT_DIALOG_BUTTON_STR_SIZE = 24; const S32 SCRIPT_DIALOG_MAX_MESSAGE_SIZE = 512; const char* SCRIPT_DIALOG_HEADER = "Script Dialog:\n"; bool callback_script_dialog(const LLSD& notification, const LLSD& response) { LLNotificationForm form(notification["form"]); std::string button = LLNotification::getSelectedOptionName(response); S32 button_idx = LLNotification::getSelectedOption(notification, response); // Didn't click "Ignore" if (button_idx != -1) { LLMessageSystem* msg = gMessageSystem; msg->newMessage("ScriptDialogReply"); msg->nextBlock("AgentData"); msg->addUUID("AgentID", gAgent.getID()); msg->addUUID("SessionID", gAgent.getSessionID()); msg->nextBlock("Data"); msg->addUUID("ObjectID", notification["payload"]["object_id"].asUUID()); msg->addS32("ChatChannel", notification["payload"]["chat_channel"].asInteger()); msg->addS32("ButtonIndex", button_idx); msg->addString("ButtonLabel", button); msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); } return false; } static LLNotificationFunctorRegistration callback_script_dialog_reg_1("ScriptDialog", callback_script_dialog); static LLNotificationFunctorRegistration callback_script_dialog_reg_2("ScriptDialogGroup", callback_script_dialog); void process_script_dialog(LLMessageSystem* msg, void**) { S32 i; LLSD payload; LLUUID object_id; msg->getUUID("Data", "ObjectID", object_id); if (LLMuteList::getInstance()->isMuted(object_id)) { return; } std::string message; std::string first_name; std::string last_name; std::string title; S32 chat_channel; msg->getString("Data", "FirstName", first_name); msg->getString("Data", "LastName", last_name); msg->getString("Data", "ObjectName", title); msg->getString("Data", "Message", message); msg->getS32("Data", "ChatChannel", chat_channel); // unused for now LLUUID image_id; msg->getUUID("Data", "ImageID", image_id); payload["sender"] = msg->getSender().getIPandPort(); payload["object_id"] = object_id; payload["chat_channel"] = chat_channel; // build up custom form S32 button_count = msg->getNumberOfBlocks("Buttons"); if (button_count > SCRIPT_DIALOG_MAX_BUTTONS) { llwarns << "Too many script dialog buttons - omitting some" << llendl; button_count = SCRIPT_DIALOG_MAX_BUTTONS; } LLNotificationForm form; for (i = 0; i < button_count; i++) { std::string tdesc; msg->getString("Buttons", "ButtonLabel", tdesc, i); form.addElement("button", std::string(tdesc)); } LLSD args; args["TITLE"] = title; args["MESSAGE"] = message; LLNotificationPtr notification; if (!first_name.empty()) { args["FIRST"] = first_name; args["LAST"] = last_name; notification = LLNotifications::instance().add( LLNotification::Params("ScriptDialog").substitutions(args).payload(payload).form_elements(form.asLLSD())); } else { args["GROUPNAME"] = last_name; notification = LLNotifications::instance().add( LLNotification::Params("ScriptDialogGroup").substitutions(args).payload(payload).form_elements(form.asLLSD())); } } //--------------------------------------------------------------------------- std::vector gLoadUrlList; bool callback_load_url(const LLSD& notification, const LLSD& response) { S32 option = LLNotification::getSelectedOption(notification, response); if (0 == option) { LLWeb::loadURL(notification["payload"]["url"].asString()); } return false; } static LLNotificationFunctorRegistration callback_load_url_reg("LoadWebPage", callback_load_url); // We've got the name of the person who owns the object hurling the url. // Display confirmation dialog. void callback_load_url_name(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* data) { std::vector::iterator it; for (it = gLoadUrlList.begin(); it != gLoadUrlList.end(); ) { LLSD load_url_info = *it; if (load_url_info["owner_id"].asUUID() == id) { it = gLoadUrlList.erase(it); std::string owner_name; if (is_group) { owner_name = first + " (group)"; } else { owner_name = first + " " + last; } // For legacy name-only mutes. if (LLMuteList::getInstance()->isMuted(LLUUID::null, owner_name)) { continue; } LLSD args; args["URL"] = load_url_info["url"].asString(); args["MESSAGE"] = load_url_info["message"].asString();; args["OBJECTNAME"] = load_url_info["object_name"].asString(); args["NAME"] = owner_name; LLNotifications::instance().add("LoadWebPage", args, load_url_info); } else { ++it; } } } void process_load_url(LLMessageSystem* msg, void**) { LLUUID object_id; LLUUID owner_id; BOOL owner_is_group; char object_name[256]; /* Flawfinder: ignore */ char message[256]; /* Flawfinder: ignore */ char url[256]; /* Flawfinder: ignore */ msg->getString("Data", "ObjectName", 256, object_name); msg->getUUID( "Data", "ObjectID", object_id); msg->getUUID( "Data", "OwnerID", owner_id); msg->getBOOL( "Data", "OwnerIsGroup", owner_is_group); msg->getString("Data", "Message", 256, message); msg->getString("Data", "URL", 256, url); LLSD payload; payload["object_id"] = object_id; payload["owner_id"] = owner_id; payload["owner_is_group"] = owner_is_group; payload["object_name"] = object_name; payload["message"] = message; payload["url"] = url; // URL is safety checked in load_url above // Check if object or owner is muted if (LLMuteList::getInstance()->isMuted(object_id, object_name) || LLMuteList::getInstance()->isMuted(owner_id)) { LL_INFOS("Messaging")<<"Ignoring load_url from muted object/owner."<getNameFromUUID(owner_id, owner_is_group, callback_load_url_name); } void callback_download_complete(void** data, S32 result, LLExtStat ext_status) { std::string* filepath = (std::string*)data; LLSD args; args["DOWNLOAD_PATH"] = *filepath; LLNotifications::instance().add("FinishedRawDownload", args); delete filepath; } void process_initiate_download(LLMessageSystem* msg, void**) { LLUUID agent_id; msg->getUUID("AgentData", "AgentID", agent_id); if (agent_id != gAgent.getID()) { LL_WARNS("Messaging") << "Initiate download for wrong agent" << LL_ENDL; return; } std::string sim_filename; std::string viewer_filename; msg->getString("FileData", "SimFilename", sim_filename); msg->getString("FileData", "ViewerFilename", viewer_filename); if (!gXferManager->validateFileForRequest(viewer_filename)) { llwarns << "SECURITY: Unauthorized download to local file " << viewer_filename << llendl; return; } gXferManager->requestFile(viewer_filename, sim_filename, LL_PATH_NONE, msg->getSender(), FALSE, // don't delete remote callback_download_complete, (void**)new std::string(viewer_filename)); } void process_script_teleport_request(LLMessageSystem* msg, void**) { std::string object_name; std::string sim_name; LLVector3 pos; LLVector3 look_at; msg->getString("Data", "ObjectName", object_name); msg->getString("Data", "SimName", sim_name); msg->getVector3("Data", "SimPosition", pos); msg->getVector3("Data", "LookAt", look_at); gFloaterWorldMap->trackURL(sim_name, (S32)pos.mV[VX], (S32)pos.mV[VY], (S32)pos.mV[VZ]); LLFloaterWorldMap::show(NULL, TRUE); // remove above two lines and replace with below line // to re-enable parcel browser for llMapDestination() // LLURLDispatcher::dispatch(LLURLDispatcher::buildSLURL(sim_name, (S32)pos.mV[VX], (S32)pos.mV[VY], (S32)pos.mV[VZ]), FALSE); } void process_covenant_reply(LLMessageSystem* msg, void**) { LLUUID covenant_id, estate_owner_id; std::string estate_name; U32 covenant_timestamp; msg->getUUID("Data", "CovenantID", covenant_id); msg->getU32("Data", "CovenantTimestamp", covenant_timestamp); msg->getString("Data", "EstateName", estate_name); msg->getUUID("Data", "EstateOwnerID", estate_owner_id); LLPanelEstateCovenant::updateEstateName(estate_name); LLPanelLandCovenant::updateEstateName(estate_name); LLFloaterBuyLand::updateEstateName(estate_name); // standard message, not from system std::string last_modified; if (covenant_timestamp == 0) { last_modified = LLTrans::getString("covenant_never_modified"); } else { last_modified = LLTrans::getString("covenant_modified") + " " + formatted_time((time_t)covenant_timestamp); } LLPanelEstateCovenant::updateLastModified(last_modified); LLPanelLandCovenant::updateLastModified(last_modified); LLFloaterBuyLand::updateLastModified(last_modified); // Estates can't be owned by groups BOOL is_group = FALSE; gCacheName->getNameFromUUID(estate_owner_id, is_group, callbackCacheEstateOwnerName); // load the actual covenant asset data const BOOL high_priority = TRUE; if (covenant_id.notNull()) { gAssetStorage->getEstateAsset(gAgent.getRegionHost(), gAgent.getID(), gAgent.getSessionID(), covenant_id, LLAssetType::AT_NOTECARD, ET_Covenant, onCovenantLoadComplete, NULL, high_priority); } else { std::string covenant_text; if (estate_owner_id.isNull()) { // mainland covenant_text = "There is no Covenant provided for this Estate."; } else { covenant_text = "There is no Covenant provided for this Estate. The land on this estate is being sold by the Estate owner, not Linden Lab. Please contact the Estate Owner for sales details."; } LLPanelEstateCovenant::updateCovenantText(covenant_text, covenant_id); LLPanelLandCovenant::updateCovenantText(covenant_text); LLFloaterBuyLand::updateCovenantText(covenant_text, covenant_id); } } void callbackCacheEstateOwnerName(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void*) { std::string name; if (id.isNull()) { name = "(none)"; } else { name = first + " " + last; } LLPanelEstateCovenant::updateEstateOwnerName(name); LLPanelLandCovenant::updateEstateOwnerName(name); LLFloaterBuyLand::updateEstateOwnerName(name); } void onCovenantLoadComplete(LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status) { LL_DEBUGS("Messaging") << "onCovenantLoadComplete()" << LL_ENDL; std::string covenant_text; if(0 == status) { LLVFile file(vfs, asset_uuid, type, LLVFile::READ); S32 file_length = file.getSize(); std::vector buffer(file_length+1); file.read((U8*)&buffer[0], file_length); // put a EOS at the end buffer[file_length] = '\0'; if( (file_length > 19) && !strncmp( &buffer[0], "Linden text version", 19 ) ) { LLViewerTextEditor* editor = new LLViewerTextEditor(std::string("temp"), LLRect(0,0,0,0), file_length+1); if( !editor->importBuffer( &buffer[0], file_length+1 ) ) { LL_WARNS("Messaging") << "Problem importing estate covenant." << LL_ENDL; covenant_text = "Problem importing estate covenant."; } else { // Version 0 (just text, doesn't include version number) covenant_text = editor->getText(); } delete editor; } else { LL_WARNS("Messaging") << "Problem importing estate covenant: Covenant file format error." << LL_ENDL; covenant_text = "Problem importing estate covenant: Covenant file format error."; } } else { LLViewerStats::getInstance()->incStat( LLViewerStats::ST_DOWNLOAD_FAILED ); if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status || LL_ERR_FILE_EMPTY == status) { covenant_text = "Estate covenant notecard is missing from database."; } else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status) { covenant_text = "Insufficient permissions to view estate covenant."; } else { covenant_text = "Unable to load estate covenant at this time."; } LL_WARNS("Messaging") << "Problem loading notecard: " << status << LL_ENDL; } LLPanelEstateCovenant::updateCovenantText(covenant_text, asset_uuid); LLPanelLandCovenant::updateCovenantText(covenant_text); LLFloaterBuyLand::updateCovenantText(covenant_text, asset_uuid); } void process_feature_disabled_message(LLMessageSystem* msg, void**) { // Handle Blacklisted feature simulator response... LLUUID agentID; LLUUID transactionID; std::string messageText; msg->getStringFast(_PREHASH_FailureInfo,_PREHASH_ErrorMessage, messageText,0); msg->getUUIDFast(_PREHASH_FailureInfo,_PREHASH_AgentID,agentID); msg->getUUIDFast(_PREHASH_FailureInfo,_PREHASH_TransactionID,transactionID); LL_WARNS("Messaging") << "Blacklisted Feature Response:" << messageText << LL_ENDL; } // ------------------------------------------------------------ // Message system exception callbacks // ------------------------------------------------------------ void invalid_message_callback(LLMessageSystem* msg, void*, EMessageException exception) { LLAppViewer::instance()->badNetworkHandler(); } // Please do not add more message handlers here. This file is huge. // Put them in a file related to the functionality you are implementing. void LLOfferInfo::forceResponse(InventoryOfferResponse response) { LLNotification::Params params("UserGiveItem"); params.functor(boost::bind(&LLOfferInfo::inventory_offer_callback, this, _1, _2)); LLNotifications::instance().forceResponse(params, response); }