Files
SingularityViewer/indra/newview/llgroupmgr.cpp
Lirusaito 2c48161595 Make Baker's Group code compile! Thanks Shyotl~
Note: Large groups still don't load?!
SimConsole cap is needed for Sim Console, won't work without it, keeping it in!
..But it should work with the new ASync one? Maybe needs tweaks, first?

Also switches opening IMs to the gCacheName->getFullName(id, name) system mentioned earlier, my connection has improved!
And makes the Avatar Picker show display names! Yay! (Rearranged the includes there, alphabetical~)
Also fallback to gCacheName->getFullName(id, name) if getting name fails in the new system.. hopefully this will combat occasional big failures.. but it may not work anyway.. maybe my connection is just awful again
2012-12-04 11:54:54 -05:00

2216 lines
60 KiB
C++

/**
* @file llgroupmgr.cpp
* @brief LLGroupMgr class implementation
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
/**
* Manager for aggregating all client knowledge for specific groups
* Keeps a cache of group information.
*/
#include "llviewerprecompiledheaders.h"
#include "llgroupmgr.h"
#include <vector>
#include <algorithm>
#include "llappviewer.h" //For gFrameCount
#include "llagent.h"
#include "llui.h"
#include "message.h"
#include "roles_constants.h"
#include "lltransactiontypes.h"
#include "llstatusbar.h"
#include "lleconomy.h"
#include "llviewercontrol.h"
#include "llviewerregion.h"
#include "llviewerwindow.h"
#include "llfloaterdirectory.h"
#include "llfloatergroupinfo.h"
#include "llnotificationsutil.h"
#include "lluictrlfactory.h"
#include "lltrans.h"
#include <boost/regex.hpp>
#if LL_MSVC
#pragma warning(push)
// disable boost::lexical_cast warning
#pragma warning (disable:4702)
#endif
#include <boost/lexical_cast.hpp>
#if LL_MSVC
#pragma warning(pop) // Restore all warnings to the previous state
#endif
const U32 MAX_CACHED_GROUPS = 10;
//
// LLRoleActionSet
//
LLRoleActionSet::LLRoleActionSet()
: mActionSetData(NULL)
{ }
LLRoleActionSet::~LLRoleActionSet()
{
delete mActionSetData;
std::for_each(mActions.begin(), mActions.end(), DeletePointer());
}
//
// LLGroupMemberData
//
LLGroupMemberData::LLGroupMemberData(const LLUUID& id,
S32 contribution,
U64 agent_powers,
const std::string& title,
const std::string& online_status,
BOOL is_owner) :
mID(id),
mContribution(contribution),
mAgentPowers(agent_powers),
mTitle(title),
mOnlineStatus(online_status),
mIsOwner(is_owner)
{
}
LLGroupMemberData::~LLGroupMemberData()
{
}
void LLGroupMemberData::addRole(const LLUUID& role, LLGroupRoleData* rd)
{
mRolesList[role] = rd;
}
bool LLGroupMemberData::removeRole(const LLUUID& role)
{
role_list_t::iterator it = mRolesList.find(role);
if (it != mRolesList.end())
{
mRolesList.erase(it);
return true;
}
return false;
}
//
// LLGroupRoleData
//
LLGroupRoleData::LLGroupRoleData(const LLUUID& role_id,
const std::string& role_name,
const std::string& role_title,
const std::string& role_desc,
const U64 role_powers,
const S32 member_count) :
mRoleID(role_id),
mMemberCount(member_count),
mMembersNeedsSort(FALSE)
{
mRoleData.mRoleName = role_name;
mRoleData.mRoleTitle = role_title;
mRoleData.mRoleDescription = role_desc;
mRoleData.mRolePowers = role_powers;
mRoleData.mChangeType = RC_UPDATE_NONE;
}
LLGroupRoleData::LLGroupRoleData(const LLUUID& role_id,
LLRoleData role_data,
const S32 member_count) :
mRoleID(role_id),
mRoleData(role_data),
mMemberCount(member_count),
mMembersNeedsSort(FALSE)
{
}
LLGroupRoleData::~LLGroupRoleData()
{
}
S32 LLGroupRoleData::getMembersInRole(uuid_vec_t members,
BOOL needs_sort)
{
if (mRoleID.isNull())
{
// This is the everyone role, just return the size of members,
// because everyone is in the everyone role.
return members.size();
}
// Sort the members list, if needed.
if (mMembersNeedsSort)
{
std::sort(mMemberIDs.begin(), mMemberIDs.end());
mMembersNeedsSort = FALSE;
}
if (needs_sort)
{
// Sort the members parameter.
std::sort(members.begin(), members.end());
}
// Return the number of members in the intersection.
S32 max_size = llmin( members.size(), mMemberIDs.size() );
uuid_vec_t in_role( max_size );
uuid_vec_t::iterator in_role_end;
in_role_end = std::set_intersection(mMemberIDs.begin(), mMemberIDs.end(),
members.begin(), members.end(),
in_role.begin());
return in_role_end - in_role.begin();
}
void LLGroupRoleData::addMember(const LLUUID& member)
{
mMembersNeedsSort = TRUE;
mMemberIDs.push_back(member);
}
bool LLGroupRoleData::removeMember(const LLUUID& member)
{
uuid_vec_t::iterator it = std::find(mMemberIDs.begin(),mMemberIDs.end(),member);
if (it != mMemberIDs.end())
{
mMembersNeedsSort = TRUE;
mMemberIDs.erase(it);
return true;
}
return false;
}
void LLGroupRoleData::clearMembers()
{
mMembersNeedsSort = FALSE;
mMemberIDs.clear();
}
//
// LLGroupMgrGroupData
//
LLGroupMgrGroupData::LLGroupMgrGroupData(const LLUUID& id) :
mID(id),
mShowInList(TRUE),
mOpenEnrollment(FALSE),
mMembershipFee(0),
mAllowPublish(FALSE),
mMaturePublish(FALSE),
mChanged(FALSE),
mMemberCount(0),
mRoleCount(0),
mReceivedRoleMemberPairs(0),
mMemberDataComplete(FALSE),
mRoleDataComplete(FALSE),
mRoleMemberDataComplete(FALSE),
mGroupPropertiesDataComplete(FALSE),
mPendingRoleMemberRequest(FALSE)
{
}
BOOL LLGroupMgrGroupData::getRoleData(const LLUUID& role_id, LLRoleData& role_data)
{
role_data_map_t::const_iterator it;
// Do we have changes for it?
it = mRoleChanges.find(role_id);
if (it != mRoleChanges.end())
{
if ((*it).second.mChangeType == RC_DELETE) return FALSE;
role_data = (*it).second;
return TRUE;
}
// Ok, no changes, hasn't been deleted, isn't a new role, just find the role.
role_list_t::const_iterator rit = mRoles.find(role_id);
if (rit != mRoles.end())
{
role_data = (*rit).second->getRoleData();
return TRUE;
}
// This role must not exist.
return FALSE;
}
void LLGroupMgrGroupData::setRoleData(const LLUUID& role_id, LLRoleData role_data)
{
// If this is a newly created group, we need to change the data in the created list.
role_data_map_t::iterator it;
it = mRoleChanges.find(role_id);
if (it != mRoleChanges.end())
{
if ((*it).second.mChangeType == RC_CREATE)
{
role_data.mChangeType = RC_CREATE;
mRoleChanges[role_id] = role_data;
return;
}
else if ((*it).second.mChangeType == RC_DELETE)
{
// Don't do anything for a role being deleted.
return;
}
}
// Not a new role, so put it in the changes list.
LLRoleData old_role_data;
role_list_t::iterator rit = mRoles.find(role_id);
if (rit != mRoles.end())
{
bool data_change = ( ((*rit).second->mRoleData.mRoleDescription != role_data.mRoleDescription)
|| ((*rit).second->mRoleData.mRoleName != role_data.mRoleName)
|| ((*rit).second->mRoleData.mRoleTitle != role_data.mRoleTitle) );
bool powers_change = ((*rit).second->mRoleData.mRolePowers != role_data.mRolePowers);
if (!data_change && !powers_change)
{
// We are back to the original state, the changes have been 'undone' so take out the change.
mRoleChanges.erase(role_id);
return;
}
if (data_change && powers_change)
{
role_data.mChangeType = RC_UPDATE_ALL;
}
else if (data_change)
{
role_data.mChangeType = RC_UPDATE_DATA;
}
else
{
role_data.mChangeType = RC_UPDATE_POWERS;
}
mRoleChanges[role_id] = role_data;
}
else
{
llwarns << "Change being made to non-existant role " << role_id << llendl;
}
}
BOOL LLGroupMgrGroupData::pendingRoleChanges()
{
return (!mRoleChanges.empty());
}
// This is a no-op if the role has already been created.
void LLGroupMgrGroupData::createRole(const LLUUID& role_id, LLRoleData role_data)
{
if (mRoleChanges.find(role_id) != mRoleChanges.end())
{
llwarns << "create role for existing role! " << role_id << llendl;
}
else
{
role_data.mChangeType = RC_CREATE;
mRoleChanges[role_id] = role_data;
}
}
void LLGroupMgrGroupData::deleteRole(const LLUUID& role_id)
{
role_data_map_t::iterator it;
// If this was a new role, just discard it.
it = mRoleChanges.find(role_id);
if (it != mRoleChanges.end()
&& (*it).second.mChangeType == RC_CREATE)
{
mRoleChanges.erase(it);
return;
}
LLRoleData rd;
rd.mChangeType = RC_DELETE;
mRoleChanges[role_id] = rd;
}
void LLGroupMgrGroupData::addRolePower(const LLUUID &role_id, U64 power)
{
LLRoleData rd;
if (getRoleData(role_id,rd))
{
rd.mRolePowers |= power;
setRoleData(role_id,rd);
}
else
{
llwarns << "addRolePower: no role data found for " << role_id << llendl;
}
}
void LLGroupMgrGroupData::removeRolePower(const LLUUID &role_id, U64 power)
{
LLRoleData rd;
if (getRoleData(role_id,rd))
{
rd.mRolePowers &= ~power;
setRoleData(role_id,rd);
}
else
{
llwarns << "removeRolePower: no role data found for " << role_id << llendl;
}
}
U64 LLGroupMgrGroupData::getRolePowers(const LLUUID& role_id)
{
LLRoleData rd;
if (getRoleData(role_id,rd))
{
return rd.mRolePowers;
}
else
{
llwarns << "getRolePowers: no role data found for " << role_id << llendl;
return GP_NO_POWERS;
}
}
void LLGroupMgrGroupData::removeData()
{
// Remove member data first, because removeRoleData will walk the member list
removeMemberData();
removeRoleData();
}
void LLGroupMgrGroupData::removeMemberData()
{
for (member_list_t::iterator mi = mMembers.begin(); mi != mMembers.end(); ++mi)
{
delete mi->second;
}
mMembers.clear();
mMemberDataComplete = FALSE;
}
void LLGroupMgrGroupData::removeRoleData()
{
for (member_list_t::iterator mi = mMembers.begin(); mi != mMembers.end(); ++mi)
{
LLGroupMemberData* data = mi->second;
if (data)
{
data->clearRoles();
}
}
for (role_list_t::iterator ri = mRoles.begin(); ri != mRoles.end(); ++ri)
{
LLGroupRoleData* data = ri->second;
delete data;
}
mRoles.clear();
mReceivedRoleMemberPairs = 0;
mRoleDataComplete = FALSE;
mRoleMemberDataComplete = FALSE;
}
void LLGroupMgrGroupData::removeRoleMemberData()
{
for (member_list_t::iterator mi = mMembers.begin(); mi != mMembers.end(); ++mi)
{
LLGroupMemberData* data = mi->second;
if (data)
{
data->clearRoles();
}
}
for (role_list_t::iterator ri = mRoles.begin(); ri != mRoles.end(); ++ri)
{
LLGroupRoleData* data = ri->second;
if (data)
{
data->clearMembers();
}
}
mReceivedRoleMemberPairs = 0;
mRoleMemberDataComplete = FALSE;
}
LLGroupMgrGroupData::~LLGroupMgrGroupData()
{
removeData();
}
bool LLGroupMgrGroupData::changeRoleMember(const LLUUID& role_id,
const LLUUID& member_id,
LLRoleMemberChangeType rmc)
{
role_list_t::iterator ri = mRoles.find(role_id);
member_list_t::iterator mi = mMembers.find(member_id);
if (ri == mRoles.end()
|| mi == mMembers.end() )
{
if (ri == mRoles.end()) llwarns << "LLGroupMgrGroupData::changeRoleMember couldn't find role " << role_id << llendl;
if (mi == mMembers.end()) llwarns << "LLGroupMgrGroupData::changeRoleMember couldn't find member " << member_id << llendl;
return false;
}
LLGroupRoleData* grd = ri->second;
LLGroupMemberData* gmd = mi->second;
if (!grd || !gmd)
{
llwarns << "LLGroupMgrGroupData::changeRoleMember couldn't get member or role data." << llendl;
return false;
}
if (RMC_ADD == rmc)
{
llinfos << " adding member to role." << llendl;
grd->addMember(member_id);
gmd->addRole(role_id,grd);
//TODO move this into addrole function
//see if they added someone to the owner role and update isOwner
gmd->mIsOwner = (role_id == mOwnerRole) ? TRUE : gmd->mIsOwner;
}
else if (RMC_REMOVE == rmc)
{
llinfos << " removing member from role." << llendl;
grd->removeMember(member_id);
gmd->removeRole(role_id);
//see if they removed someone from the owner role and update isOwner
gmd->mIsOwner = (role_id == mOwnerRole) ? FALSE : gmd->mIsOwner;
}
lluuid_pair role_member;
role_member.first = role_id;
role_member.second = member_id;
change_map_t::iterator it = mRoleMemberChanges.find(role_member);
if (it != mRoleMemberChanges.end())
{
// There was already a role change for this role_member
if (it->second.mChange == rmc)
{
// Already recorded this change? Weird.
llinfos << "Received duplicate change for "
<< " role: " << role_id << " member " << member_id
<< " change " << (rmc == RMC_ADD ? "ADD" : "REMOVE") << llendl;
}
else
{
// The only two operations (add and remove) currently cancel each other out
// If that changes this will need more logic
if (rmc == RMC_NONE)
{
llwarns << "changeRoleMember: existing entry with 'RMC_NONE' change! This shouldn't happen." << llendl;
LLRoleMemberChange rc(role_id,member_id,rmc);
mRoleMemberChanges[role_member] = rc;
}
else
{
mRoleMemberChanges.erase(it);
}
}
}
else
{
LLRoleMemberChange rc(role_id,member_id,rmc);
mRoleMemberChanges[role_member] = rc;
}
recalcAgentPowers(member_id);
mChanged = TRUE;
return true;
}
void LLGroupMgrGroupData::recalcAllAgentPowers()
{
LLGroupMemberData* gmd;
for (member_list_t::iterator mit = mMembers.begin();
mit != mMembers.end(); ++mit)
{
gmd = mit->second;
if (!gmd) continue;
gmd->mAgentPowers = 0;
for (LLGroupMemberData::role_list_t::iterator it = gmd->mRolesList.begin();
it != gmd->mRolesList.end(); ++it)
{
LLGroupRoleData* grd = (*it).second;
if (!grd) continue;
gmd->mAgentPowers |= grd->mRoleData.mRolePowers;
}
}
}
void LLGroupMgrGroupData::recalcAgentPowers(const LLUUID& agent_id)
{
member_list_t::iterator mi = mMembers.find(agent_id);
if (mi == mMembers.end()) return;
LLGroupMemberData* gmd = mi->second;
if (!gmd) return;
gmd->mAgentPowers = 0;
for (LLGroupMemberData::role_list_t::iterator it = gmd->mRolesList.begin();
it != gmd->mRolesList.end(); ++it)
{
LLGroupRoleData* grd = (*it).second;
if (!grd) continue;
gmd->mAgentPowers |= grd->mRoleData.mRolePowers;
}
}
bool packRoleUpdateMessageBlock(LLMessageSystem* msg,
const LLUUID& group_id,
const LLUUID& role_id,
const LLRoleData& role_data,
bool start_message)
{
if (start_message)
{
msg->newMessage("GroupRoleUpdate");
msg->nextBlock("AgentData");
msg->addUUID("AgentID",gAgent.getID());
msg->addUUID("SessionID",gAgent.getSessionID());
msg->addUUID("GroupID",group_id);
start_message = false;
}
msg->nextBlock("RoleData");
msg->addUUID("RoleID",role_id);
msg->addString("Name", role_data.mRoleName);
msg->addString("Description", role_data.mRoleDescription);
msg->addString("Title", role_data.mRoleTitle);
msg->addU64("Powers", role_data.mRolePowers);
msg->addU8("UpdateType", (U8)role_data.mChangeType);
if (msg->isSendFullFast())
{
gAgent.sendReliableMessage();
start_message = true;
}
return start_message;
}
#if SHY_MOD //Group Title script access
#include "shcommandhandler.h"
class SH_GroupRoleChanger
{
std::pair<LLUUID, std::string> gForceRole;
typedef std::map<const bool,std::string> title_map_t;
typedef std::map<std::string,title_map_t> role_map_t;
typedef std::map<const LLUUID,role_map_t> group_map_t;
//group_map_t = <GroupID,<Rolename,<Title/Desc,text>>>
group_map_t gPendingRoleChanges; //lol
public:
virtual ~SH_GroupRoleChanger(){}
void CheckUpdateRole(const LLUUID &group_id,LLGroupMgrGroupData::role_list_t &roles)
{
group_map_t::iterator group_it = gPendingRoleChanges.find(group_id);
if(group_it != gPendingRoleChanges.end()) //valid group
{
for(LLGroupMgrGroupData::role_list_t::iterator roles_it = roles.begin();roles_it!=roles.end();++roles_it)
{
//bool start_message = true;
LLGroupRoleData &role = *(roles_it->second);
LLRoleData data = role.getRoleData();
role_map_t::iterator stored_role_it = group_it->second.find(data.mRoleName);
if(stored_role_it != group_it->second.end())//found matching role.
{
if(!(stored_role_it->second.empty()))
{
title_map_t::iterator entry_it = stored_role_it->second.begin();
for(;entry_it!=stored_role_it->second.end();++entry_it)
{
if(entry_it->first == false)
data.mRoleDescription = entry_it->second;
else
data.mRoleTitle = entry_it->second;
}
if(gForceRole.first == group_id && gForceRole.second == data.mRoleName)
{
if(gAgent.getGroupID() != group_id)
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_ActivateGroup);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->addUUIDFast(_PREHASH_GroupID, group_id);
gAgent.sendReliableMessage(); //Set group.
}
LLGroupMgr::getInstance()->sendGroupTitleUpdate(group_id,role.getID());
}
LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_id);
gdatap->setRoleData(role.getID(),data);
}
}
}
gPendingRoleChanges.erase(group_it);
}
LLGroupMgr::getInstance()->sendGroupRoleChanges(group_id); //get roles list from groupid
gForceRole.first = LLUUID::null;
}
void RequestUpdateRole(const LLUUID& group_id, const std::string &rolename, bool title, const std::string &string, bool force = false) //if !title then desc.
{
if(force)
{
gForceRole.first = group_id;
gForceRole.second = rolename;
}
gPendingRoleChanges[group_id][rolename][title]=string;
LLGroupMgr::getInstance()->sendGroupRoleDataRequest(group_id); //get roles list from groupid
}
}gGroupRoleChanger;
CMD_SCRIPT(setroletext)
{
const LLUUID group_id = args[1].asUUID();
const std::string rolename = args[2].asString();
const bool title = args[3].asInteger() == 1;
const std::string str = args[4].asString();
const bool force = args[5].asInteger() == 1;
llinfos<<"Updating role- group_id:"<<group_id<<" rolename:"<<rolename<<" title:"<<title<<" string:"<<str<<llendl;
gGroupRoleChanger.RequestUpdateRole(group_id,rolename,title,str,force);
}
#endif //shy_mod
void LLGroupMgrGroupData::sendRoleChanges()
{
// Commit changes locally
LLGroupRoleData* grd;
role_list_t::iterator role_it;
LLMessageSystem* msg = gMessageSystem;
bool start_message = true;
bool need_role_cleanup = false;
bool need_role_data = false;
bool need_power_recalc = false;
// Apply all changes
for (role_data_map_t::iterator iter = mRoleChanges.begin();
iter != mRoleChanges.end(); )
{
role_data_map_t::iterator it = iter++; // safely incrament iter
const LLUUID& role_id = (*it).first;
const LLRoleData& role_data = (*it).second;
// Commit to local data set
role_it = mRoles.find((*it).first);
if ( (mRoles.end() == role_it
&& RC_CREATE != role_data.mChangeType)
|| (mRoles.end() != role_it
&& RC_CREATE == role_data.mChangeType))
{
continue;
}
// NOTE: role_it is valid EXCEPT for the RC_CREATE case
switch (role_data.mChangeType)
{
case RC_CREATE:
{
// NOTE: role_it is NOT valid in this case
grd = new LLGroupRoleData(role_id, role_data, 0);
mRoles[role_id] = grd;
need_role_data = true;
break;
}
case RC_DELETE:
{
LLGroupRoleData* group_role_data = (*role_it).second;
delete group_role_data;
mRoles.erase(role_it);
need_role_cleanup = true;
need_power_recalc = true;
break;
}
case RC_UPDATE_ALL:
case RC_UPDATE_POWERS:
need_power_recalc = true;
case RC_UPDATE_DATA:
default:
{
LLGroupRoleData* group_role_data = (*role_it).second;
group_role_data->setRoleData(role_data); // NOTE! might modify mRoleChanges!
break;
}
}
// Update dataserver
start_message = packRoleUpdateMessageBlock(msg,getID(),role_id,role_data,start_message);
}
if (!start_message)
{
gAgent.sendReliableMessage();
}
// If we delete a role then all the role-member pairs are invalid!
if (need_role_cleanup)
{
removeRoleMemberData();
}
// If we create a new role, then we need to re-fetch all the role data.
if (need_role_data)
{
LLGroupMgr::getInstance()->sendGroupRoleDataRequest(getID());
}
// Clean up change lists
mRoleChanges.clear();
// Recalculate all the agent powers because role powers have now changed.
if (need_power_recalc)
{
recalcAllAgentPowers();
}
}
void LLGroupMgrGroupData::cancelRoleChanges()
{
// Clear out all changes!
mRoleChanges.clear();
}
//
// LLGroupMgr
//
LLGroupMgr::LLGroupMgr()
{
mLastGroupMembersRequestFrame = 0;
}
LLGroupMgr::~LLGroupMgr()
{
clearGroups();
}
void LLGroupMgr::clearGroups()
{
std::for_each(mRoleActionSets.begin(), mRoleActionSets.end(), DeletePointer());
mRoleActionSets.clear();
std::for_each(mGroups.begin(), mGroups.end(), DeletePairedPointer());
mGroups.clear();
mObservers.clear();
}
void LLGroupMgr::clearGroupData(const LLUUID& group_id)
{
group_map_t::iterator iter = mGroups.find(group_id);
if (iter != mGroups.end())
{
delete (*iter).second;
mGroups.erase(iter);
}
}
void LLGroupMgr::addObserver(LLGroupMgrObserver* observer)
{
if( observer->getID() != LLUUID::null )
mObservers.insert(std::pair<LLUUID, LLGroupMgrObserver*>(observer->getID(), observer));
}
void LLGroupMgr::addObserver(const LLUUID& group_id, LLParticularGroupObserver* observer)
{
if(group_id.notNull() && observer)
{
mParticularObservers[group_id].insert(observer);
}
}
void LLGroupMgr::removeObserver(LLGroupMgrObserver* observer)
{
if (!observer)
{
return;
}
observer_multimap_t::iterator it;
it = mObservers.find(observer->getID());
while (it != mObservers.end())
{
if (it->second == observer)
{
mObservers.erase(it);
break;
}
else
{
++it;
}
}
}
void LLGroupMgr::removeObserver(const LLUUID& group_id, LLParticularGroupObserver* observer)
{
if(group_id.isNull() || !observer)
{
return;
}
observer_map_t::iterator obs_it = mParticularObservers.find(group_id);
if(obs_it == mParticularObservers.end())
return;
obs_it->second.erase(observer);
if (obs_it->second.size() == 0)
mParticularObservers.erase(obs_it);
}
LLGroupMgrGroupData* LLGroupMgr::getGroupData(const LLUUID& id)
{
group_map_t::iterator gi = mGroups.find(id);
if (gi != mGroups.end())
{
return gi->second;
}
return NULL;
}
// Helper function for LLGroupMgr::processGroupMembersReply
// This reformats date strings from MM/DD/YYYY to YYYY/MM/DD ( e.g. 1/27/2008 -> 2008/1/27 )
// so that the sorter can sort by year before month before day.
static void formatDateString(std::string &date_string)
{
tm t;
if (sscanf(date_string.c_str(), "%u/%u/%u", &t.tm_mon, &t.tm_mday, &t.tm_year) == 3 && t.tm_year > 1900)
{
t.tm_year -= 1900;
t.tm_mon--;
t.tm_hour = t.tm_min = t.tm_sec = 0;
timeStructToFormattedString(&t, gSavedSettings.getString("ShortDateFormat"), date_string);
}
}
// static
void LLGroupMgr::processGroupMembersReply(LLMessageSystem* msg, void** data)
{
lldebugs << "LLGroupMgr::processGroupMembersReply" << llendl;
LLUUID agent_id;
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
if (gAgent.getID() != agent_id)
{
llwarns << "Got group members reply for another agent!" << llendl;
return;
}
LLUUID group_id;
msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id );
LLUUID request_id;
msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_RequestID, request_id);
LLGroupMgrGroupData* group_datap = LLGroupMgr::getInstance()->createGroupData(group_id);
if (!group_datap || (group_datap->mMemberRequestID != request_id))
{
llwarns << "processGroupMembersReply: Received incorrect (stale?) group or request id" << llendl;
return;
}
msg->getS32(_PREHASH_GroupData, "MemberCount", group_datap->mMemberCount );
if (group_datap->mMemberCount > 0)
{
S32 contribution = 0;
std::string online_status;
std::string title;
U64 agent_powers = 0;
BOOL is_owner = FALSE;
S32 num_members = msg->getNumberOfBlocksFast(_PREHASH_MemberData);
for (S32 i = 0; i < num_members; i++)
{
LLUUID member_id;
msg->getUUIDFast(_PREHASH_MemberData, _PREHASH_AgentID, member_id, i );
msg->getS32(_PREHASH_MemberData, _PREHASH_Contribution, contribution, i);
msg->getU64(_PREHASH_MemberData, "AgentPowers", agent_powers, i);
msg->getStringFast(_PREHASH_MemberData, _PREHASH_OnlineStatus, online_status, i);
msg->getString(_PREHASH_MemberData, "Title", title, i);
msg->getBOOL(_PREHASH_MemberData,"IsOwner",is_owner,i);
if (member_id.notNull())
{
if (online_status == "Online")
{
static std::string localized_online(LLTrans::getString("group_member_status_online"));
online_status = localized_online;
}
else
{
formatDateString(online_status); // reformat for sorting, e.g. 12/25/2008 -> 2008/12/25
}
//llinfos << "Member " << member_id << " has powers " << std::hex << agent_powers << std::dec << llendl;
LLGroupMemberData* newdata = new LLGroupMemberData(member_id,
contribution,
agent_powers,
title,
online_status,
is_owner);
#if LL_DEBUG
LLGroupMgrGroupData::member_list_t::iterator mit = group_datap->mMembers.find(member_id);
if (mit != group_datap->mMembers.end())
{
llinfos << " *** Received duplicate member data for agent " << member_id << llendl;
}
#endif
group_datap->mMembers[member_id] = newdata;
}
else
{
llinfos << "Received null group member data." << llendl;
}
}
//if group members are loaded while titles are missing, load the titles.
if(group_datap->mTitles.size() < 1)
{
LLGroupMgr::getInstance()->sendGroupTitlesRequest(group_id);
}
}
if (group_datap->mMembers.size() == (U32)group_datap->mMemberCount)
{
group_datap->mMemberDataComplete = TRUE;
group_datap->mMemberRequestID.setNull();
// We don't want to make role-member data requests until we have all the members
if (group_datap->mPendingRoleMemberRequest)
{
group_datap->mPendingRoleMemberRequest = FALSE;
LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(group_datap->mID);
}
}
group_datap->mChanged = TRUE;
LLGroupMgr::getInstance()->notifyObservers(GC_MEMBER_DATA);
}
//static
void LLGroupMgr::processGroupPropertiesReply(LLMessageSystem* msg, void** data)
{
lldebugs << "LLGroupMgr::processGroupPropertiesReply" << llendl;
LLUUID agent_id;
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
if (gAgent.getID() != agent_id)
{
llwarns << "Got group properties reply for another agent!" << llendl;
return;
}
LLUUID group_id;
std::string name;
std::string charter;
BOOL show_in_list = FALSE;
LLUUID founder_id;
U64 powers_mask = GP_NO_POWERS;
S32 money = 0;
std::string member_title;
LLUUID insignia_id;
LLUUID owner_role;
U32 membership_fee = 0;
BOOL open_enrollment = FALSE;
S32 num_group_members = 0;
S32 num_group_roles = 0;
BOOL allow_publish = FALSE;
BOOL mature = FALSE;
msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id );
msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_FounderID, founder_id);
msg->getStringFast(_PREHASH_GroupData, _PREHASH_Name, name );
msg->getStringFast(_PREHASH_GroupData, _PREHASH_Charter, charter );
msg->getBOOLFast(_PREHASH_GroupData, _PREHASH_ShowInList, show_in_list );
msg->getStringFast(_PREHASH_GroupData, _PREHASH_MemberTitle, member_title );
msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_InsigniaID, insignia_id );
msg->getU64Fast(_PREHASH_GroupData, _PREHASH_PowersMask, powers_mask );
msg->getU32Fast(_PREHASH_GroupData, _PREHASH_MembershipFee, membership_fee );
msg->getBOOLFast(_PREHASH_GroupData, _PREHASH_OpenEnrollment, open_enrollment );
msg->getS32Fast(_PREHASH_GroupData, _PREHASH_GroupMembershipCount, num_group_members);
msg->getS32(_PREHASH_GroupData, "GroupRolesCount", num_group_roles);
msg->getS32Fast(_PREHASH_GroupData, _PREHASH_Money, money);
msg->getBOOL("GroupData", "AllowPublish", allow_publish);
msg->getBOOL("GroupData", "MaturePublish", mature);
msg->getUUID(_PREHASH_GroupData, "OwnerRole", owner_role);
LLGroupMgrGroupData* group_datap = LLGroupMgr::getInstance()->createGroupData(group_id);
group_datap->mName = name;
group_datap->mCharter = charter;
group_datap->mShowInList = show_in_list;
group_datap->mInsigniaID = insignia_id;
group_datap->mFounderID = founder_id;
group_datap->mMembershipFee = membership_fee;
group_datap->mOpenEnrollment = open_enrollment;
group_datap->mAllowPublish = allow_publish;
group_datap->mMaturePublish = mature;
group_datap->mOwnerRole = owner_role;
group_datap->mMemberCount = num_group_members;
group_datap->mRoleCount = num_group_roles + 1; // Add the everyone role.
group_datap->mGroupPropertiesDataComplete = TRUE;
group_datap->mChanged = TRUE;
LLGroupMgr::getInstance()->notifyObservers(GC_PROPERTIES);
}
// static
void LLGroupMgr::processGroupRoleDataReply(LLMessageSystem* msg, void** data)
{
lldebugs << "LLGroupMgr::processGroupRoleDataReply" << llendl;
LLUUID agent_id;
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
if (gAgent.getID() != agent_id)
{
llwarns << "Got group role data reply for another agent!" << llendl;
return;
}
LLUUID group_id;
msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id );
LLUUID request_id;
msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_RequestID, request_id);
LLGroupMgrGroupData* group_datap = LLGroupMgr::getInstance()->createGroupData(group_id);
if (!group_datap || (group_datap->mRoleDataRequestID != request_id))
{
llwarns << "processGroupPropertiesReply: Received incorrect (stale?) group or request id" << llendl;
return;
}
msg->getS32(_PREHASH_GroupData, "RoleCount", group_datap->mRoleCount );
std::string name;
std::string title;
std::string desc;
U64 powers = 0;
U32 member_count = 0;
LLUUID role_id;
U32 num_blocks = msg->getNumberOfBlocks("RoleData");
U32 i = 0;
for (i=0; i< num_blocks; ++i)
{
msg->getUUID("RoleData", "RoleID", role_id, i );
msg->getString("RoleData","Name",name,i);
msg->getString("RoleData","Title",title,i);
msg->getString("RoleData","Description",desc,i);
msg->getU64("RoleData","Powers",powers,i);
msg->getU32("RoleData","Members",member_count,i);
//there are 3 predifined roles - Owners, Officers, Everyone
//there names are defined in lldatagroups.cpp
//lets change names from server to localized strings
if(name == "Everyone")
{
name = LLTrans::getString("group_role_everyone");
}
else if(name == "Officers")
{
name = LLTrans::getString("group_role_officers");
}
else if(name == "Owners")
{
name = LLTrans::getString("group_role_owners");
}
lldebugs << "Adding role data: " << name << " {" << role_id << "}" << llendl;
LLGroupRoleData* rd = new LLGroupRoleData(role_id,name,title,desc,powers,member_count);
group_datap->mRoles[role_id] = rd;
}
if (group_datap->mRoles.size() == (U32)group_datap->mRoleCount)
{
group_datap->mRoleDataComplete = TRUE;
group_datap->mRoleDataRequestID.setNull();
// We don't want to make role-member data requests until we have all the role data
if (group_datap->mPendingRoleMemberRequest)
{
group_datap->mPendingRoleMemberRequest = FALSE;
LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(group_datap->mID);
}
}
group_datap->mChanged = TRUE;
#if SHY_MOD //Group title script access
gGroupRoleChanger.CheckUpdateRole(group_id,group_datap->mRoles);
#endif //shy_mod
LLGroupMgr::getInstance()->notifyObservers(GC_ROLE_DATA);
}
// static
void LLGroupMgr::processGroupRoleMembersReply(LLMessageSystem* msg, void** data)
{
lldebugs << "LLGroupMgr::processGroupRoleMembersReply" << llendl;
LLUUID agent_id;
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
if (gAgent.getID() != agent_id)
{
llwarns << "Got group role members reply for another agent!" << llendl;
return;
}
LLUUID request_id;
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_RequestID, request_id);
LLUUID group_id;
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
U32 total_pairs;
msg->getU32(_PREHASH_AgentData, "TotalPairs", total_pairs);
LLGroupMgrGroupData* group_datap = LLGroupMgr::getInstance()->createGroupData(group_id);
if (!group_datap || (group_datap->mRoleMembersRequestID != request_id))
{
llwarns << "processGroupRoleMembersReply: Received incorrect (stale?) group or request id" << llendl;
return;
}
U32 num_blocks = msg->getNumberOfBlocks("MemberData");
U32 i;
LLUUID member_id;
LLUUID role_id;
LLGroupRoleData* rd = NULL;
LLGroupMemberData* md = NULL;
LLGroupMgrGroupData::role_list_t::iterator ri;
LLGroupMgrGroupData::member_list_t::iterator mi;
// If total_pairs == 0, there are no members in any custom roles.
if (total_pairs > 0)
{
for (i = 0;i < num_blocks; ++i)
{
msg->getUUID("MemberData","RoleID",role_id,i);
msg->getUUID("MemberData","MemberID",member_id,i);
if (role_id.notNull() && member_id.notNull() )
{
rd = NULL;
ri = group_datap->mRoles.find(role_id);
if (ri != group_datap->mRoles.end())
{
rd = ri->second;
}
md = NULL;
mi = group_datap->mMembers.find(member_id);
if (mi != group_datap->mMembers.end())
{
md = mi->second;
}
if (rd && md)
{
lldebugs << "Adding role-member pair: " << role_id << ", " << member_id << llendl;
rd->addMember(member_id);
md->addRole(role_id,rd);
}
else
{
if (!rd) llwarns << "Received role data for unknown role " << role_id << " in group " << group_id << llendl;
if (!md) llwarns << "Received role data for unknown member " << member_id << " in group " << group_id << llendl;
}
}
}
group_datap->mReceivedRoleMemberPairs += num_blocks;
}
if (group_datap->mReceivedRoleMemberPairs == total_pairs)
{
// Add role data for the 'everyone' role to all members
LLGroupRoleData* everyone = group_datap->mRoles[LLUUID::null];
if (!everyone)
{
llwarns << "Everyone role not found!" << llendl;
}
else
{
for (LLGroupMgrGroupData::member_list_t::iterator mi = group_datap->mMembers.begin();
mi != group_datap->mMembers.end(); ++mi)
{
LLGroupMemberData* data = mi->second;
if (data)
{
data->addRole(LLUUID::null,everyone);
}
}
}
group_datap->mRoleMemberDataComplete = TRUE;
group_datap->mRoleMembersRequestID.setNull();
}
group_datap->mChanged = TRUE;
LLGroupMgr::getInstance()->notifyObservers(GC_ROLE_MEMBER_DATA);
}
// static
void LLGroupMgr::processGroupTitlesReply(LLMessageSystem* msg, void** data)
{
lldebugs << "LLGroupMgr::processGroupTitlesReply" << llendl;
LLUUID agent_id;
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
if (gAgent.getID() != agent_id)
{
llwarns << "Got group properties reply for another agent!" << llendl;
return;
}
LLUUID group_id;
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
LLUUID request_id;
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_RequestID, request_id);
LLGroupMgrGroupData* group_datap = LLGroupMgr::getInstance()->createGroupData(group_id);
if (!group_datap || (group_datap->mTitlesRequestID != request_id))
{
llwarns << "processGroupTitlesReply: Received incorrect (stale?) group" << llendl;
return;
}
LLGroupTitle title;
S32 i = 0;
S32 blocks = msg->getNumberOfBlocksFast(_PREHASH_GroupData);
for (i=0; i<blocks; ++i)
{
msg->getString("GroupData","Title",title.mTitle,i);
msg->getUUID("GroupData","RoleID",title.mRoleID,i);
msg->getBOOL("GroupData","Selected",title.mSelected,i);
if (!title.mTitle.empty())
{
lldebugs << "LLGroupMgr adding title: " << title.mTitle << ", " << title.mRoleID << ", " << (title.mSelected ? 'Y' : 'N') << llendl;
group_datap->mTitles.push_back(title);
}
}
group_datap->mChanged = TRUE;
LLGroupMgr::getInstance()->notifyObservers(GC_TITLES);
}
// static
void LLGroupMgr::processEjectGroupMemberReply(LLMessageSystem* msg, void ** data)
{
lldebugs << "processEjectGroupMemberReply" << llendl;
LLUUID group_id;
msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id);
BOOL success;
msg->getBOOLFast(_PREHASH_EjectData, _PREHASH_Success, success);
// If we had a failure, the group panel needs to be updated.
if (!success)
{
LLFloaterGroupInfo::refreshGroup(group_id);
}
}
// static
void LLGroupMgr::processJoinGroupReply(LLMessageSystem* msg, void ** data)
{
lldebugs << "processJoinGroupReply" << llendl;
LLUUID group_id;
BOOL success;
msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id);
msg->getBOOLFast(_PREHASH_GroupData, _PREHASH_Success, success);
if (success)
{
// refresh all group information
gAgent.sendAgentDataUpdateRequest();
LLGroupMgr::getInstance()->clearGroupData(group_id);
// refresh the floater for this group, if any.
LLFloaterGroupInfo::refreshGroup(group_id);
// refresh the group panel of the search window, if necessary.
LLFloaterDirectory::refreshGroup(group_id);
}
}
// static
void LLGroupMgr::processLeaveGroupReply(LLMessageSystem* msg, void ** data)
{
lldebugs << "processLeaveGroupReply" << llendl;
LLUUID group_id;
BOOL success;
msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id);
msg->getBOOLFast(_PREHASH_GroupData, _PREHASH_Success, success);
if (success)
{
// refresh all group information
gAgent.sendAgentDataUpdateRequest();
LLGroupMgr::getInstance()->clearGroupData(group_id);
// close the floater for this group, if any.
LLFloaterGroupInfo::closeGroup(group_id);
// refresh the group panel of the search window, if necessary.
LLFloaterDirectory::refreshGroup(group_id);
}
}
// static
void LLGroupMgr::processCreateGroupReply(LLMessageSystem* msg, void ** data)
{
LLUUID group_id;
BOOL success;
std::string message;
msg->getUUIDFast(_PREHASH_ReplyData, _PREHASH_GroupID, group_id );
msg->getBOOLFast(_PREHASH_ReplyData, _PREHASH_Success, success );
msg->getStringFast(_PREHASH_ReplyData, _PREHASH_Message, message );
if (success)
{
// refresh all group information
gAgent.sendAgentDataUpdateRequest();
// HACK! We haven't gotten the agent group update yet, so ... um ... fake it.
// This is so when we go to modify the group we will be able to do so.
// This isn't actually too bad because real data will come down in 2 or 3 miliseconds and replace this.
LLGroupData gd;
gd.mAcceptNotices = TRUE;
gd.mListInProfile = TRUE;
gd.mContribution = 0;
gd.mID = group_id;
gd.mName = "new group";
gd.mPowers = GP_ALL_POWERS;
gAgent.mGroups.push_back(gd);
LLFloaterGroupInfo::closeCreateGroup();
LLFloaterGroupInfo::showFromUUID(group_id,"roles_tab");
}
else
{
// *TODO:translate
LLSD args;
args["MESSAGE"] = message;
LLNotificationsUtil::add("UnableToCreateGroup", args);
}
}
LLGroupMgrGroupData* LLGroupMgr::createGroupData(const LLUUID& id)
{
LLGroupMgrGroupData* group_datap;
group_map_t::iterator existing_group = LLGroupMgr::getInstance()->mGroups.find(id);
if (existing_group == LLGroupMgr::getInstance()->mGroups.end())
{
group_datap = new LLGroupMgrGroupData(id);
LLGroupMgr::getInstance()->addGroup(group_datap);
}
else
{
group_datap = existing_group->second;
}
return group_datap;
}
void LLGroupMgr::notifyObservers(LLGroupChange gc)
{
for (group_map_t::iterator gi = mGroups.begin(); gi != mGroups.end(); ++gi)
{
LLUUID group_id = gi->first;
if (gi->second->mChanged)
{
// notify LLGroupMgrObserver
// Copy the map because observers may remove themselves on update
observer_multimap_t observers = mObservers;
// find all observers for this group id
observer_multimap_t::iterator oi = observers.lower_bound(group_id);
observer_multimap_t::iterator end = observers.upper_bound(group_id);
for (; oi != end; ++oi)
{
oi->second->changed(gc);
}
gi->second->mChanged = FALSE;
// notify LLParticularGroupObserver
observer_map_t::iterator obs_it = mParticularObservers.find(group_id);
if(obs_it == mParticularObservers.end())
return;
observer_set_t& obs = obs_it->second;
for (observer_set_t::iterator ob_it = obs.begin(); ob_it != obs.end(); ++ob_it)
{
(*ob_it)->changed(group_id, gc);
}
}
}
}
void LLGroupMgr::addGroup(LLGroupMgrGroupData* group_datap)
{
if (mGroups.size() > MAX_CACHED_GROUPS)
{
// get rid of groups that aren't observed
for (group_map_t::iterator gi = mGroups.begin(); gi != mGroups.end() && mGroups.size() > MAX_CACHED_GROUPS / 2; )
{
observer_multimap_t::iterator oi = mObservers.find(gi->first);
if (oi == mObservers.end())
{
// not observed
LLGroupMgrGroupData* unobserved_groupp = gi->second;
delete unobserved_groupp;
mGroups.erase(gi++);
}
else
{
++gi;
}
}
}
mGroups[group_datap->getID()] = group_datap;
}
void LLGroupMgr::sendGroupPropertiesRequest(const LLUUID& group_id)
{
lldebugs << "LLGroupMgr::sendGroupPropertiesRequest" << llendl;
// This will happen when we get the reply
//LLGroupMgrGroupData* group_datap = createGroupData(group_id);
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("GroupProfileRequest");
msg->nextBlock("AgentData");
msg->addUUID("AgentID",gAgent.getID());
msg->addUUID("SessionID",gAgent.getSessionID());
msg->nextBlock("GroupData");
msg->addUUID("GroupID",group_id);
gAgent.sendReliableMessage();
}
void LLGroupMgr::sendGroupMembersRequest(const LLUUID& group_id)
{
lldebugs << "LLGroupMgr::sendGroupMembersRequest" << llendl;
LLGroupMgrGroupData* group_datap = createGroupData(group_id);
if (group_datap->mMemberRequestID.isNull())
{
group_datap->removeMemberData();
group_datap->mMemberRequestID.generate();
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("GroupMembersRequest");
msg->nextBlock("AgentData");
msg->addUUID("AgentID",gAgent.getID());
msg->addUUID("SessionID",gAgent.getSessionID());
msg->nextBlock("GroupData");
msg->addUUID("GroupID",group_id);
msg->addUUID("RequestID",group_datap->mMemberRequestID);
gAgent.sendReliableMessage();
}
}
void LLGroupMgr::sendGroupRoleDataRequest(const LLUUID& group_id)
{
lldebugs << "LLGroupMgr::sendGroupRoleDataRequest" << llendl;
LLGroupMgrGroupData* group_datap = createGroupData(group_id);
if (group_datap->mRoleDataRequestID.isNull())
{
group_datap->removeRoleData();
group_datap->mRoleDataRequestID.generate();
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("GroupRoleDataRequest");
msg->nextBlock("AgentData");
msg->addUUID("AgentID",gAgent.getID());
msg->addUUID("SessionID",gAgent.getSessionID());
msg->nextBlock("GroupData");
msg->addUUID("GroupID",group_id);
msg->addUUID("RequestID",group_datap->mRoleDataRequestID);
gAgent.sendReliableMessage();
}
}
void LLGroupMgr::sendGroupRoleMembersRequest(const LLUUID& group_id)
{
lldebugs << "LLGroupMgr::sendGroupRoleMembersRequest" << llendl;
LLGroupMgrGroupData* group_datap = createGroupData(group_id);
if (group_datap->mRoleMembersRequestID.isNull())
{
// Don't send the request if we don't have all the member or role data
if (!group_datap->isMemberDataComplete()
|| !group_datap->isRoleDataComplete())
{
// *TODO: KLW FIXME: Should we start a member or role data request?
llinfos << " Pending: " << (group_datap->mPendingRoleMemberRequest ? "Y" : "N")
<< " MemberDataComplete: " << (group_datap->mMemberDataComplete ? "Y" : "N")
<< " RoleDataComplete: " << (group_datap->mRoleDataComplete ? "Y" : "N") << llendl;
group_datap->mPendingRoleMemberRequest = TRUE;
return;
}
group_datap->removeRoleMemberData();
group_datap->mRoleMembersRequestID.generate();
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("GroupRoleMembersRequest");
msg->nextBlock("AgentData");
msg->addUUID("AgentID",gAgent.getID());
msg->addUUID("SessionID",gAgent.getSessionID());
msg->nextBlock("GroupData");
msg->addUUID("GroupID",group_id);
msg->addUUID("RequestID",group_datap->mRoleMembersRequestID);
gAgent.sendReliableMessage();
}
}
void LLGroupMgr::sendGroupTitlesRequest(const LLUUID& group_id)
{
lldebugs << "LLGroupMgr::sendGroupTitlesRequest" << llendl;
LLGroupMgrGroupData* group_datap = createGroupData(group_id);
group_datap->mTitles.clear();
group_datap->mTitlesRequestID.generate();
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("GroupTitlesRequest");
msg->nextBlock("AgentData");
msg->addUUID("AgentID",gAgent.getID());
msg->addUUID("SessionID",gAgent.getSessionID());
msg->addUUID("GroupID",group_id);
msg->addUUID("RequestID",group_datap->mTitlesRequestID);
gAgent.sendReliableMessage();
}
void LLGroupMgr::sendGroupTitleUpdate(const LLUUID& group_id, const LLUUID& title_role_id)
{
lldebugs << "LLGroupMgr::sendGroupTitleUpdate" << llendl;
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("GroupTitleUpdate");
msg->nextBlock("AgentData");
msg->addUUID("AgentID",gAgent.getID());
msg->addUUID("SessionID",gAgent.getSessionID());
msg->addUUID("GroupID",group_id);
msg->addUUID("TitleRoleID",title_role_id);
gAgent.sendReliableMessage();
// Save the change locally
LLGroupMgrGroupData* group_datap = createGroupData(group_id);
for (std::vector<LLGroupTitle>::iterator iter = group_datap->mTitles.begin();
iter != group_datap->mTitles.end(); ++iter)
{
if (iter->mRoleID == title_role_id)
{
iter->mSelected = TRUE;
}
else if (iter->mSelected)
{
iter->mSelected = FALSE;
}
}
}
// static
void LLGroupMgr::sendCreateGroupRequest(const std::string& name,
const std::string& charter,
U8 show_in_list,
const LLUUID& insignia,
S32 membership_fee,
BOOL open_enrollment,
BOOL allow_publish,
BOOL mature_publish)
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("CreateGroupRequest");
msg->nextBlock("AgentData");
msg->addUUID("AgentID",gAgent.getID());
msg->addUUID("SessionID",gAgent.getSessionID());
msg->nextBlock("GroupData");
msg->addString("Name",name);
msg->addString("Charter",charter);
msg->addBOOL("ShowInList",show_in_list);
msg->addUUID("InsigniaID",insignia);
msg->addS32("MembershipFee",membership_fee);
msg->addBOOL("OpenEnrollment",open_enrollment);
msg->addBOOL("AllowPublish",allow_publish);
msg->addBOOL("MaturePublish",mature_publish);
gAgent.sendReliableMessage();
}
void LLGroupMgr::sendUpdateGroupInfo(const LLUUID& group_id)
{
lldebugs << "LLGroupMgr::sendUpdateGroupInfo" << llendl;
LLGroupMgrGroupData* group_datap = createGroupData(group_id);
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_UpdateGroupInfo);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID,gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_GroupData);
msg->addUUIDFast(_PREHASH_GroupID,group_datap->getID());
msg->addStringFast(_PREHASH_Charter,group_datap->mCharter);
msg->addBOOLFast(_PREHASH_ShowInList,group_datap->mShowInList);
msg->addUUIDFast(_PREHASH_InsigniaID,group_datap->mInsigniaID);
msg->addS32Fast(_PREHASH_MembershipFee,group_datap->mMembershipFee);
msg->addBOOLFast(_PREHASH_OpenEnrollment,group_datap->mOpenEnrollment);
msg->addBOOLFast(_PREHASH_AllowPublish,group_datap->mAllowPublish);
msg->addBOOLFast(_PREHASH_MaturePublish,group_datap->mMaturePublish);
gAgent.sendReliableMessage();
// Not expecting a response, so let anyone else watching know the data has changed.
group_datap->mChanged = TRUE;
notifyObservers(GC_PROPERTIES);
}
void LLGroupMgr::sendGroupRoleMemberChanges(const LLUUID& group_id)
{
lldebugs << "LLGroupMgr::sendGroupRoleMemberChanges" << llendl;
LLGroupMgrGroupData* group_datap = createGroupData(group_id);
if (group_datap->mRoleMemberChanges.empty()) return;
LLMessageSystem* msg = gMessageSystem;
bool start_message = true;
for (LLGroupMgrGroupData::change_map_t::const_iterator citer = group_datap->mRoleMemberChanges.begin();
citer != group_datap->mRoleMemberChanges.end(); ++citer)
{
if (start_message)
{
msg->newMessage("GroupRoleChanges");
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID,gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
msg->addUUIDFast(_PREHASH_GroupID,group_id);
start_message = false;
}
msg->nextBlock("RoleChange");
msg->addUUID("RoleID",citer->second.mRole);
msg->addUUID("MemberID",citer->second.mMember);
msg->addU32("Change",(U32)citer->second.mChange);
if (msg->isSendFullFast())
{
gAgent.sendReliableMessage();
start_message = true;
}
}
if (!start_message)
{
gAgent.sendReliableMessage();
}
group_datap->mRoleMemberChanges.clear();
// Not expecting a response, so let anyone else watching know the data has changed.
group_datap->mChanged = TRUE;
notifyObservers(GC_ROLE_MEMBER_DATA);
}
//static
void LLGroupMgr::sendGroupMemberJoin(const LLUUID& group_id)
{
LLMessageSystem *msg = gMessageSystem;
msg->newMessageFast(_PREHASH_JoinGroupRequest);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_GroupData);
msg->addUUIDFast(_PREHASH_GroupID, group_id);
gAgent.sendReliableMessage();
}
// member_role_pairs is <member_id,role_id>
// static
void LLGroupMgr::sendGroupMemberInvites(const LLUUID& group_id, std::map<LLUUID,LLUUID>& member_role_pairs)
{
bool start_message = true;
LLMessageSystem* msg = gMessageSystem;
for (std::map<LLUUID,LLUUID>::iterator it = member_role_pairs.begin();
it != member_role_pairs.end(); ++it)
{
if (start_message)
{
msg->newMessage("InviteGroupRequest");
msg->nextBlock("AgentData");
msg->addUUID("AgentID",gAgent.getID());
msg->addUUID("SessionID",gAgent.getSessionID());
msg->nextBlock("GroupData");
msg->addUUID("GroupID",group_id);
start_message = false;
}
msg->nextBlock("InviteData");
msg->addUUID("InviteeID",(*it).first);
msg->addUUID("RoleID",(*it).second);
if (msg->isSendFull())
{
gAgent.sendReliableMessage();
start_message = true;
}
}
if (!start_message)
{
gAgent.sendReliableMessage();
}
}
//static
void LLGroupMgr::sendGroupMemberEjects(const LLUUID& group_id,
uuid_vec_t& member_ids)
{
bool start_message = true;
LLMessageSystem* msg = gMessageSystem;
LLGroupMgrGroupData* group_datap = LLGroupMgr::getInstance()->getGroupData(group_id);
if (!group_datap) return;
for (uuid_vec_t::iterator it = member_ids.begin();
it != member_ids.end(); ++it)
{
LLUUID& ejected_member_id = (*it);
// Can't use 'eject' to leave a group.
if (ejected_member_id == gAgent.getID()) continue;
// Make sure they are in the group, and we need the member data
LLGroupMgrGroupData::member_list_t::iterator mit = group_datap->mMembers.find(ejected_member_id);
if (mit != group_datap->mMembers.end())
{
// Add them to the message
if (start_message)
{
msg->newMessage("EjectGroupMemberRequest");
msg->nextBlock("AgentData");
msg->addUUID("AgentID",gAgent.getID());
msg->addUUID("SessionID",gAgent.getSessionID());
msg->nextBlock("GroupData");
msg->addUUID("GroupID",group_id);
start_message = false;
}
msg->nextBlock("EjectData");
msg->addUUID("EjecteeID",ejected_member_id);
if (msg->isSendFull())
{
gAgent.sendReliableMessage();
start_message = true;
}
LLGroupMemberData* member_data = (*mit).second;
// Clean up groupmgr
for (LLGroupMemberData::role_list_t::iterator rit = member_data->roleBegin();
rit != member_data->roleEnd(); ++rit)
{
if ((*rit).first.notNull() && (*rit).second!=0)
{
(*rit).second->removeMember(ejected_member_id);
}
}
group_datap->mMembers.erase(ejected_member_id);
// member_data was introduced and is used here instead of (*mit).second to avoid crash because of invalid iterator
// It becomes invalid after line with erase above. EXT-4778
delete member_data;
}
}
if (!start_message)
{
gAgent.sendReliableMessage();
}
}
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy groupMemberDataResponder_timeout;
// Responder class for capability group management
class GroupMemberDataResponder : public LLHTTPClient::ResponderWithResult
{
public:
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return groupMemberDataResponder_timeout; }
GroupMemberDataResponder() {}
virtual ~GroupMemberDataResponder() {}
virtual void result(const LLSD& pContent);
virtual void error(U32 pStatus, const std::string& pReason);
private:
LLSD mMemberData;
};
void GroupMemberDataResponder::error(U32 pStatus, const std::string& pReason)
{
LL_WARNS("GrpMgr") << "Error receiving group member data." << LL_ENDL;
}
void GroupMemberDataResponder::result(const LLSD& content)
{
LLGroupMgr::processCapGroupMembersRequest(content);
}
// static
void LLGroupMgr::sendCapGroupMembersRequest(const LLUUID& group_id)
{
// Have we requested the information already this frame?
if (mLastGroupMembersRequestFrame == gFrameCount)
return;
LLViewerRegion* currentRegion = gAgent.getRegion();
// Thank you FS:Ansariel!
if (!currentRegion)
{
LL_WARNS("GrpMgr") << "Agent does not have a current region. Uh-oh!" << LL_ENDL;
return;
}
// Check to make sure we have our capabilities
if (!currentRegion->capabilitiesReceived())
{
LL_WARNS("GrpMgr") << " Capabilities not received!" << LL_ENDL;
return;
}
// Get our capability
std::string cap_url = currentRegion->getCapability("GroupMemberData");
// Thank you FS:Ansariel!
if (cap_url.empty())
{
LL_INFOS("GrpMgr") << "Region has no GroupMemberData capability. Falling back to UDP fetch." << LL_ENDL;
sendGroupMembersRequest(group_id);
return;
}
// Post to our service. Add a body containing the group_id.
LLSD body = LLSD::emptyMap();
body["group_id"] = group_id;
LLHTTPClient::ResponderPtr grp_data_responder = new GroupMemberDataResponder();
// This could take a while to finish, timeout after 5 minutes.
LLHTTPClient::post(cap_url, body, grp_data_responder);
mLastGroupMembersRequestFrame = gFrameCount;
}
// static
void LLGroupMgr::processCapGroupMembersRequest(const LLSD& content)
{
// Did we get anything in content?
if (!content.size())
{
LL_DEBUGS("GrpMgr") << "No group member data received." << LL_ENDL;
return;
}
// If we have no members, there's no reason to do anything else
S32 num_members = content["member_count"];
if (num_members < 1)
return;
LLUUID group_id = content["group_id"].asUUID();
LLGroupMgrGroupData* group_datap = LLGroupMgr::getInstance()->getGroupData(group_id);
if (!group_datap)
{
LL_WARNS("GrpMgr") << "Received incorrect, possibly stale, group or request id" << LL_ENDL;
return;
}
group_datap->mMemberCount = num_members;
LLSD member_list = content["members"];
LLSD titles = content["titles"];
LLSD defaults = content["defaults"];
std::string online_status;
std::string title;
S32 contribution;
U64 member_powers;
// If this is changed to a bool, make sure to change the LLGroupMemberData constructor
BOOL is_owner;
// Compute this once, rather than every time.
U64 default_powers = llstrtou64(defaults["default_powers"].asString().c_str(), NULL, 16);
LLSD::map_const_iterator member_iter_start = member_list.beginMap();
LLSD::map_const_iterator member_iter_end = member_list.endMap();
for ( ; member_iter_start != member_iter_end; ++member_iter_start)
{
// Reset defaults
online_status = "unknown";
title = titles[0].asString();
contribution = 0;
member_powers = default_powers;
is_owner = false;
const LLUUID member_id(member_iter_start->first);
LLSD member_info = member_iter_start->second;
if (member_info.has("last_login"))
{
online_status = member_info["last_login"].asString();
if (online_status == "Online")
online_status = LLTrans::getString("group_member_status_online");
else
formatDateString(online_status);
}
if (member_info.has("title"))
title = titles[member_info["title"].asInteger()].asString();
if (member_info.has("powers"))
member_powers = llstrtou64(member_info["powers"].asString().c_str(), NULL, 16);
if (member_info.has("donated_square_meters"))
contribution = member_info["donated_square_meters"];
if (member_info.has("owner"))
is_owner = true;
LLGroupMemberData* data = new LLGroupMemberData(member_id,
contribution,
member_powers,
title,
online_status,
is_owner);
group_datap->mMembers[member_id] = data;
}
// Technically, we have this data, but to prevent completely overhauling
// this entire system (it would be nice, but I don't have the time),
// I'm going to be dumb and just call services I most likely don't need
// with the thought being that the system might need it to be done.
//
// TODO: Refactor to reduce multiple calls for data we already have.
if (group_datap->mTitles.size() < 1)
LLGroupMgr::getInstance()->sendGroupTitlesRequest(group_id);
group_datap->mMemberDataComplete = TRUE;
group_datap->mMemberRequestID.setNull();
// Make the role-member data request
if (group_datap->mPendingRoleMemberRequest)
{
group_datap->mPendingRoleMemberRequest = FALSE;
LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(group_id);
}
group_datap->mChanged = TRUE;
LLGroupMgr::getInstance()->notifyObservers(GC_MEMBER_DATA);
}
void LLGroupMgr::sendGroupRoleChanges(const LLUUID& group_id)
{
lldebugs << "LLGroupMgr::sendGroupRoleChanges" << llendl;
LLGroupMgrGroupData* group_datap = getGroupData(group_id);
if (group_datap && group_datap->pendingRoleChanges())
{
group_datap->sendRoleChanges();
// Not expecting a response, so let anyone else watching know the data has changed.
group_datap->mChanged = TRUE;
notifyObservers(GC_ROLE_DATA);
}
}
void LLGroupMgr::cancelGroupRoleChanges(const LLUUID& group_id)
{
lldebugs << "LLGroupMgr::cancelGroupRoleChanges" << llendl;
LLGroupMgrGroupData* group_datap = getGroupData(group_id);
if (group_datap) group_datap->cancelRoleChanges();
}
//static
bool LLGroupMgr::parseRoleActions(const std::string& xml_filename)
{
LLXMLNodePtr root;
BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
if (!success || !root || !root->hasName( "role_actions" ))
{
llerrs << "Problem reading UI role_actions file: " << xml_filename << llendl;
return false;
}
LLXMLNodeList role_list;
root->getChildren("action_set", role_list, false);
for (LLXMLNodeList::iterator role_iter = role_list.begin(); role_iter != role_list.end(); ++role_iter)
{
LLXMLNodePtr action_set = role_iter->second;
LLRoleActionSet* role_action_set = new LLRoleActionSet();
LLRoleAction* role_action_data = new LLRoleAction();
// name=
std::string action_set_name;
if (action_set->getAttributeString("name", action_set_name))
{
lldebugs << "Loading action set " << action_set_name << llendl;
role_action_data->mName = action_set_name;
}
else
{
llwarns << "Unable to parse action set with no name" << llendl;
delete role_action_set;
delete role_action_data;
continue;
}
// description=
std::string set_description;
if (action_set->getAttributeString("description", set_description))
{
role_action_data->mDescription = set_description;
}
// long description=
std::string set_longdescription;
if (action_set->getAttributeString("longdescription", set_longdescription))
{
role_action_data->mLongDescription = set_longdescription;
}
// power mask=
U64 set_power_mask = 0;
LLXMLNodeList action_list;
LLXMLNodeList::iterator action_iter;
action_set->getChildren("action", action_list, false);
for (action_iter = action_list.begin(); action_iter != action_list.end(); ++action_iter)
{
LLXMLNodePtr action = action_iter->second;
LLRoleAction* role_action = new LLRoleAction();
// name=
std::string action_name;
if (action->getAttributeString("name", action_name))
{
lldebugs << "Loading action " << action_name << llendl;
role_action->mName = action_name;
}
else
{
llwarns << "Unable to parse action with no name" << llendl;
delete role_action;
continue;
}
// description=
std::string description;
if (action->getAttributeString("description", description))
{
role_action->mDescription = description;
}
// long description=
std::string longdescription;
if (action->getAttributeString("longdescription", longdescription))
{
role_action->mLongDescription = longdescription;
}
// description=
S32 power_bit = 0;
if (action->getAttributeS32("value", power_bit))
{
if (0 <= power_bit && power_bit < 64)
{
role_action->mPowerBit = 0x1LL << power_bit;
}
}
set_power_mask |= role_action->mPowerBit;
role_action_set->mActions.push_back(role_action);
}
role_action_data->mPowerBit = set_power_mask;
role_action_set->mActionSetData = role_action_data;
LLGroupMgr::getInstance()->mRoleActionSets.push_back(role_action_set);
}
return true;
}
// static
void LLGroupMgr::debugClearAllGroups(void*)
{
LLGroupMgr::getInstance()->clearGroups();
LLGroupMgr::parseRoleActions("role_actions.xml");
}