/* * Copyright (c) 2007-2009, openmetaverse.org * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the openmetaverse.org nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections.Generic; using OpenMetaverse.Packets; using OpenMetaverse.StructuredData; using OpenMetaverse.Messages.Linden; using OpenMetaverse.Interfaces; using OpenMetaverse.Http; namespace OpenMetaverse { #region Structs /// /// Avatar group management /// public struct GroupMember { /// Key of Group Member public UUID ID; /// Total land contribution public int Contribution; /// Online status information public string OnlineStatus; /// Abilities that the Group Member has public GroupPowers Powers; /// Current group title public string Title; /// Is a group owner public bool IsOwner; } /// /// Role manager for a group /// public struct GroupRole { /// Key of the group public UUID GroupID; /// Key of Role public UUID ID; /// Name of Role public string Name; /// Group Title associated with Role public string Title; /// Description of Role public string Description; /// Abilities Associated with Role public GroupPowers Powers; /// Returns the role's title /// The role's title public override string ToString() { return Name; } } /// /// Class to represent Group Title /// public struct GroupTitle { /// Key of the group public UUID GroupID; /// ID of the role title belongs to public UUID RoleID; /// Group Title public string Title; /// Whether title is Active public bool Selected; /// Returns group title public override string ToString() { return Title; } } /// /// Represents a group on the grid /// public struct Group { /// Key of Group public UUID ID; /// Key of Group Insignia public UUID InsigniaID; /// Key of Group Founder public UUID FounderID; /// Key of Group Role for Owners public UUID OwnerRole; /// Name of Group public string Name; /// Text of Group Charter public string Charter; /// Title of "everyone" role public string MemberTitle; /// Is the group open for enrolement to everyone public bool OpenEnrollment; /// Will group show up in search public bool ShowInList; /// public GroupPowers Powers; /// public bool AcceptNotices; /// public bool AllowPublish; /// Is the group Mature public bool MaturePublish; /// Cost of group membership public int MembershipFee; /// public int Money; /// public int Contribution; /// The total number of current members this group has public int GroupMembershipCount; /// The number of roles this group has configured public int GroupRolesCount; /// Show this group in agent's profile public bool ListInProfile; /// Returns the name of the group /// A string containing the name of the group public override string ToString() { return Name; } } /// /// A group Vote /// public struct Vote { /// Key of Avatar who created Vote public UUID Candidate; /// Text of the Vote proposal public string VoteString; /// Total number of votes public int NumVotes; } /// /// A group proposal /// public struct GroupProposal { /// The Text of the proposal public string VoteText; /// The minimum number of members that must vote before proposal passes or failes public int Quorum; /// The required ration of yes/no votes required for vote to pass /// The three options are Simple Majority, 2/3 Majority, and Unanimous /// TODO: this should be an enum public float Majority; /// The duration in days votes are accepted public int Duration; } /// /// /// public struct GroupAccountSummary { /// public int IntervalDays; /// public int CurrentInterval; /// public string StartDate; /// public int Balance; /// public int TotalCredits; /// public int TotalDebits; /// public int ObjectTaxCurrent; /// public int LightTaxCurrent; /// public int LandTaxCurrent; /// public int GroupTaxCurrent; /// public int ParcelDirFeeCurrent; /// public int ObjectTaxEstimate; /// public int LightTaxEstimate; /// public int LandTaxEstimate; /// public int GroupTaxEstimate; /// public int ParcelDirFeeEstimate; /// public int NonExemptMembers; /// public string LastTaxDate; /// public string TaxDate; } /// /// Struct representing a group notice /// public struct GroupNotice { /// public string Subject; /// public string Message; /// public UUID AttachmentID; /// public UUID OwnerID; /// /// /// /// public byte[] SerializeAttachment() { if (OwnerID == UUID.Zero || AttachmentID == UUID.Zero) return Utils.EmptyBytes; OpenMetaverse.StructuredData.OSDMap att = new OpenMetaverse.StructuredData.OSDMap(); att.Add("item_id", OpenMetaverse.StructuredData.OSD.FromUUID(AttachmentID)); att.Add("owner_id", OpenMetaverse.StructuredData.OSD.FromUUID(OwnerID)); return OpenMetaverse.StructuredData.OSDParser.SerializeLLSDXmlBytes(att); /* //I guess this is how this works, no gaurentees string lsd = "" + AttachmentID.ToString() + "" + OwnerID.ToString() + ""; return Utils.StringToBytes(lsd); */ } } /// /// Struct representing a group notice list entry /// public struct GroupNoticesListEntry { /// Notice ID public UUID NoticeID; /// Creation timestamp of notice public uint Timestamp; /// Agent name who created notice public string FromName; /// Notice subject public string Subject; /// Is there an attachment? public bool HasAttachment; /// Attachment Type public AssetType AssetType; } /// /// Struct representing a member of a group chat session and their settings /// public struct ChatSessionMember { /// The of the Avatar public UUID AvatarKey; /// True if user has voice chat enabled public bool CanVoiceChat; /// True of Avatar has moderator abilities public bool IsModerator; /// True if a moderator has muted this avatars chat public bool MuteText; /// True if a moderator has muted this avatars voice public bool MuteVoice; } #endregion Structs #region Enums /// /// Role update flags /// public enum GroupRoleUpdate : uint { /// NoUpdate, /// UpdateData, /// UpdatePowers, /// UpdateAll, /// Create, /// Delete } [Flags] public enum GroupPowers : ulong { /// None = 0, // Membership /// Can send invitations to groups default role Invite = 1UL << 1, /// Can eject members from group Eject = 1UL << 2, /// Can toggle 'Open Enrollment' and change 'Signup fee' ChangeOptions = 1UL << 3, /// Member is visible in the public member list MemberVisible = 1UL << 47, // Roles /// Can create new roles CreateRole = 1UL << 4, /// Can delete existing roles DeleteRole = 1UL << 5, /// Can change Role names, titles and descriptions RoleProperties = 1UL << 6, /// Can assign other members to assigners role AssignMemberLimited = 1UL << 7, /// Can assign other members to any role AssignMember = 1UL << 8, /// Can remove members from roles RemoveMember = 1UL << 9, /// Can assign and remove abilities in roles ChangeActions = 1UL << 10, // Identity /// Can change group Charter, Insignia, 'Publish on the web' and which /// members are publicly visible in group member listings ChangeIdentity = 1UL << 11, // Parcel management /// Can buy land or deed land to group LandDeed = 1UL << 12, /// Can abandon group owned land to Governor Linden on mainland, or Estate owner for /// private estates LandRelease = 1UL << 13, /// Can set land for-sale information on group owned parcels LandSetSale = 1UL << 14, /// Can subdivide and join parcels LandDivideJoin = 1UL << 15, // Chat /// Can join group chat sessions JoinChat = 1UL << 16, /// Can use voice chat in Group Chat sessions AllowVoiceChat = 1UL << 27, /// Can moderate group chat sessions ModerateChat = 1UL << 37, // Parcel identity /// Can toggle "Show in Find Places" and set search category FindPlaces = 1UL << 17, /// Can change parcel name, description, and 'Publish on web' settings LandChangeIdentity = 1UL << 18, /// Can set the landing point and teleport routing on group land SetLandingPoint = 1UL << 19, // Parcel settings /// Can change music and media settings ChangeMedia = 1UL << 20, /// Can toggle 'Edit Terrain' option in Land settings LandEdit = 1UL << 21, /// Can toggle various About Land > Options settings LandOptions = 1UL << 22, // Parcel powers /// Can always terraform land, even if parcel settings have it turned off AllowEditLand = 1UL << 23, /// Can always fly while over group owned land AllowFly = 1UL << 24, /// Can always rez objects on group owned land AllowRez = 1UL << 25, /// Can always create landmarks for group owned parcels AllowLandmark = 1UL << 26, /// Can set home location on any group owned parcel AllowSetHome = 1UL << 28, // Parcel access /// Can modify public access settings for group owned parcels LandManageAllowed = 1UL << 29, /// Can manager parcel ban lists on group owned land LandManageBanned = 1UL << 30, /// Can manage pass list sales information LandManagePasses = 1UL << 31, /// Can eject and freeze other avatars on group owned land LandEjectAndFreeze = 1UL << 32, // Parcel content /// Can return objects set to group ReturnGroupSet = 1UL << 33, /// Can return non-group owned/set objects ReturnNonGroup = 1UL << 34, /// Can return group owned objects ReturnGroupOwned = 1UL << 48, /// Can landscape using Linden plants LandGardening = 1UL << 35, // Objects /// Can deed objects to group DeedObject = 1UL << 36, /// Can move group owned objects ObjectManipulate = 1UL << 38, /// Can set group owned objects for-sale ObjectSetForSale = 1UL << 39, /// Pay group liabilities and receive group dividends Accountable = 1UL << 40, /// List and Host group events HostEvent = 1UL << 41, // Notices and proposals /// Can send group notices SendNotices = 1UL << 42, /// Can receive group notices ReceiveNotices = 1UL << 43, /// Can create group proposals StartProposal = 1UL << 44, /// Can vote on group proposals VoteOnProposal = 1UL << 45 } #endregion Enums /// /// Handles all network traffic related to reading and writing group /// information /// public class GroupManager { #region Delegates /// The event subscribers. null if no subcribers private EventHandler m_CurrentGroups; /// Raises the CurrentGroups event /// A CurrentGroupsEventArgs object containing the /// data sent from the simulator protected virtual void OnCurrentGroups(CurrentGroupsEventArgs e) { EventHandler handler = m_CurrentGroups; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_CurrentGroupsLock = new object(); /// Raised when the simulator sends us data containing /// our current group membership public event EventHandler CurrentGroups { add { lock (m_CurrentGroupsLock) { m_CurrentGroups += value; } } remove { lock (m_CurrentGroupsLock) { m_CurrentGroups -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_GroupNames; /// Raises the GroupNamesReply event /// A GroupNamesEventArgs object containing the /// data response from the simulator protected virtual void OnGroupNamesReply(GroupNamesEventArgs e) { EventHandler handler = m_GroupNames; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_GroupNamesLock = new object(); /// Raised when the simulator responds to a RequestGroupName /// or RequestGroupNames request public event EventHandler GroupNamesReply { add { lock (m_GroupNamesLock) { m_GroupNames += value; } } remove { lock (m_GroupNamesLock) { m_GroupNames -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_GroupProfile; /// Raises the GroupProfile event /// An GroupProfileEventArgs object containing the /// data returned from the simulator protected virtual void OnGroupProfile(GroupProfileEventArgs e) { EventHandler handler = m_GroupProfile; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_GroupProfileLock = new object(); /// Raised when the simulator responds to a request public event EventHandler GroupProfile { add { lock (m_GroupProfileLock) { m_GroupProfile += value; } } remove { lock (m_GroupProfileLock) { m_GroupProfile -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_GroupMembers; /// Raises the GroupMembers event /// A GroupMembersEventArgs object containing the /// data returned from the simulator protected virtual void OnGroupMembersReply(GroupMembersReplyEventArgs e) { EventHandler handler = m_GroupMembers; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_GroupMembersLock = new object(); /// Raised when the simulator responds to a request public event EventHandler GroupMembersReply { add { lock (m_GroupMembersLock) { m_GroupMembers += value; } } remove { lock (m_GroupMembersLock) { m_GroupMembers -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_GroupRoles; /// Raises the GroupRolesDataReply event /// A GroupRolesDataReplyEventArgs object containing the /// data returned from the simulator protected virtual void OnGroupRoleDataReply(GroupRolesDataReplyEventArgs e) { EventHandler handler = m_GroupRoles; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_GroupRolesLock = new object(); /// Raised when the simulator responds to a request public event EventHandler GroupRoleDataReply { add { lock (m_GroupRolesLock) { m_GroupRoles += value; } } remove { lock (m_GroupRolesLock) { m_GroupRoles -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_GroupRoleMembers; /// Raises the GroupRoleMembersReply event /// A GroupRolesRoleMembersReplyEventArgs object containing the /// data returned from the simulator protected virtual void OnGroupRoleMembers(GroupRolesMembersReplyEventArgs e) { EventHandler handler = m_GroupRoleMembers; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_GroupRolesMembersLock = new object(); /// Raised when the simulator responds to a request public event EventHandler GroupRoleMembersReply { add { lock (m_GroupRolesMembersLock) { m_GroupRoleMembers += value; } } remove { lock (m_GroupRolesMembersLock) { m_GroupRoleMembers -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_GroupTitles; /// Raises the GroupTitlesReply event /// A GroupTitlesReplyEventArgs object containing the /// data returned from the simulator protected virtual void OnGroupTitles(GroupTitlesReplyEventArgs e) { EventHandler handler = m_GroupTitles; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_GroupTitlesLock = new object(); /// Raised when the simulator responds to a request public event EventHandler GroupTitlesReply { add { lock (m_GroupTitlesLock) { m_GroupTitles += value; } } remove { lock (m_GroupTitlesLock) { m_GroupTitles -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_GroupAccountSummary; /// Raises the GroupAccountSummary event /// A GroupAccountSummaryReplyEventArgs object containing the /// data returned from the simulator protected virtual void OnGroupAccountSummaryReply(GroupAccountSummaryReplyEventArgs e) { EventHandler handler = m_GroupAccountSummary; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_GroupAccountSummaryLock = new object(); /// Raised when a response to a RequestGroupAccountSummary is returned /// by the simulator public event EventHandler GroupAccountSummaryReply { add { lock (m_GroupAccountSummaryLock) { m_GroupAccountSummary += value; } } remove { lock (m_GroupAccountSummaryLock) { m_GroupAccountSummary -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_GroupCreated; /// Raises the GroupCreated event /// An GroupCreatedEventArgs object containing the /// data returned from the simulator protected virtual void OnGroupCreatedReply(GroupCreatedReplyEventArgs e) { EventHandler handler = m_GroupCreated; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_GroupCreatedLock = new object(); /// Raised when a request to create a group is successful public event EventHandler GroupCreatedReply { add { lock (m_GroupCreatedLock) { m_GroupCreated += value; } } remove { lock (m_GroupCreatedLock) { m_GroupCreated -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_GroupJoined; /// Raises the GroupJoined event /// A GroupOperationEventArgs object containing the /// result of the operation returned from the simulator protected virtual void OnGroupJoinedReply(GroupOperationEventArgs e) { EventHandler handler = m_GroupJoined; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_GroupJoinedLock = new object(); /// Raised when a request to join a group either /// fails or succeeds public event EventHandler GroupJoinedReply { add { lock (m_GroupJoinedLock) { m_GroupJoined += value; } } remove { lock (m_GroupJoinedLock) { m_GroupJoined -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_GroupLeft; /// Raises the GroupLeft event /// A GroupOperationEventArgs object containing the /// result of the operation returned from the simulator protected virtual void OnGroupLeaveReply(GroupOperationEventArgs e) { EventHandler handler = m_GroupLeft; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_GroupLeftLock = new object(); /// Raised when a request to leave a group either /// fails or succeeds public event EventHandler GroupLeaveReply { add { lock (m_GroupLeftLock) { m_GroupLeft += value; } } remove { lock (m_GroupLeftLock) { m_GroupLeft -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_GroupDropped; /// Raises the GroupDropped event /// An GroupDroppedEventArgs object containing the /// the group your agent left protected virtual void OnGroupDropped(GroupDroppedEventArgs e) { EventHandler handler = m_GroupDropped; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_GroupDroppedLock = new object(); /// Raised when A group is removed from the group server public event EventHandler GroupDropped { add { lock (m_GroupDroppedLock) { m_GroupDropped += value; } } remove { lock (m_GroupDroppedLock) { m_GroupDropped -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_GroupMemberEjected; /// Raises the GroupMemberEjected event /// An GroupMemberEjectedEventArgs object containing the /// data returned from the simulator protected virtual void OnGroupMemberEjected(GroupOperationEventArgs e) { EventHandler handler = m_GroupMemberEjected; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_GroupMemberEjectedLock = new object(); /// Raised when a request to eject a member from a group either /// fails or succeeds public event EventHandler GroupMemberEjected { add { lock (m_GroupMemberEjectedLock) { m_GroupMemberEjected += value; } } remove { lock (m_GroupMemberEjectedLock) { m_GroupMemberEjected -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_GroupNoticesListReply; /// Raises the GroupNoticesListReply event /// An GroupNoticesListReplyEventArgs object containing the /// data returned from the simulator protected virtual void OnGroupNoticesListReply(GroupNoticesListReplyEventArgs e) { EventHandler handler = m_GroupNoticesListReply; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_GroupNoticesListReplyLock = new object(); /// Raised when the simulator sends us group notices /// public event EventHandler GroupNoticesListReply { add { lock (m_GroupNoticesListReplyLock) { m_GroupNoticesListReply += value; } } remove { lock (m_GroupNoticesListReplyLock) { m_GroupNoticesListReply -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_GroupInvitation; /// Raises the GroupInvitation event /// An GroupInvitationEventArgs object containing the /// data returned from the simulator protected virtual void OnGroupInvitation(GroupInvitationEventArgs e) { EventHandler handler = m_GroupInvitation; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_GroupInvitationLock = new object(); /// Raised when another agent invites our avatar to join a group public event EventHandler GroupInvitation { add { lock (m_GroupInvitationLock) { m_GroupInvitation += value; } } remove { lock (m_GroupInvitationLock) { m_GroupInvitation -= value; } } } #endregion Delegates #region Events #endregion Events /// A reference to the current instance private GridClient Client; /// Currently-active group members requests private List GroupMembersRequests; /// Currently-active group roles requests private List GroupRolesRequests; /// Currently-active group role-member requests private List GroupRolesMembersRequests; /// Dictionary keeping group members while request is in progress private InternalDictionary> TempGroupMembers; /// Dictionary keeping mebmer/role mapping while request is in progress private InternalDictionary>> TempGroupRolesMembers; /// Dictionary keeping GroupRole information while request is in progress private InternalDictionary> TempGroupRoles; /// Caches group name lookups public InternalDictionary GroupName2KeyCache; /// /// Construct a new instance of the GroupManager class /// /// A reference to the current instance public GroupManager(GridClient client) { Client = client; TempGroupMembers = new InternalDictionary>(); GroupMembersRequests = new List(); TempGroupRoles = new InternalDictionary>(); GroupRolesRequests = new List(); TempGroupRolesMembers = new InternalDictionary>>(); GroupRolesMembersRequests = new List(); GroupName2KeyCache = new InternalDictionary(); Client.Self.IM += Self_IM; Client.Network.RegisterEventCallback("AgentGroupDataUpdate", new Caps.EventQueueCallback(AgentGroupDataUpdateMessageHandler)); // deprecated in simulator v1.27 Client.Network.RegisterCallback(PacketType.AgentDropGroup, AgentDropGroupHandler); Client.Network.RegisterCallback(PacketType.GroupTitlesReply, GroupTitlesReplyHandler); Client.Network.RegisterCallback(PacketType.GroupProfileReply, GroupProfileReplyHandler); Client.Network.RegisterCallback(PacketType.GroupMembersReply, GroupMembersHandler); Client.Network.RegisterCallback(PacketType.GroupRoleDataReply, GroupRoleDataReplyHandler); Client.Network.RegisterCallback(PacketType.GroupRoleMembersReply, GroupRoleMembersReplyHandler); Client.Network.RegisterCallback(PacketType.GroupActiveProposalItemReply, GroupActiveProposalItemHandler); Client.Network.RegisterCallback(PacketType.GroupVoteHistoryItemReply, GroupVoteHistoryItemHandler); Client.Network.RegisterCallback(PacketType.GroupAccountSummaryReply, GroupAccountSummaryReplyHandler); Client.Network.RegisterCallback(PacketType.CreateGroupReply, CreateGroupReplyHandler); Client.Network.RegisterCallback(PacketType.JoinGroupReply, JoinGroupReplyHandler); Client.Network.RegisterCallback(PacketType.LeaveGroupReply, LeaveGroupReplyHandler); Client.Network.RegisterCallback(PacketType.UUIDGroupNameReply, UUIDGroupNameReplyHandler); Client.Network.RegisterCallback(PacketType.EjectGroupMemberReply, EjectGroupMemberReplyHandler); Client.Network.RegisterCallback(PacketType.GroupNoticesListReply, GroupNoticesListReplyHandler); Client.Network.RegisterEventCallback("AgentDropGroup", new Caps.EventQueueCallback(AgentDropGroupMessageHandler)); } void Self_IM(object sender, InstantMessageEventArgs e) { if(m_GroupInvitation != null && e.IM.Dialog == InstantMessageDialog.GroupInvitation) { GroupInvitationEventArgs args = new GroupInvitationEventArgs(e.Simulator, e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message); OnGroupInvitation(args); if (args.Accept) { Client.Self.InstantMessage("name", e.IM.FromAgentID, "message", e.IM.IMSessionID, InstantMessageDialog.GroupInvitationAccept, InstantMessageOnline.Online, Client.Self.SimPosition, UUID.Zero, Utils.EmptyBytes); } else { Client.Self.InstantMessage("name", e.IM.FromAgentID, "message", e.IM.IMSessionID, InstantMessageDialog.GroupInvitationDecline, InstantMessageOnline.Online, Client.Self.SimPosition, UUID.Zero, new byte[1] { 0 }); } } } #region Public Methods /// /// Request a current list of groups the avatar is a member of. /// /// CAPS Event Queue must be running for this to work since the results /// come across CAPS. public void RequestCurrentGroups() { AgentDataUpdateRequestPacket request = new AgentDataUpdateRequestPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; Client.Network.SendPacket(request); } /// /// Lookup name of group based on groupID /// /// groupID of group to lookup name for. public void RequestGroupName(UUID groupID) { // if we already have this in the cache, return from cache instead of making a request if (GroupName2KeyCache.ContainsKey(groupID)) { Dictionary groupNames = new Dictionary(); lock (GroupName2KeyCache.Dictionary) groupNames.Add(groupID, GroupName2KeyCache.Dictionary[groupID]); if (m_GroupNames != null) { OnGroupNamesReply(new GroupNamesEventArgs(groupNames)); } } else { UUIDGroupNameRequestPacket req = new UUIDGroupNameRequestPacket(); UUIDGroupNameRequestPacket.UUIDNameBlockBlock[] block = new UUIDGroupNameRequestPacket.UUIDNameBlockBlock[1]; block[0] = new UUIDGroupNameRequestPacket.UUIDNameBlockBlock(); block[0].ID = groupID; req.UUIDNameBlock = block; Client.Network.SendPacket(req); } } /// /// Request lookup of multiple group names /// /// List of group IDs to request. public void RequestGroupNames(List groupIDs) { Dictionary groupNames = new Dictionary(); lock (GroupName2KeyCache.Dictionary) { foreach (UUID groupID in groupIDs) { if (GroupName2KeyCache.ContainsKey(groupID)) groupNames[groupID] = GroupName2KeyCache.Dictionary[groupID]; } } if (groupIDs.Count > 0) { UUIDGroupNameRequestPacket req = new UUIDGroupNameRequestPacket(); UUIDGroupNameRequestPacket.UUIDNameBlockBlock[] block = new UUIDGroupNameRequestPacket.UUIDNameBlockBlock[groupIDs.Count]; for (int i = 0; i < groupIDs.Count; i++) { block[i] = new UUIDGroupNameRequestPacket.UUIDNameBlockBlock(); block[i].ID = groupIDs[i]; } req.UUIDNameBlock = block; Client.Network.SendPacket(req); } // fire handler from cache if (groupNames.Count > 0 && m_GroupNames != null) { OnGroupNamesReply(new GroupNamesEventArgs(groupNames)); } } /// Lookup group profile data such as name, enrollment, founder, logo, etc /// Subscribe to OnGroupProfile event to receive the results. /// group ID (UUID) public void RequestGroupProfile(UUID group) { GroupProfileRequestPacket request = new GroupProfileRequestPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.GroupData.GroupID = group; Client.Network.SendPacket(request); } /// Request a list of group members. /// Subscribe to OnGroupMembers event to receive the results. /// group ID (UUID) /// UUID of the request, use to index into cache public UUID RequestGroupMembers(UUID group) { UUID requestID = UUID.Random(); Uri url = null; if (Client.Network.CurrentSim.Caps != null && null != (url = Client.Network.CurrentSim.Caps.CapabilityURI("GroupMemberData"))) { CapsClient req = new CapsClient(url); req.OnComplete += (client, result, error) => { if (error == null) { GroupMembersHandlerCaps(requestID, result); } }; OSDMap requestData = new OSDMap(1); requestData["group_id"] = group; req.BeginGetResponse(requestData, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT * 4); return requestID; } lock (GroupMembersRequests) GroupMembersRequests.Add(requestID); GroupMembersRequestPacket request = new GroupMembersRequestPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.GroupData.GroupID = group; request.GroupData.RequestID = requestID; Client.Network.SendPacket(request); return requestID; } /// Request group roles /// Subscribe to OnGroupRoles event to receive the results. /// group ID (UUID) /// UUID of the request, use to index into cache public UUID RequestGroupRoles(UUID group) { UUID requestID = UUID.Random(); lock (GroupRolesRequests) GroupRolesRequests.Add(requestID); GroupRoleDataRequestPacket request = new GroupRoleDataRequestPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.GroupData.GroupID = group; request.GroupData.RequestID = requestID; Client.Network.SendPacket(request); return requestID; } /// Request members (members,role) role mapping for a group. /// Subscribe to OnGroupRolesMembers event to receive the results. /// group ID (UUID) /// UUID of the request, use to index into cache public UUID RequestGroupRolesMembers(UUID group) { UUID requestID = UUID.Random(); lock (GroupRolesRequests) GroupRolesMembersRequests.Add(requestID); GroupRoleMembersRequestPacket request = new GroupRoleMembersRequestPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.GroupData.GroupID = group; request.GroupData.RequestID = requestID; Client.Network.SendPacket(request); return requestID; } /// Request a groups Titles /// Subscribe to OnGroupTitles event to receive the results. /// group ID (UUID) /// UUID of the request, use to index into cache public UUID RequestGroupTitles(UUID group) { UUID requestID = UUID.Random(); GroupTitlesRequestPacket request = new GroupTitlesRequestPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.AgentData.GroupID = group; request.AgentData.RequestID = requestID; Client.Network.SendPacket(request); return requestID; } /// Begin to get the group account summary /// Subscribe to the OnGroupAccountSummary event to receive the results. /// group ID (UUID) /// How long of an interval /// Which interval (0 for current, 1 for last) public void RequestGroupAccountSummary(UUID group, int intervalDays, int currentInterval) { GroupAccountSummaryRequestPacket p = new GroupAccountSummaryRequestPacket(); p.AgentData.AgentID = Client.Self.AgentID; p.AgentData.SessionID = Client.Self.SessionID; p.AgentData.GroupID = group; p.MoneyData.RequestID = UUID.Random(); p.MoneyData.CurrentInterval = currentInterval; p.MoneyData.IntervalDays = intervalDays; Client.Network.SendPacket(p); } /// Invites a user to a group /// The group to invite to /// A list of roles to invite a person to /// Key of person to invite public void Invite(UUID group, List roles, UUID personkey) { InviteGroupRequestPacket igp = new InviteGroupRequestPacket(); igp.AgentData = new InviteGroupRequestPacket.AgentDataBlock(); igp.AgentData.AgentID = Client.Self.AgentID; igp.AgentData.SessionID = Client.Self.SessionID; igp.GroupData = new InviteGroupRequestPacket.GroupDataBlock(); igp.GroupData.GroupID = group; igp.InviteData = new InviteGroupRequestPacket.InviteDataBlock[roles.Count]; for (int i = 0; i < roles.Count; i++) { igp.InviteData[i] = new InviteGroupRequestPacket.InviteDataBlock(); igp.InviteData[i].InviteeID = personkey; igp.InviteData[i].RoleID = roles[i]; } Client.Network.SendPacket(igp); } /// Set a group as the current active group /// group ID (UUID) public void ActivateGroup(UUID id) { ActivateGroupPacket activate = new ActivateGroupPacket(); activate.AgentData.AgentID = Client.Self.AgentID; activate.AgentData.SessionID = Client.Self.SessionID; activate.AgentData.GroupID = id; Client.Network.SendPacket(activate); } /// Change the role that determines your active title /// Group ID to use /// Role ID to change to public void ActivateTitle(UUID group, UUID role) { GroupTitleUpdatePacket gtu = new GroupTitleUpdatePacket(); gtu.AgentData.AgentID = Client.Self.AgentID; gtu.AgentData.SessionID = Client.Self.SessionID; gtu.AgentData.TitleRoleID = role; gtu.AgentData.GroupID = group; Client.Network.SendPacket(gtu); } /// Set this avatar's tier contribution /// Group ID to change tier in /// amount of tier to donate public void SetGroupContribution(UUID group, int contribution) { SetGroupContributionPacket sgp = new SetGroupContributionPacket(); sgp.AgentData.AgentID = Client.Self.AgentID; sgp.AgentData.SessionID = Client.Self.SessionID; sgp.Data.GroupID = group; sgp.Data.Contribution = contribution; Client.Network.SendPacket(sgp); } /// /// Save wheather agent wants to accept group notices and list this group in their profile /// /// Group /// Accept notices from this group /// List this group in the profile public void SetGroupAcceptNotices(UUID groupID, bool acceptNotices, bool listInProfile) { SetGroupAcceptNoticesPacket p = new SetGroupAcceptNoticesPacket(); p.AgentData.AgentID = Client.Self.AgentID; p.AgentData.SessionID = Client.Self.SessionID; p.Data.GroupID = groupID; p.Data.AcceptNotices = acceptNotices; p.NewData.ListInProfile = listInProfile; Client.Network.SendPacket(p); } /// Request to join a group /// Subscribe to OnGroupJoined event for confirmation. /// group ID (UUID) to join. public void RequestJoinGroup(UUID id) { JoinGroupRequestPacket join = new JoinGroupRequestPacket(); join.AgentData.AgentID = Client.Self.AgentID; join.AgentData.SessionID = Client.Self.SessionID; join.GroupData.GroupID = id; Client.Network.SendPacket(join); } /// /// Request to create a new group. If the group is successfully /// created, L$100 will automatically be deducted /// /// Subscribe to OnGroupCreated event to receive confirmation. /// Group struct containing the new group info public void RequestCreateGroup(Group group) { OpenMetaverse.Packets.CreateGroupRequestPacket cgrp = new CreateGroupRequestPacket(); cgrp.AgentData = new CreateGroupRequestPacket.AgentDataBlock(); cgrp.AgentData.AgentID = Client.Self.AgentID; cgrp.AgentData.SessionID = Client.Self.SessionID; cgrp.GroupData = new CreateGroupRequestPacket.GroupDataBlock(); cgrp.GroupData.AllowPublish = group.AllowPublish; cgrp.GroupData.Charter = Utils.StringToBytes(group.Charter); cgrp.GroupData.InsigniaID = group.InsigniaID; cgrp.GroupData.MaturePublish = group.MaturePublish; cgrp.GroupData.MembershipFee = group.MembershipFee; cgrp.GroupData.Name = Utils.StringToBytes(group.Name); cgrp.GroupData.OpenEnrollment = group.OpenEnrollment; cgrp.GroupData.ShowInList = group.ShowInList; Client.Network.SendPacket(cgrp); } /// Update a group's profile and other information /// Groups ID (UUID) to update. /// Group struct to update. public void UpdateGroup(UUID id, Group group) { OpenMetaverse.Packets.UpdateGroupInfoPacket cgrp = new UpdateGroupInfoPacket(); cgrp.AgentData = new UpdateGroupInfoPacket.AgentDataBlock(); cgrp.AgentData.AgentID = Client.Self.AgentID; cgrp.AgentData.SessionID = Client.Self.SessionID; cgrp.GroupData = new UpdateGroupInfoPacket.GroupDataBlock(); cgrp.GroupData.GroupID = id; cgrp.GroupData.AllowPublish = group.AllowPublish; cgrp.GroupData.Charter = Utils.StringToBytes(group.Charter); cgrp.GroupData.InsigniaID = group.InsigniaID; cgrp.GroupData.MaturePublish = group.MaturePublish; cgrp.GroupData.MembershipFee = group.MembershipFee; cgrp.GroupData.OpenEnrollment = group.OpenEnrollment; cgrp.GroupData.ShowInList = group.ShowInList; Client.Network.SendPacket(cgrp); } /// Eject a user from a group /// Group ID to eject the user from /// Avatar's key to eject public void EjectUser(UUID group, UUID member) { OpenMetaverse.Packets.EjectGroupMemberRequestPacket eject = new EjectGroupMemberRequestPacket(); eject.AgentData = new EjectGroupMemberRequestPacket.AgentDataBlock(); eject.AgentData.AgentID = Client.Self.AgentID; eject.AgentData.SessionID = Client.Self.SessionID; eject.GroupData = new EjectGroupMemberRequestPacket.GroupDataBlock(); eject.GroupData.GroupID = group; eject.EjectData = new EjectGroupMemberRequestPacket.EjectDataBlock[1]; eject.EjectData[0] = new EjectGroupMemberRequestPacket.EjectDataBlock(); eject.EjectData[0].EjecteeID = member; Client.Network.SendPacket(eject); } /// Update role information /// Modified role to be updated public void UpdateRole(GroupRole role) { OpenMetaverse.Packets.GroupRoleUpdatePacket gru = new GroupRoleUpdatePacket(); gru.AgentData.AgentID = Client.Self.AgentID; gru.AgentData.SessionID = Client.Self.SessionID; gru.AgentData.GroupID = role.GroupID; gru.RoleData = new GroupRoleUpdatePacket.RoleDataBlock[1]; gru.RoleData[0] = new GroupRoleUpdatePacket.RoleDataBlock(); gru.RoleData[0].Name = Utils.StringToBytes(role.Name); gru.RoleData[0].Description = Utils.StringToBytes(role.Description); gru.RoleData[0].Powers = (ulong)role.Powers; gru.RoleData[0].RoleID = role.ID; gru.RoleData[0].Title = Utils.StringToBytes(role.Title); gru.RoleData[0].UpdateType = (byte)GroupRoleUpdate.UpdateAll; Client.Network.SendPacket(gru); } /// Create a new group role /// Group ID to update /// Role to create public void CreateRole(UUID group, GroupRole role) { OpenMetaverse.Packets.GroupRoleUpdatePacket gru = new GroupRoleUpdatePacket(); gru.AgentData.AgentID = Client.Self.AgentID; gru.AgentData.SessionID = Client.Self.SessionID; gru.AgentData.GroupID = group; gru.RoleData = new GroupRoleUpdatePacket.RoleDataBlock[1]; gru.RoleData[0] = new GroupRoleUpdatePacket.RoleDataBlock(); gru.RoleData[0].RoleID = UUID.Random(); gru.RoleData[0].Name = Utils.StringToBytes(role.Name); gru.RoleData[0].Description = Utils.StringToBytes(role.Description); gru.RoleData[0].Powers = (ulong)role.Powers; gru.RoleData[0].Title = Utils.StringToBytes(role.Title); gru.RoleData[0].UpdateType = (byte)GroupRoleUpdate.Create; Client.Network.SendPacket(gru); } /// Delete a group role /// Group ID to update /// Role to delete public void DeleteRole(UUID group, UUID roleID) { OpenMetaverse.Packets.GroupRoleUpdatePacket gru = new GroupRoleUpdatePacket(); gru.AgentData.AgentID = Client.Self.AgentID; gru.AgentData.SessionID = Client.Self.SessionID; gru.AgentData.GroupID = group; gru.RoleData = new GroupRoleUpdatePacket.RoleDataBlock[1]; gru.RoleData[0] = new GroupRoleUpdatePacket.RoleDataBlock(); gru.RoleData[0].RoleID = roleID; gru.RoleData[0].Name = Utils.StringToBytes(string.Empty); gru.RoleData[0].Description = Utils.StringToBytes(string.Empty); gru.RoleData[0].Powers = 0u; gru.RoleData[0].Title = Utils.StringToBytes(string.Empty); gru.RoleData[0].UpdateType = (byte)GroupRoleUpdate.Delete; Client.Network.SendPacket(gru); } /// Remove an avatar from a role /// Group ID to update /// Role ID to be removed from /// Avatar's Key to remove public void RemoveFromRole(UUID group, UUID role, UUID member) { OpenMetaverse.Packets.GroupRoleChangesPacket grc = new GroupRoleChangesPacket(); grc.AgentData.AgentID = Client.Self.AgentID; grc.AgentData.SessionID = Client.Self.SessionID; grc.AgentData.GroupID = group; grc.RoleChange = new GroupRoleChangesPacket.RoleChangeBlock[1]; grc.RoleChange[0] = new GroupRoleChangesPacket.RoleChangeBlock(); //Add to members and role grc.RoleChange[0].MemberID = member; grc.RoleChange[0].RoleID = role; //1 = Remove From Role TODO: this should be in an enum grc.RoleChange[0].Change = 1; Client.Network.SendPacket(grc); } /// Assign an avatar to a role /// Group ID to update /// Role ID to assign to /// Avatar's ID to assign to role public void AddToRole(UUID group, UUID role, UUID member) { OpenMetaverse.Packets.GroupRoleChangesPacket grc = new GroupRoleChangesPacket(); grc.AgentData.AgentID = Client.Self.AgentID; grc.AgentData.SessionID = Client.Self.SessionID; grc.AgentData.GroupID = group; grc.RoleChange = new GroupRoleChangesPacket.RoleChangeBlock[1]; grc.RoleChange[0] = new GroupRoleChangesPacket.RoleChangeBlock(); //Add to members and role grc.RoleChange[0].MemberID = member; grc.RoleChange[0].RoleID = role; //0 = Add to Role TODO: this should be in an enum grc.RoleChange[0].Change = 0; Client.Network.SendPacket(grc); } /// Request the group notices list /// Group ID to fetch notices for public void RequestGroupNoticesList(UUID group) { OpenMetaverse.Packets.GroupNoticesListRequestPacket gnl = new GroupNoticesListRequestPacket(); gnl.AgentData.AgentID = Client.Self.AgentID; gnl.AgentData.SessionID = Client.Self.SessionID; gnl.Data.GroupID = group; Client.Network.SendPacket(gnl); } /// Request a group notice by key /// ID of group notice public void RequestGroupNotice(UUID noticeID) { OpenMetaverse.Packets.GroupNoticeRequestPacket gnr = new GroupNoticeRequestPacket(); gnr.AgentData.AgentID = Client.Self.AgentID; gnr.AgentData.SessionID = Client.Self.SessionID; gnr.Data.GroupNoticeID = noticeID; Client.Network.SendPacket(gnr); } /// Send out a group notice /// Group ID to update /// GroupNotice structure containing notice data public void SendGroupNotice(UUID group, GroupNotice notice) { Client.Self.InstantMessage(Client.Self.Name, group, notice.Subject + "|" + notice.Message, UUID.Zero, InstantMessageDialog.GroupNotice, InstantMessageOnline.Online, Vector3.Zero, UUID.Zero, notice.SerializeAttachment()); } /// Start a group proposal (vote) /// The Group ID to send proposal to /// GroupProposal structure containing the proposal public void StartProposal(UUID group, GroupProposal prop) { StartGroupProposalPacket p = new StartGroupProposalPacket(); p.AgentData.AgentID = Client.Self.AgentID; p.AgentData.SessionID = Client.Self.SessionID; p.ProposalData.GroupID = group; p.ProposalData.ProposalText = Utils.StringToBytes(prop.VoteText); p.ProposalData.Quorum = prop.Quorum; p.ProposalData.Majority = prop.Majority; p.ProposalData.Duration = prop.Duration; Client.Network.SendPacket(p); } /// Request to leave a group /// Subscribe to OnGroupLeft event to receive confirmation /// The group to leave public void LeaveGroup(UUID groupID) { LeaveGroupRequestPacket p = new LeaveGroupRequestPacket(); p.AgentData.AgentID = Client.Self.AgentID; p.AgentData.SessionID = Client.Self.SessionID; p.GroupData.GroupID = groupID; Client.Network.SendPacket(p); } #endregion #region Packet Handlers protected void AgentGroupDataUpdateMessageHandler(string capsKey, IMessage message, Simulator simulator) { if (m_CurrentGroups != null) { AgentGroupDataUpdateMessage msg = (AgentGroupDataUpdateMessage)message; Dictionary currentGroups = new Dictionary(); for (int i = 0; i < msg.GroupDataBlock.Length; i++) { Group group = new Group(); group.ID = msg.GroupDataBlock[i].GroupID; group.InsigniaID = msg.GroupDataBlock[i].GroupInsigniaID; group.Name = msg.GroupDataBlock[i].GroupName; group.Contribution = msg.GroupDataBlock[i].Contribution; group.AcceptNotices = msg.GroupDataBlock[i].AcceptNotices; group.Powers = msg.GroupDataBlock[i].GroupPowers; group.ListInProfile = msg.NewGroupDataBlock[i].ListInProfile; currentGroups.Add(group.ID, group); lock (GroupName2KeyCache.Dictionary) { if (!GroupName2KeyCache.Dictionary.ContainsKey(group.ID)) GroupName2KeyCache.Dictionary.Add(group.ID, group.Name); } } OnCurrentGroups(new CurrentGroupsEventArgs(currentGroups)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void AgentDropGroupHandler(object sender, PacketReceivedEventArgs e) { if (m_GroupDropped != null) { Packet packet = e.Packet; OnGroupDropped(new GroupDroppedEventArgs(((AgentDropGroupPacket)packet).AgentData.GroupID)); } } protected void AgentDropGroupMessageHandler(string capsKey, IMessage message, Simulator simulator) { if (m_GroupDropped != null) { AgentDropGroupMessage msg = (AgentDropGroupMessage)message; for (int i = 0; i < msg.AgentDataBlock.Length; i++) { OnGroupDropped(new GroupDroppedEventArgs(msg.AgentDataBlock[i].GroupID)); } } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void GroupProfileReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_GroupProfile != null) { Packet packet = e.Packet; GroupProfileReplyPacket profile = (GroupProfileReplyPacket)packet; Group group = new Group(); group.ID = profile.GroupData.GroupID; group.AllowPublish = profile.GroupData.AllowPublish; group.Charter = Utils.BytesToString(profile.GroupData.Charter); group.FounderID = profile.GroupData.FounderID; group.GroupMembershipCount = profile.GroupData.GroupMembershipCount; group.GroupRolesCount = profile.GroupData.GroupRolesCount; group.InsigniaID = profile.GroupData.InsigniaID; group.MaturePublish = profile.GroupData.MaturePublish; group.MembershipFee = profile.GroupData.MembershipFee; group.MemberTitle = Utils.BytesToString(profile.GroupData.MemberTitle); group.Money = profile.GroupData.Money; group.Name = Utils.BytesToString(profile.GroupData.Name); group.OpenEnrollment = profile.GroupData.OpenEnrollment; group.OwnerRole = profile.GroupData.OwnerRole; group.Powers = (GroupPowers)profile.GroupData.PowersMask; group.ShowInList = profile.GroupData.ShowInList; OnGroupProfile(new GroupProfileEventArgs(group)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void GroupNoticesListReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_GroupNoticesListReply != null) { Packet packet = e.Packet; GroupNoticesListReplyPacket reply = (GroupNoticesListReplyPacket)packet; List notices = new List(); foreach (GroupNoticesListReplyPacket.DataBlock entry in reply.Data) { GroupNoticesListEntry notice = new GroupNoticesListEntry(); notice.FromName = Utils.BytesToString(entry.FromName); notice.Subject = Utils.BytesToString(entry.Subject); notice.NoticeID = entry.NoticeID; notice.Timestamp = entry.Timestamp; notice.HasAttachment = entry.HasAttachment; notice.AssetType = (AssetType)entry.AssetType; notices.Add(notice); } OnGroupNoticesListReply(new GroupNoticesListReplyEventArgs(reply.AgentData.GroupID, notices)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void GroupTitlesReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_GroupTitles != null) { Packet packet = e.Packet; GroupTitlesReplyPacket titles = (GroupTitlesReplyPacket)packet; Dictionary groupTitleCache = new Dictionary(); foreach (GroupTitlesReplyPacket.GroupDataBlock block in titles.GroupData) { GroupTitle groupTitle = new GroupTitle(); groupTitle.GroupID = titles.AgentData.GroupID; groupTitle.RoleID = block.RoleID; groupTitle.Title = Utils.BytesToString(block.Title); groupTitle.Selected = block.Selected; groupTitleCache[block.RoleID] = groupTitle; } OnGroupTitles(new GroupTitlesReplyEventArgs(titles.AgentData.RequestID, titles.AgentData.GroupID, groupTitleCache)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void GroupMembersHandler(object sender, PacketReceivedEventArgs e) { Packet packet = e.Packet; GroupMembersReplyPacket members = (GroupMembersReplyPacket)packet; Dictionary groupMemberCache = null; lock (GroupMembersRequests) { // If nothing is registered to receive this RequestID drop the data if (GroupMembersRequests.Contains(members.GroupData.RequestID)) { lock (TempGroupMembers.Dictionary) { if (!TempGroupMembers.TryGetValue(members.GroupData.RequestID, out groupMemberCache)) { groupMemberCache = new Dictionary(); TempGroupMembers[members.GroupData.RequestID] = groupMemberCache; } foreach (GroupMembersReplyPacket.MemberDataBlock block in members.MemberData) { GroupMember groupMember = new GroupMember(); groupMember.ID = block.AgentID; groupMember.Contribution = block.Contribution; groupMember.IsOwner = block.IsOwner; groupMember.OnlineStatus = Utils.BytesToString(block.OnlineStatus); groupMember.Powers = (GroupPowers)block.AgentPowers; groupMember.Title = Utils.BytesToString(block.Title); groupMemberCache[block.AgentID] = groupMember; } if (groupMemberCache.Count >= members.GroupData.MemberCount) { GroupMembersRequests.Remove(members.GroupData.RequestID); TempGroupMembers.Remove(members.GroupData.RequestID); } } } } if (m_GroupMembers != null && groupMemberCache != null && groupMemberCache.Count >= members.GroupData.MemberCount) { OnGroupMembersReply(new GroupMembersReplyEventArgs(members.GroupData.RequestID, members.GroupData.GroupID, groupMemberCache)); } } protected void GroupMembersHandlerCaps(UUID requestID, OSD result) { try { OSDMap res = (OSDMap)result; int memberCount = res["member_count"]; OSDArray titlesOSD = (OSDArray)res["titles"]; string[] titles = new string[titlesOSD.Count]; for (int i = 0; i < titlesOSD.Count; i++) { titles[i] = titlesOSD[i]; } UUID groupID = res["group_id"]; GroupPowers defaultPowers = (GroupPowers)(ulong)((OSDMap)res["defaults"])["default_powers"]; OSDMap membersOSD = (OSDMap)res["members"]; Dictionary groupMembers = new Dictionary(membersOSD.Count); foreach (var memberID in membersOSD.Keys) { OSDMap member = (OSDMap)membersOSD[memberID]; GroupMember groupMember = new GroupMember(); groupMember.ID = (UUID)memberID; groupMember.Contribution = member["donated_square_meters"]; groupMember.IsOwner = "Y" == member["owner"].AsString(); groupMember.OnlineStatus = member["last_login"]; groupMember.Powers = defaultPowers; if (member.ContainsKey("powers")) { groupMember.Powers = (GroupPowers)(ulong)member["powers"]; } groupMember.Title = titles[(int)member["title"]]; groupMembers[groupMember.ID] = groupMember; } OnGroupMembersReply(new GroupMembersReplyEventArgs(requestID, groupID, groupMembers)); } catch (Exception ex) { Logger.Log("Failed to decode result of GroupMemberData capability: ", Helpers.LogLevel.Error, Client, ex); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void GroupRoleDataReplyHandler(object sender, PacketReceivedEventArgs e) { Packet packet = e.Packet; GroupRoleDataReplyPacket roles = (GroupRoleDataReplyPacket)packet; Dictionary groupRoleCache = null; lock (GroupRolesRequests) { // If nothing is registered to receive this RequestID drop the data if (GroupRolesRequests.Contains(roles.GroupData.RequestID)) { lock (TempGroupRoles.Dictionary) { if (!TempGroupRoles.TryGetValue(roles.GroupData.RequestID, out groupRoleCache)) { groupRoleCache = new Dictionary(); TempGroupRoles[roles.GroupData.RequestID] = groupRoleCache; } foreach (GroupRoleDataReplyPacket.RoleDataBlock block in roles.RoleData) { GroupRole groupRole = new GroupRole(); groupRole.GroupID = roles.GroupData.GroupID; groupRole.ID = block.RoleID; groupRole.Description = Utils.BytesToString(block.Description); groupRole.Name = Utils.BytesToString(block.Name); groupRole.Powers = (GroupPowers)block.Powers; groupRole.Title = Utils.BytesToString(block.Title); groupRoleCache[block.RoleID] = groupRole; } if (groupRoleCache.Count >= roles.GroupData.RoleCount) { GroupRolesRequests.Remove(roles.GroupData.RequestID); TempGroupRoles.Remove(roles.GroupData.RequestID); } } } } if (m_GroupRoles != null && groupRoleCache != null && groupRoleCache.Count >= roles.GroupData.RoleCount) { OnGroupRoleDataReply(new GroupRolesDataReplyEventArgs(roles.GroupData.RequestID, roles.GroupData.GroupID, groupRoleCache)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void GroupRoleMembersReplyHandler(object sender, PacketReceivedEventArgs e) { Packet packet = e.Packet; GroupRoleMembersReplyPacket members = (GroupRoleMembersReplyPacket)packet; List> groupRoleMemberCache = null; try { lock (GroupRolesMembersRequests) { // If nothing is registered to receive this RequestID drop the data if (GroupRolesMembersRequests.Contains(members.AgentData.RequestID)) { lock (TempGroupRolesMembers.Dictionary) { if (!TempGroupRolesMembers.TryGetValue(members.AgentData.RequestID, out groupRoleMemberCache)) { groupRoleMemberCache = new List>(); TempGroupRolesMembers[members.AgentData.RequestID] = groupRoleMemberCache; } foreach (GroupRoleMembersReplyPacket.MemberDataBlock block in members.MemberData) { KeyValuePair rolemember = new KeyValuePair(block.RoleID, block.MemberID); groupRoleMemberCache.Add(rolemember); } if (groupRoleMemberCache.Count >= members.AgentData.TotalPairs) { GroupRolesMembersRequests.Remove(members.AgentData.RequestID); TempGroupRolesMembers.Remove(members.AgentData.RequestID); } } } } } catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); } if (m_GroupRoleMembers != null && groupRoleMemberCache != null && groupRoleMemberCache.Count >= members.AgentData.TotalPairs) { OnGroupRoleMembers(new GroupRolesMembersReplyEventArgs(members.AgentData.RequestID, members.AgentData.GroupID, groupRoleMemberCache)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void GroupActiveProposalItemHandler(object sender, PacketReceivedEventArgs e) { //GroupActiveProposalItemReplyPacket proposal = (GroupActiveProposalItemReplyPacket)packet; // TODO: Create a proposal struct to represent the fields in a proposal item } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void GroupVoteHistoryItemHandler(object sender, PacketReceivedEventArgs e) { //GroupVoteHistoryItemReplyPacket history = (GroupVoteHistoryItemReplyPacket)packet; // TODO: This was broken in the official viewer when I was last trying to work on it } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void GroupAccountSummaryReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_GroupAccountSummary != null) { Packet packet = e.Packet; GroupAccountSummaryReplyPacket summary = (GroupAccountSummaryReplyPacket)packet; GroupAccountSummary account = new GroupAccountSummary(); account.Balance = summary.MoneyData.Balance; account.CurrentInterval = summary.MoneyData.CurrentInterval; account.GroupTaxCurrent = summary.MoneyData.GroupTaxCurrent; account.GroupTaxEstimate = summary.MoneyData.GroupTaxEstimate; account.IntervalDays = summary.MoneyData.IntervalDays; account.LandTaxCurrent = summary.MoneyData.LandTaxCurrent; account.LandTaxEstimate = summary.MoneyData.LandTaxEstimate; account.LastTaxDate = Utils.BytesToString(summary.MoneyData.LastTaxDate); account.LightTaxCurrent = summary.MoneyData.LightTaxCurrent; account.LightTaxEstimate = summary.MoneyData.LightTaxEstimate; account.NonExemptMembers = summary.MoneyData.NonExemptMembers; account.ObjectTaxCurrent = summary.MoneyData.ObjectTaxCurrent; account.ObjectTaxEstimate = summary.MoneyData.ObjectTaxEstimate; account.ParcelDirFeeCurrent = summary.MoneyData.ParcelDirFeeCurrent; account.ParcelDirFeeEstimate = summary.MoneyData.ParcelDirFeeEstimate; account.StartDate = Utils.BytesToString(summary.MoneyData.StartDate); account.TaxDate = Utils.BytesToString(summary.MoneyData.TaxDate); account.TotalCredits = summary.MoneyData.TotalCredits; account.TotalDebits = summary.MoneyData.TotalDebits; OnGroupAccountSummaryReply(new GroupAccountSummaryReplyEventArgs(summary.AgentData.GroupID, account)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void CreateGroupReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_GroupCreated != null) { Packet packet = e.Packet; CreateGroupReplyPacket reply = (CreateGroupReplyPacket)packet; string message = Utils.BytesToString(reply.ReplyData.Message); OnGroupCreatedReply(new GroupCreatedReplyEventArgs(reply.ReplyData.GroupID, reply.ReplyData.Success, message)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void JoinGroupReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_GroupJoined != null) { Packet packet = e.Packet; JoinGroupReplyPacket reply = (JoinGroupReplyPacket)packet; OnGroupJoinedReply(new GroupOperationEventArgs(reply.GroupData.GroupID, reply.GroupData.Success)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void LeaveGroupReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_GroupLeft != null) { Packet packet = e.Packet; LeaveGroupReplyPacket reply = (LeaveGroupReplyPacket)packet; OnGroupLeaveReply(new GroupOperationEventArgs(reply.GroupData.GroupID, reply.GroupData.Success)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data private void UUIDGroupNameReplyHandler(object sender, PacketReceivedEventArgs e) { Packet packet = e.Packet; UUIDGroupNameReplyPacket reply = (UUIDGroupNameReplyPacket)packet; UUIDGroupNameReplyPacket.UUIDNameBlockBlock[] blocks = reply.UUIDNameBlock; Dictionary groupNames = new Dictionary(); foreach (UUIDGroupNameReplyPacket.UUIDNameBlockBlock block in blocks) { groupNames.Add(block.ID, Utils.BytesToString(block.GroupName)); if (!GroupName2KeyCache.ContainsKey(block.ID)) GroupName2KeyCache.Add(block.ID, Utils.BytesToString(block.GroupName)); } if (m_GroupNames != null) { OnGroupNamesReply(new GroupNamesEventArgs(groupNames)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void EjectGroupMemberReplyHandler(object sender, PacketReceivedEventArgs e) { Packet packet = e.Packet; EjectGroupMemberReplyPacket reply = (EjectGroupMemberReplyPacket)packet; // TODO: On Success remove the member from the cache(s) if (m_GroupMemberEjected != null) { OnGroupMemberEjected(new GroupOperationEventArgs(reply.GroupData.GroupID, reply.EjectData.Success)); } } #endregion Packet Handlers } #region EventArgs /// Contains the current groups your agent is a member of public class CurrentGroupsEventArgs : EventArgs { private readonly Dictionary m_Groups; /// Get the current groups your agent is a member of public Dictionary Groups { get { return m_Groups; } } /// Construct a new instance of the CurrentGroupsEventArgs class /// The current groups your agent is a member of public CurrentGroupsEventArgs(Dictionary groups) { this.m_Groups = groups; } } /// A Dictionary of group names, where the Key is the groups ID and the value is the groups name public class GroupNamesEventArgs : EventArgs { private readonly Dictionary m_GroupNames; /// Get the Group Names dictionary public Dictionary GroupNames { get { return m_GroupNames; } } /// Construct a new instance of the GroupNamesEventArgs class /// The Group names dictionary public GroupNamesEventArgs(Dictionary groupNames) { this.m_GroupNames = groupNames; } } /// Represents the members of a group public class GroupMembersReplyEventArgs : EventArgs { private readonly UUID m_RequestID; private readonly UUID m_GroupID; private readonly Dictionary m_Members; /// Get the ID as returned by the request to correlate /// this result set and the request public UUID RequestID { get { return m_RequestID; } } /// Get the ID of the group public UUID GroupID { get { return m_GroupID; } } /// Get the dictionary of members public Dictionary Members { get { return m_Members; } } /// /// Construct a new instance of the GroupMembersReplyEventArgs class /// /// The ID of the request /// The ID of the group /// The membership list of the group public GroupMembersReplyEventArgs(UUID requestID, UUID groupID, Dictionary members) { this.m_RequestID = requestID; this.m_GroupID = groupID; this.m_Members = members; } } /// Represents the roles associated with a group public class GroupRolesDataReplyEventArgs : EventArgs { private readonly UUID m_RequestID; private readonly UUID m_GroupID; private readonly Dictionary m_Roles; /// Get the ID as returned by the request to correlate /// this result set and the request public UUID RequestID { get { return m_RequestID; } } /// Get the ID of the group public UUID GroupID { get { return m_GroupID; } } /// Get the dictionary containing the roles public Dictionary Roles { get { return m_Roles; } } /// Construct a new instance of the GroupRolesDataReplyEventArgs class /// The ID as returned by the request to correlate /// this result set and the request /// The ID of the group /// The dictionary containing the roles public GroupRolesDataReplyEventArgs(UUID requestID, UUID groupID, Dictionary roles) { this.m_RequestID = requestID; this.m_GroupID = groupID; this.m_Roles = roles; } } /// Represents the Role to Member mappings for a group public class GroupRolesMembersReplyEventArgs : EventArgs { private readonly UUID m_RequestID; private readonly UUID m_GroupID; private readonly List> m_RolesMembers; /// Get the ID as returned by the request to correlate /// this result set and the request public UUID RequestID { get { return m_RequestID; } } /// Get the ID of the group public UUID GroupID { get { return m_GroupID; } } /// Get the member to roles map public List> RolesMembers { get { return m_RolesMembers; } } /// Construct a new instance of the GroupRolesMembersReplyEventArgs class /// The ID as returned by the request to correlate /// this result set and the request /// The ID of the group /// The member to roles map public GroupRolesMembersReplyEventArgs(UUID requestID, UUID groupID, List> rolesMembers) { this.m_RequestID = requestID; this.m_GroupID = groupID; this.m_RolesMembers = rolesMembers; } } /// Represents the titles for a group public class GroupTitlesReplyEventArgs : EventArgs { private readonly UUID m_RequestID; private readonly UUID m_GroupID; private readonly Dictionary m_Titles; /// Get the ID as returned by the request to correlate /// this result set and the request public UUID RequestID { get { return m_RequestID; } } /// Get the ID of the group public UUID GroupID { get { return m_GroupID; } } /// Get the titles public Dictionary Titles { get { return m_Titles; } } /// Construct a new instance of the GroupTitlesReplyEventArgs class /// The ID as returned by the request to correlate /// this result set and the request /// The ID of the group /// The titles public GroupTitlesReplyEventArgs(UUID requestID, UUID groupID, Dictionary titles) { this.m_RequestID = requestID; this.m_GroupID = groupID; this.m_Titles = titles; } } /// Represents the summary data for a group public class GroupAccountSummaryReplyEventArgs : EventArgs { private readonly UUID m_GroupID; private readonly GroupAccountSummary m_Summary; /// Get the ID of the group public UUID GroupID { get { return m_GroupID; } } /// Get the summary data public GroupAccountSummary Summary { get { return m_Summary; } } /// Construct a new instance of the GroupAccountSummaryReplyEventArgs class /// The ID of the group /// The summary data public GroupAccountSummaryReplyEventArgs(UUID groupID, GroupAccountSummary summary) { this.m_GroupID = groupID; this.m_Summary = summary; } } /// A response to a group create request public class GroupCreatedReplyEventArgs : EventArgs { private readonly UUID m_GroupID; private readonly bool m_Success; private readonly string m_Message; /// Get the ID of the group public UUID GroupID { get { return m_GroupID; } } /// true of the group was created successfully public bool Success { get { return m_Success; } } /// A string containing the message public string Message { get { return m_Message; } } /// Construct a new instance of the GroupCreatedReplyEventArgs class /// The ID of the group /// the success or faulure of the request /// A string containing additional information public GroupCreatedReplyEventArgs(UUID groupID, bool success, string messsage) { this.m_GroupID = groupID; this.m_Success = success; this.m_Message = messsage; } } /// Represents a response to a request public class GroupOperationEventArgs : EventArgs { private readonly UUID m_GroupID; private readonly bool m_Success; /// Get the ID of the group public UUID GroupID { get { return m_GroupID; } } /// true of the request was successful public bool Success { get { return m_Success; } } /// Construct a new instance of the GroupOperationEventArgs class /// The ID of the group /// true of the request was successful public GroupOperationEventArgs(UUID groupID, bool success) { this.m_GroupID = groupID; this.m_Success = success; } } /// Represents your agent leaving a group public class GroupDroppedEventArgs : EventArgs { private readonly UUID m_GroupID; /// Get the ID of the group public UUID GroupID { get { return m_GroupID; } } /// Construct a new instance of the GroupDroppedEventArgs class /// The ID of the group public GroupDroppedEventArgs(UUID groupID) { m_GroupID = groupID; } } /// Represents a list of active group notices public class GroupNoticesListReplyEventArgs : EventArgs { private readonly UUID m_GroupID; private readonly List m_Notices; /// Get the ID of the group public UUID GroupID { get { return m_GroupID; } } /// Get the notices list public List Notices { get { return m_Notices; } } /// Construct a new instance of the GroupNoticesListReplyEventArgs class /// The ID of the group /// The list containing active notices public GroupNoticesListReplyEventArgs(UUID groupID, List notices) { m_GroupID = groupID; m_Notices = notices; } } /// Represents the profile of a group public class GroupProfileEventArgs : EventArgs { private readonly Group m_Group; /// Get the group profile public Group Group { get { return m_Group; } } /// Construct a new instance of the GroupProfileEventArgs class /// The group profile public GroupProfileEventArgs(Group group) { this.m_Group = group; } } /// /// Provides notification of a group invitation request sent by another Avatar /// /// The invitation is raised when another avatar makes an offer for our avatar /// to join a group. public class GroupInvitationEventArgs : EventArgs { private readonly UUID m_FromAgentID; private readonly string m_FromAgentName; private readonly string m_Message; private readonly Simulator m_Simulator; /// The ID of the Avatar sending the group invitation public UUID AgentID { get { return m_FromAgentID; } } /// The name of the Avatar sending the group invitation public string FromName { get { return m_FromAgentName; } } /// A message containing the request information which includes /// the name of the group, the groups charter and the fee to join details public string Message { get { return m_Message; } } /// The Simulator public Simulator Simulator { get { return m_Simulator; } } /// Set to true to accept invitation, false to decline public bool Accept { get; set; } public GroupInvitationEventArgs(Simulator simulator, UUID agentID, string agentName, string message) { this.m_Simulator = simulator; this.m_FromAgentID = agentID; this.m_FromAgentName = agentName; this.m_Message = message; } } #endregion }