/* * Copyright (c) 2006, Second Life Reverse Engineering Team * 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 Second Life Reverse Engineering Team 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 libsecondlife.Packets; namespace libsecondlife { /// /// Avatar group management /// public class GroupMember { /// Key of Group Member public LLUUID ID; /// Total land contribution public int Contribution; /// Online status information public string OnlineStatus; /// Abilities that the Group Member has public ulong Powers; /// Current group title public string Title; /// Is a group owner public bool IsOwner; /// /// Constructor /// /// public GroupMember(LLUUID id) { ID = id; } } /// /// Role manager for a group /// public class GroupRole { /// Key of Role public LLUUID 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 ulong Powers; /// /// Constructor for Group Roles /// /// Key associated with Group Role public GroupRole(LLUUID id) { ID = id; } } /// /// Class to represent Group Title /// public class GroupTitle { /// Group Title public string Title; /// Whether title is Active public bool Selected; } /// /// Represents a group in Second Life /// public class Group { /// Key of Group public LLUUID ID; /// Key of Group Insignia public LLUUID InsigniaID; /// Key of Group Founder public LLUUID FounderID; /// Key of Group Role for Owners public LLUUID 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 ulong 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; /// public int GroupMembershipCount; /// public int GroupRolesCount; /// /// Constructor /// /// Key of Group public Group(LLUUID id) { ID = id; InsigniaID = LLUUID.Zero; } /// /// Returns the name of the group /// /// public override string ToString() { return Name; } } /// /// Profile of a group /// public class GroupProfile { /// public LLUUID ID; /// Key of Group Insignia public LLUUID InsigniaID; /// Key of Group Founder public LLUUID FounderID; /// Key of Group Role for Owners public LLUUID OwnerRole; /// Name of Group public string Name; /// Text of Group Charter public string Charter; /// public string MemberTitle; /// public bool OpenEnrollment; /// public bool ShowInList; /// public ulong Powers; /// public bool AcceptNotices; /// public bool AllowPublish; /// public bool MaturePublish; /// public int MembershipFee; /// public int Money; /// public int Contribution; /// public int GroupMembershipCount; /// public int GroupRolesCount; } /// /// A group Vote /// public class Vote { /// Key of Avatar who created Vote public LLUUID Candidate; /// Text of the Vote proposal public string VoteString; /// Total number of votes public int NumVotes; } /// /// /// public class 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; } /// /// /// public class GroupAccountDetails { /// public int IntervalDays; /// public int CurrentInterval; /// public string StartDate; /// A list of description/amount pairs making up the account /// history public List> HistoryItems; } /// /// /// public class GroupAccountTransactions { /// public int IntervalDays; /// public int CurrentInterval; /// public string StartDate; /// List of all the transactions for this group public List Transactions; } /// /// A single transaction made by a group /// public class Transaction { /// public string Time; /// public string User; /// public int Type; /// public string Item; /// public int Amount; } /// /// Handles all network traffic related to reading and writing group /// information /// public class GroupManager { /// /// Callback for the list of groups the avatar is currently a member of /// /// public delegate void CurrentGroupsCallback(Dictionary groups); /// /// Callback for the profile of a group /// /// public delegate void GroupProfileCallback(GroupProfile group); /// /// Callback for the member list of a group /// /// public delegate void GroupMembersCallback(Dictionary members); /// /// Callback for the role list of a group /// /// public delegate void GroupRolesCallback(Dictionary roles); /// /// Callback for a pairing of roles to members /// /// public delegate void GroupRolesMembersCallback(List> rolesMembers); /// /// Callback for the title list of a group /// /// public delegate void GroupTitlesCallback(Dictionary titles); /// /// /// /// public delegate void GroupAccountSummaryCallback(GroupAccountSummary summary); /// /// /// /// public delegate void GroupAccountDetailsCallback(GroupAccountDetails details); /// /// /// /// public delegate void GroupAccountTransactionsCallback(GroupAccountTransactions transactions); private SecondLife Client; // No need for concurrency with the current group list request private CurrentGroupsCallback OnCurrentGroups; private Dictionary GroupProfileCallbacks; private Dictionary GroupMembersCallbacks; private Dictionary GroupRolesCallbacks; private Dictionary GroupRolesMembersCallbacks; private Dictionary GroupTitlesCallbacks; private Dictionary GroupAccountSummaryCallbacks; private Dictionary GroupAccountDetailsCallbacks; //TODO - presumably someone created this Dictionary so they could use it. //private Dictionary GroupAccountTransactionsCallbacks; /// A list of all the lists of group members, indexed by the request ID private Dictionary> GroupMembersCaches; /// A list of all the lists of group roles, indexed by the request ID private Dictionary> GroupRolesCaches; /// A list of all the role to member mappings private Dictionary>> GroupRolesMembersCaches; /// /// /// /// public GroupManager(SecondLife client) { Client = client; GroupProfileCallbacks = new Dictionary(); GroupMembersCallbacks = new Dictionary(); GroupRolesCallbacks = new Dictionary(); GroupRolesMembersCallbacks = new Dictionary(); GroupTitlesCallbacks = new Dictionary(); GroupAccountSummaryCallbacks = new Dictionary(); GroupAccountDetailsCallbacks = new Dictionary(); //GroupAccountTransactionsCallbacks = new Dictionary(); GroupMembersCaches = new Dictionary>(); GroupRolesCaches = new Dictionary>(); GroupRolesMembersCaches = new Dictionary>>(); Client.Network.RegisterCallback(PacketType.AgentGroupDataUpdate, new NetworkManager.PacketCallback(GroupDataHandler)); Client.Network.RegisterCallback(PacketType.GroupTitlesReply, new NetworkManager.PacketCallback(GroupTitlesHandler)); Client.Network.RegisterCallback(PacketType.GroupProfileReply, new NetworkManager.PacketCallback(GroupProfileHandler)); Client.Network.RegisterCallback(PacketType.GroupMembersReply, new NetworkManager.PacketCallback(GroupMembersHandler)); Client.Network.RegisterCallback(PacketType.GroupRoleDataReply, new NetworkManager.PacketCallback(GroupRoleDataHandler)); Client.Network.RegisterCallback(PacketType.GroupRoleMembersReply, new NetworkManager.PacketCallback(GroupRoleMembersHandler)); Client.Network.RegisterCallback(PacketType.GroupActiveProposalItemReply, new NetworkManager.PacketCallback(GroupActiveProposalItemHandler)); Client.Network.RegisterCallback(PacketType.GroupVoteHistoryItemReply, new NetworkManager.PacketCallback(GroupVoteHistoryItemHandler)); Client.Network.RegisterCallback(PacketType.GroupAccountSummaryReply, new NetworkManager.PacketCallback(GroupAccountSummaryHandler)); Client.Network.RegisterCallback(PacketType.GroupAccountDetailsReply, new NetworkManager.PacketCallback(GroupAccountDetailsHandler)); Client.Network.RegisterCallback(PacketType.GroupAccountTransactionsReply, new NetworkManager.PacketCallback(GroupAccountTransactionsHandler)); } /// /// /// /// public void BeginGetCurrentGroups(CurrentGroupsCallback cgc) { OnCurrentGroups = cgc; AgentDataUpdateRequestPacket request = new AgentDataUpdateRequestPacket(); request.AgentData.AgentID = Client.Network.AgentID; request.AgentData.SessionID = Client.Network.SessionID; Client.Network.SendPacket(request); } /// /// /// /// /// public void BeginGetGroupProfile(LLUUID group, GroupProfileCallback gpc) { GroupProfileCallbacks[group] = gpc; GroupProfileRequestPacket request = new GroupProfileRequestPacket(); request.AgentData.AgentID = Client.Network.AgentID; request.AgentData.SessionID = Client.Network.SessionID; request.GroupData.GroupID = group; Client.Network.SendPacket(request); } /// /// /// /// /// public void BeginGetGroupMembers(LLUUID group, GroupMembersCallback gmc) { LLUUID requestID = LLUUID.Random(); lock (GroupMembersCaches) { GroupMembersCaches[requestID] = new Dictionary(); } GroupMembersCallbacks[group] = gmc; GroupMembersRequestPacket request = new GroupMembersRequestPacket(); request.AgentData.AgentID = Client.Network.AgentID; request.AgentData.SessionID = Client.Network.SessionID; request.GroupData.GroupID = group; request.GroupData.RequestID = requestID; Client.Network.SendPacket(request); } /// /// /// /// /// public void BeginGetGroupRoles(LLUUID group, GroupRolesCallback grc) { LLUUID requestID = LLUUID.Random(); lock (GroupRolesCaches) { GroupRolesCaches[requestID] = new Dictionary(); } GroupRolesCallbacks[group] = grc; GroupRoleDataRequestPacket request = new GroupRoleDataRequestPacket(); request.AgentData.AgentID = Client.Network.AgentID; request.AgentData.SessionID = Client.Network.SessionID; request.GroupData.GroupID = group; request.GroupData.RequestID = requestID; Client.Network.SendPacket(request); } /// /// /// /// /// public void BeginGetGroupTitles(LLUUID group, GroupTitlesCallback gtc) { LLUUID requestID = LLUUID.Random(); GroupTitlesCallbacks[group] = gtc; GroupTitlesRequestPacket request = new GroupTitlesRequestPacket(); request.AgentData.AgentID = Client.Network.AgentID; request.AgentData.SessionID = Client.Network.SessionID; request.AgentData.GroupID = group; request.AgentData.RequestID = requestID; Client.Network.SendPacket(request); } private void GroupDataHandler(Packet packet, Simulator simulator) { if (OnCurrentGroups != null) { AgentGroupDataUpdatePacket update = (AgentGroupDataUpdatePacket)packet; Dictionary currentGroups = new Dictionary(); foreach (AgentGroupDataUpdatePacket.GroupDataBlock block in update.GroupData) { Group group = new Group(block.GroupID); group.InsigniaID = block.GroupInsigniaID; group.Name = Helpers.FieldToString(block.GroupName); group.Powers = block.GroupPowers; group.Contribution = block.Contribution; group.AcceptNotices = block.AcceptNotices; currentGroups[block.GroupID] = group; } if (OnCurrentGroups != null) { OnCurrentGroups(currentGroups); } } } private void GroupProfileHandler(Packet packet, Simulator simulator) { GroupProfileReplyPacket profile = (GroupProfileReplyPacket)packet; if (GroupProfileCallbacks.ContainsKey(profile.GroupData.GroupID)) { GroupProfile group = new GroupProfile(); group.AllowPublish = profile.GroupData.AllowPublish; group.Charter = Helpers.FieldToString(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 = Helpers.FieldToString(profile.GroupData.MemberTitle); group.Money = profile.GroupData.Money; group.Name = Helpers.FieldToString(profile.GroupData.Name); group.OpenEnrollment = profile.GroupData.OpenEnrollment; group.OwnerRole = profile.GroupData.OwnerRole; group.Powers = profile.GroupData.PowersMask; group.ShowInList = profile.GroupData.ShowInList; GroupProfileCallbacks[profile.GroupData.GroupID](group); } } private void GroupTitlesHandler(Packet packet, Simulator simulator) { GroupTitlesReplyPacket titles = (GroupTitlesReplyPacket)packet; Dictionary groupTitleCache = new Dictionary(); foreach (GroupTitlesReplyPacket.GroupDataBlock block in titles.GroupData) { GroupTitle groupTitle = new GroupTitle(); groupTitle.Title = Helpers.FieldToString(block.Title); groupTitle.Selected = block.Selected; groupTitleCache[block.RoleID] = groupTitle; } GroupTitlesCallbacks[titles.AgentData.GroupID](groupTitleCache); } private void GroupMembersHandler(Packet packet, Simulator simulator) { GroupMembersReplyPacket members = (GroupMembersReplyPacket)packet; Dictionary groupMemberCache = null; lock (GroupMembersCaches) { // If nothing is registered to receive this RequestID drop the data if (GroupMembersCaches.ContainsKey(members.GroupData.RequestID)) { groupMemberCache = GroupMembersCaches[members.GroupData.RequestID]; foreach (GroupMembersReplyPacket.MemberDataBlock block in members.MemberData) { GroupMember groupMember = new GroupMember(block.AgentID); groupMember.Contribution = block.Contribution; groupMember.IsOwner = block.IsOwner; groupMember.OnlineStatus = Helpers.FieldToString(block.OnlineStatus); groupMember.Powers = block.AgentPowers; groupMember.Title = Helpers.FieldToString(block.Title); groupMemberCache[block.AgentID] = groupMember; } } } // Check if we've received all the group members that are showing up if (groupMemberCache != null && groupMemberCache.Count >= members.GroupData.MemberCount) { GroupMembersCallbacks[members.GroupData.GroupID](groupMemberCache); } } private void GroupRoleDataHandler(Packet packet, Simulator simulator) { GroupRoleDataReplyPacket roles = (GroupRoleDataReplyPacket)packet; Dictionary groupRoleCache = null; lock (GroupRolesCaches) { // If nothing is registered to receive this RequestID drop the data if (GroupRolesCaches.ContainsKey(roles.GroupData.RequestID)) { groupRoleCache = GroupRolesCaches[roles.GroupData.RequestID]; foreach (GroupRoleDataReplyPacket.RoleDataBlock block in roles.RoleData) { GroupRole groupRole = new GroupRole(block.RoleID); groupRole.Description = Helpers.FieldToString(block.Description); groupRole.Name = Helpers.FieldToString(block.Name); groupRole.Powers = block.Powers; groupRole.Title = Helpers.FieldToString(block.Title); groupRoleCache[block.RoleID] = groupRole; } } } // Check if we've received all the group members that are showing up if (groupRoleCache != null && groupRoleCache.Count >= roles.GroupData.RoleCount) { GroupRolesCallbacks[roles.GroupData.GroupID](groupRoleCache); } } private void GroupRoleMembersHandler(Packet packet, Simulator simulator) { GroupRoleMembersReplyPacket members = (GroupRoleMembersReplyPacket)packet; List> groupRoleMemberCache = null; lock (GroupRolesMembersCaches) { // If nothing is registered to receive this RequestID drop the data if (GroupRolesMembersCaches.ContainsKey(members.AgentData.RequestID)) { groupRoleMemberCache = GroupRolesMembersCaches[members.AgentData.RequestID]; int i = 0; foreach (GroupRoleMembersReplyPacket.MemberDataBlock block in members.MemberData) { KeyValuePair rolemember = new KeyValuePair(block.RoleID, block.MemberID); groupRoleMemberCache[i++] = rolemember; } } } // Check if we've received all the pairs that are showing up if (groupRoleMemberCache != null && groupRoleMemberCache.Count >= members.AgentData.TotalPairs) { GroupRolesMembersCallbacks[members.AgentData.GroupID](groupRoleMemberCache); } } private void GroupActiveProposalItemHandler(Packet packet, Simulator simulator) { //GroupActiveProposalItemReplyPacket proposal = (GroupActiveProposalItemReplyPacket)packet; // TODO: Create a proposal class to represent the fields in a proposal item } private void GroupVoteHistoryItemHandler(Packet packet, Simulator simulator) { //GroupVoteHistoryItemReplyPacket history = (GroupVoteHistoryItemReplyPacket)packet; // TODO: This was broken in the official viewer when I was last trying to work on it } private void GroupAccountSummaryHandler(Packet packet, Simulator simulator) { GroupAccountSummaryReplyPacket summary = (GroupAccountSummaryReplyPacket)packet; if (GroupAccountSummaryCallbacks.ContainsKey(summary.AgentData.GroupID)) { 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 = Helpers.FieldToString(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 = Helpers.FieldToString(summary.MoneyData.StartDate); account.TaxDate = Helpers.FieldToString(summary.MoneyData.TaxDate); account.TotalCredits = summary.MoneyData.TotalCredits; account.TotalDebits = summary.MoneyData.TotalDebits; GroupAccountSummaryCallbacks[summary.AgentData.GroupID](account); } } private void GroupAccountDetailsHandler(Packet packet, Simulator simulator) { GroupAccountDetailsReplyPacket details = (GroupAccountDetailsReplyPacket)packet; if (GroupAccountDetailsCallbacks.ContainsKey(details.AgentData.GroupID)) { GroupAccountDetails account = new GroupAccountDetails(); account.CurrentInterval = details.MoneyData.CurrentInterval; account.IntervalDays = details.MoneyData.IntervalDays; account.StartDate = Helpers.FieldToString(details.MoneyData.StartDate); account.HistoryItems = new List>(); foreach (GroupAccountDetailsReplyPacket.HistoryDataBlock block in details.HistoryData) { KeyValuePair item = new KeyValuePair(Helpers.FieldToString(block.Description), block.Amount); account.HistoryItems.Add(item); } GroupAccountDetailsCallbacks[details.AgentData.GroupID](account); } } private void GroupAccountTransactionsHandler(Packet packet, Simulator simulator) { //GroupAccountTransactionsReplyPacket transactions = (GroupAccountTransactionsReplyPacket)packet; // TODO: This one is slightly different than the previous two //if (GroupAccountTransactionsCallbacks.ContainsKey(transactions.AgentData.GroupID)) //{ // GroupAccountTransactions account = new GroupAccountTransactions(); // ; // GroupAccountTransactionsCallbacks[transactions.AgentData.GroupID](account); //} } } }