diff --git a/OpenSim/Addons/Groups/ForeignImporter.cs b/OpenSim/Addons/Groups/ForeignImporter.cs new file mode 100644 index 0000000000..788d21dfc5 --- /dev/null +++ b/OpenSim/Addons/Groups/ForeignImporter.cs @@ -0,0 +1,77 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Groups +{ + public class ForeignImporter + { + IUserManagement m_UserManagement; + public ForeignImporter(IUserManagement uman) + { + m_UserManagement = uman; + } + + public GroupMembersData ConvertGroupMembersData(ExtendedGroupMembersData _m) + { + GroupMembersData m = new GroupMembersData(); + m.AcceptNotices = _m.AcceptNotices; + m.AgentPowers = _m.AgentPowers; + m.Contribution = _m.Contribution; + m.IsOwner = _m.IsOwner; + m.ListInProfile = _m.ListInProfile; + m.OnlineStatus = _m.OnlineStatus; + m.Title = _m.Title; + + string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty; + Util.ParseUniversalUserIdentifier(_m.AgentID, out m.AgentID, out url, out first, out last, out tmp); + if (url != string.Empty) + m_UserManagement.AddUser(m.AgentID, first, last, url); + + return m; + } + + public GroupRoleMembersData ConvertGroupRoleMembersData(ExtendedGroupRoleMembersData _rm) + { + GroupRoleMembersData rm = new GroupRoleMembersData(); + rm.RoleID = _rm.RoleID; + + string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty; + Util.ParseUniversalUserIdentifier(_rm.MemberID, out rm.MemberID, out url, out first, out last, out tmp); + if (url != string.Empty) + m_UserManagement.AddUser(rm.MemberID, first, last, url); + + return rm; + } + + } +} diff --git a/OpenSim/Addons/Groups/GroupsExtendedData.cs b/OpenSim/Addons/Groups/GroupsExtendedData.cs new file mode 100644 index 0000000000..6f4db286f4 --- /dev/null +++ b/OpenSim/Addons/Groups/GroupsExtendedData.cs @@ -0,0 +1,509 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Groups +{ + public class ExtendedGroupRecord : GroupRecord + { + public int MemberCount; + public int RoleCount; + public string ServiceLocation; + public string FounderUUI; + } + + public class ExtendedGroupMembershipData : GroupMembershipData + { + public string AccessToken; + } + + public class ExtendedGroupMembersData + { + // This is the only difference: this is a string + public string AgentID; + public int Contribution; + public string OnlineStatus; + public ulong AgentPowers; + public string Title; + public bool IsOwner; + public bool ListInProfile; + public bool AcceptNotices; + public string AccessToken; + } + + public class ExtendedGroupRoleMembersData + { + public UUID RoleID; + // This is the only difference: this is a string + public string MemberID; + + } + + public struct ExtendedGroupNoticeData + { + public UUID NoticeID; + public uint Timestamp; + public string FromName; + public string Subject; + public bool HasAttachment; + public byte AttachmentType; + public string AttachmentName; + public UUID AttachmentItemID; + public string AttachmentOwnerID; + + public GroupNoticeData ToGroupNoticeData() + { + GroupNoticeData n = new GroupNoticeData(); + n.FromName = this.FromName; + n.AssetType = this.AttachmentType; + n.HasAttachment = this.HasAttachment; + n.NoticeID = this.NoticeID; + n.Subject = this.Subject; + n.Timestamp = this.Timestamp; + + return n; + } + } + + public class GroupsDataUtils + { + public static string Sanitize(string s) + { + return s == null ? string.Empty : s; + } + + public static Dictionary GroupRecord(ExtendedGroupRecord grec) + { + Dictionary dict = new Dictionary(); + if (grec == null) + return dict; + + dict["AllowPublish"] = grec.AllowPublish.ToString(); + dict["Charter"] = Sanitize(grec.Charter); + dict["FounderID"] = grec.FounderID.ToString(); + dict["FounderUUI"] = Sanitize(grec.FounderUUI); + dict["GroupID"] = grec.GroupID.ToString(); + dict["GroupName"] = Sanitize(grec.GroupName); + dict["InsigniaID"] = grec.GroupPicture.ToString(); + dict["MaturePublish"] = grec.MaturePublish.ToString(); + dict["MembershipFee"] = grec.MembershipFee.ToString(); + dict["OpenEnrollment"] = grec.OpenEnrollment.ToString(); + dict["OwnerRoleID"] = grec.OwnerRoleID.ToString(); + dict["ServiceLocation"] = Sanitize(grec.ServiceLocation); + dict["ShownInList"] = grec.ShowInList.ToString(); + dict["MemberCount"] = grec.MemberCount.ToString(); + dict["RoleCount"] = grec.RoleCount.ToString(); + + return dict; + } + + public static ExtendedGroupRecord GroupRecord(Dictionary dict) + { + if (dict == null) + return null; + + ExtendedGroupRecord grec = new ExtendedGroupRecord(); + if (dict.ContainsKey("AllowPublish") && dict["AllowPublish"] != null) + grec.AllowPublish = bool.Parse(dict["AllowPublish"].ToString()); + + if (dict.ContainsKey("Charter") && dict["Charter"] != null) + grec.Charter = dict["Charter"].ToString(); + else + grec.Charter = string.Empty; + + if (dict.ContainsKey("FounderID") && dict["FounderID"] != null) + grec.FounderID = UUID.Parse(dict["FounderID"].ToString()); + + if (dict.ContainsKey("FounderUUI") && dict["FounderUUI"] != null) + grec.FounderUUI = dict["FounderUUI"].ToString(); + else + grec.FounderUUI = string.Empty; + + if (dict.ContainsKey("GroupID") && dict["GroupID"] != null) + grec.GroupID = UUID.Parse(dict["GroupID"].ToString()); + + if (dict.ContainsKey("GroupName") && dict["GroupName"] != null) + grec.GroupName = dict["GroupName"].ToString(); + else + grec.GroupName = string.Empty; + + if (dict.ContainsKey("InsigniaID") && dict["InsigniaID"] != null) + grec.GroupPicture = UUID.Parse(dict["InsigniaID"].ToString()); + + if (dict.ContainsKey("MaturePublish") && dict["MaturePublish"] != null) + grec.MaturePublish = bool.Parse(dict["MaturePublish"].ToString()); + + if (dict.ContainsKey("MembershipFee") && dict["MembershipFee"] != null) + grec.MembershipFee = Int32.Parse(dict["MembershipFee"].ToString()); + + if (dict.ContainsKey("OpenEnrollment") && dict["OpenEnrollment"] != null) + grec.OpenEnrollment = bool.Parse(dict["OpenEnrollment"].ToString()); + + if (dict.ContainsKey("OwnerRoleID") && dict["OwnerRoleID"] != null) + grec.OwnerRoleID = UUID.Parse(dict["OwnerRoleID"].ToString()); + + if (dict.ContainsKey("ServiceLocation") && dict["ServiceLocation"] != null) + grec.ServiceLocation = dict["ServiceLocation"].ToString(); + else + grec.GroupName = string.Empty; + + if (dict.ContainsKey("ShownInList") && dict["ShownInList"] != null) + grec.ShowInList = bool.Parse(dict["ShownInList"].ToString()); + + if (dict.ContainsKey("MemberCount") && dict["MemberCount"] != null) + grec.MemberCount = Int32.Parse(dict["MemberCount"].ToString()); + + if (dict.ContainsKey("RoleCount") && dict["RoleCount"] != null) + grec.RoleCount = Int32.Parse(dict["RoleCount"].ToString()); + + return grec; + } + + public static Dictionary GroupMembershipData(ExtendedGroupMembershipData membership) + { + Dictionary dict = new Dictionary(); + if (membership == null) + return dict; + + dict["AcceptNotices"] = membership.AcceptNotices.ToString(); + dict["AccessToken"] = Sanitize(membership.AccessToken); + dict["Active"] = membership.Active.ToString(); + dict["ActiveRole"] = membership.ActiveRole.ToString(); + dict["AllowPublish"] = membership.AllowPublish.ToString(); + dict["Charter"] = Sanitize(membership.Charter); + dict["Contribution"] = membership.Contribution.ToString(); + dict["FounderID"] = membership.FounderID.ToString(); + dict["GroupID"] = membership.GroupID.ToString(); + dict["GroupName"] = Sanitize(membership.GroupName); + dict["GroupPicture"] = membership.GroupPicture.ToString(); + dict["GroupPowers"] = membership.GroupPowers.ToString(); + dict["GroupTitle"] = Sanitize(membership.GroupTitle); + dict["ListInProfile"] = membership.ListInProfile.ToString(); + dict["MaturePublish"] = membership.MaturePublish.ToString(); + dict["MembershipFee"] = membership.MembershipFee.ToString(); + dict["OpenEnrollment"] = membership.OpenEnrollment.ToString(); + dict["ShowInList"] = membership.ShowInList.ToString(); + + return dict; + } + + public static ExtendedGroupMembershipData GroupMembershipData(Dictionary dict) + { + if (dict == null) + return null; + + ExtendedGroupMembershipData membership = new ExtendedGroupMembershipData(); + + if (dict.ContainsKey("AcceptNotices") && dict["AcceptNotices"] != null) + membership.AcceptNotices = bool.Parse(dict["AcceptNotices"].ToString()); + + if (dict.ContainsKey("AccessToken") && dict["AccessToken"] != null) + membership.AccessToken = dict["AccessToken"].ToString(); + else + membership.AccessToken = string.Empty; + + if (dict.ContainsKey("Active") && dict["Active"] != null) + membership.Active = bool.Parse(dict["Active"].ToString()); + + if (dict.ContainsKey("ActiveRole") && dict["ActiveRole"] != null) + membership.ActiveRole = UUID.Parse(dict["ActiveRole"].ToString()); + + if (dict.ContainsKey("AllowPublish") && dict["AllowPublish"] != null) + membership.AllowPublish = bool.Parse(dict["AllowPublish"].ToString()); + + if (dict.ContainsKey("Charter") && dict["Charter"] != null) + membership.Charter = dict["Charter"].ToString(); + else + membership.Charter = string.Empty; + + if (dict.ContainsKey("Contribution") && dict["Contribution"] != null) + membership.Contribution = Int32.Parse(dict["Contribution"].ToString()); + + if (dict.ContainsKey("FounderID") && dict["FounderID"] != null) + membership.FounderID = UUID.Parse(dict["FounderID"].ToString()); + + if (dict.ContainsKey("GroupID") && dict["GroupID"] != null) + membership.GroupID = UUID.Parse(dict["GroupID"].ToString()); + + if (dict.ContainsKey("GroupName") && dict["GroupName"] != null) + membership.GroupName = dict["GroupName"].ToString(); + else + membership.GroupName = string.Empty; + + if (dict.ContainsKey("GroupPicture") && dict["GroupPicture"] != null) + membership.GroupPicture = UUID.Parse(dict["GroupPicture"].ToString()); + + if (dict.ContainsKey("GroupPowers") && dict["GroupPowers"] != null) + membership.GroupPowers = UInt64.Parse(dict["GroupPowers"].ToString()); + + if (dict.ContainsKey("GroupTitle") && dict["GroupTitle"] != null) + membership.GroupTitle = dict["GroupTitle"].ToString(); + else + membership.GroupTitle = string.Empty; + + if (dict.ContainsKey("ListInProfile") && dict["ListInProfile"] != null) + membership.ListInProfile = bool.Parse(dict["ListInProfile"].ToString()); + + if (dict.ContainsKey("MaturePublish") && dict["MaturePublish"] != null) + membership.MaturePublish = bool.Parse(dict["MaturePublish"].ToString()); + + if (dict.ContainsKey("MembershipFee") && dict["MembershipFee"] != null) + membership.MembershipFee = Int32.Parse(dict["MembershipFee"].ToString()); + + if (dict.ContainsKey("OpenEnrollment") && dict["OpenEnrollment"] != null) + membership.OpenEnrollment = bool.Parse(dict["OpenEnrollment"].ToString()); + + if (dict.ContainsKey("ShowInList") && dict["ShowInList"] != null) + membership.ShowInList = bool.Parse(dict["ShowInList"].ToString()); + + return membership; + } + + public static Dictionary GroupMembersData(ExtendedGroupMembersData member) + { + Dictionary dict = new Dictionary(); + + dict["AcceptNotices"] = member.AcceptNotices.ToString(); + dict["AccessToken"] = Sanitize(member.AccessToken); + dict["AgentID"] = Sanitize(member.AgentID); + dict["AgentPowers"] = member.AgentPowers.ToString(); + dict["Contribution"] = member.Contribution.ToString(); + dict["IsOwner"] = member.IsOwner.ToString(); + dict["ListInProfile"] = member.ListInProfile.ToString(); + dict["OnlineStatus"] = Sanitize(member.OnlineStatus); + dict["Title"] = Sanitize(member.Title); + + return dict; + } + + public static ExtendedGroupMembersData GroupMembersData(Dictionary dict) + { + ExtendedGroupMembersData member = new ExtendedGroupMembersData(); + + if (dict == null) + return member; + + if (dict.ContainsKey("AcceptNotices") && dict["AcceptNotices"] != null) + member.AcceptNotices = bool.Parse(dict["AcceptNotices"].ToString()); + + if (dict.ContainsKey("AccessToken") && dict["AccessToken"] != null) + member.AccessToken = Sanitize(dict["AccessToken"].ToString()); + else + member.AccessToken = string.Empty; + + if (dict.ContainsKey("AgentID") && dict["AgentID"] != null) + member.AgentID = Sanitize(dict["AgentID"].ToString()); + else + member.AgentID = UUID.Zero.ToString(); + + if (dict.ContainsKey("AgentPowers") && dict["AgentPowers"] != null) + member.AgentPowers = UInt64.Parse(dict["AgentPowers"].ToString()); + + if (dict.ContainsKey("Contribution") && dict["Contribution"] != null) + member.Contribution = Int32.Parse(dict["Contribution"].ToString()); + + if (dict.ContainsKey("IsOwner") && dict["IsOwner"] != null) + member.IsOwner = bool.Parse(dict["IsOwner"].ToString()); + + if (dict.ContainsKey("ListInProfile") && dict["ListInProfile"] != null) + member.ListInProfile = bool.Parse(dict["ListInProfile"].ToString()); + + if (dict.ContainsKey("OnlineStatus") && dict["OnlineStatus"] != null) + member.OnlineStatus = Sanitize(dict["OnlineStatus"].ToString()); + else + member.OnlineStatus = string.Empty; + + if (dict.ContainsKey("Title") && dict["Title"] != null) + member.Title = Sanitize(dict["Title"].ToString()); + else + member.Title = string.Empty; + + return member; + } + + public static Dictionary GroupRolesData(GroupRolesData role) + { + Dictionary dict = new Dictionary(); + + dict["Description"] = Sanitize(role.Description); + dict["Members"] = role.Members.ToString(); + dict["Name"] = Sanitize(role.Name); + dict["Powers"] = role.Powers.ToString(); + dict["RoleID"] = role.RoleID.ToString(); + dict["Title"] = Sanitize(role.Title); + + return dict; + } + + public static GroupRolesData GroupRolesData(Dictionary dict) + { + GroupRolesData role = new GroupRolesData(); + + if (dict == null) + return role; + + if (dict.ContainsKey("Description") && dict["Description"] != null) + role.Description = Sanitize(dict["Description"].ToString()); + else + role.Description = string.Empty; + + if (dict.ContainsKey("Members") && dict["Members"] != null) + role.Members = Int32.Parse(dict["Members"].ToString()); + + if (dict.ContainsKey("Name") && dict["Name"] != null) + role.Name = Sanitize(dict["Name"].ToString()); + else + role.Name = string.Empty; + + if (dict.ContainsKey("Powers") && dict["Powers"] != null) + role.Powers = UInt64.Parse(dict["Powers"].ToString()); + + if (dict.ContainsKey("Title") && dict["Title"] != null) + role.Title = Sanitize(dict["Title"].ToString()); + else + role.Title = string.Empty; + + if (dict.ContainsKey("RoleID") && dict["RoleID"] != null) + role.RoleID = UUID.Parse(dict["RoleID"].ToString()); + + return role; + } + + public static Dictionary GroupRoleMembersData(ExtendedGroupRoleMembersData rmember) + { + Dictionary dict = new Dictionary(); + + dict["RoleID"] = rmember.RoleID.ToString(); + dict["MemberID"] = rmember.MemberID; + return dict; + } + + public static ExtendedGroupRoleMembersData GroupRoleMembersData(Dictionary dict) + { + ExtendedGroupRoleMembersData rmember = new ExtendedGroupRoleMembersData(); + + if (dict.ContainsKey("RoleID") && dict["RoleID"] != null) + rmember.RoleID = new UUID(dict["RoleID"].ToString()); + + if (dict.ContainsKey("MemberID") && dict["MemberID"] != null) + rmember.MemberID = dict["MemberID"].ToString(); + + return rmember; + } + + public static Dictionary GroupInviteInfo(GroupInviteInfo invite) + { + Dictionary dict = new Dictionary(); + + dict["InviteID"] = invite.InviteID.ToString(); + dict["GroupID"] = invite.GroupID.ToString(); + dict["RoleID"] = invite.RoleID.ToString(); + dict["AgentID"] = invite.AgentID; + + return dict; + } + + public static GroupInviteInfo GroupInviteInfo(Dictionary dict) + { + if (dict == null) + return null; + + GroupInviteInfo invite = new GroupInviteInfo(); + + invite.InviteID = new UUID(dict["InviteID"].ToString()); + invite.GroupID = new UUID(dict["GroupID"].ToString()); + invite.RoleID = new UUID(dict["RoleID"].ToString()); + invite.AgentID = Sanitize(dict["AgentID"].ToString()); + + return invite; + } + + public static Dictionary GroupNoticeData(ExtendedGroupNoticeData notice) + { + Dictionary dict = new Dictionary(); + + dict["NoticeID"] = notice.NoticeID.ToString(); + dict["Timestamp"] = notice.Timestamp.ToString(); + dict["FromName"] = Sanitize(notice.FromName); + dict["Subject"] = Sanitize(notice.Subject); + dict["HasAttachment"] = notice.HasAttachment.ToString(); + dict["AttachmentItemID"] = notice.AttachmentItemID.ToString(); + dict["AttachmentName"] = Sanitize(notice.AttachmentName); + dict["AttachmentType"] = notice.AttachmentType.ToString(); + dict["AttachmentOwnerID"] = Sanitize(notice.AttachmentOwnerID); + + return dict; + } + + public static ExtendedGroupNoticeData GroupNoticeData(Dictionary dict) + { + ExtendedGroupNoticeData notice = new ExtendedGroupNoticeData(); + + if (dict == null) + return notice; + + notice.NoticeID = new UUID(dict["NoticeID"].ToString()); + notice.Timestamp = UInt32.Parse(dict["Timestamp"].ToString()); + notice.FromName = Sanitize(dict["FromName"].ToString()); + notice.Subject = Sanitize(dict["Subject"].ToString()); + notice.HasAttachment = bool.Parse(dict["HasAttachment"].ToString()); + notice.AttachmentItemID = new UUID(dict["AttachmentItemID"].ToString()); + notice.AttachmentName = dict["AttachmentName"].ToString(); + notice.AttachmentType = byte.Parse(dict["AttachmentType"].ToString()); + notice.AttachmentOwnerID = dict["AttachmentOwnerID"].ToString(); + + return notice; + } + + public static Dictionary GroupNoticeInfo(GroupNoticeInfo notice) + { + Dictionary dict = GroupNoticeData(notice.noticeData); + + dict["GroupID"] = notice.GroupID.ToString(); + dict["Message"] = Sanitize(notice.Message); + + return dict; + } + + public static GroupNoticeInfo GroupNoticeInfo(Dictionary dict) + { + GroupNoticeInfo notice = new GroupNoticeInfo(); + + notice.noticeData = GroupNoticeData(dict); + notice.GroupID = new UUID(dict["GroupID"].ToString()); + notice.Message = Sanitize(dict["Message"].ToString()); + + return notice; + } + } + +} diff --git a/OpenSim/Addons/Groups/GroupsMessagingModule.cs b/OpenSim/Addons/Groups/GroupsMessagingModule.cs new file mode 100644 index 0000000000..d172d48c6c --- /dev/null +++ b/OpenSim/Addons/Groups/GroupsMessagingModule.cs @@ -0,0 +1,594 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.Linq; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; + +namespace OpenSim.Groups +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsMessagingModule")] + public class GroupsMessagingModule : ISharedRegionModule, IGroupsMessagingModule + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private List m_sceneList = new List(); + private IPresenceService m_presenceService; + + private IMessageTransferModule m_msgTransferModule = null; + + private IGroupsServicesConnector m_groupData = null; + + // Config Options + private bool m_groupMessagingEnabled = false; + private bool m_debugEnabled = true; + + /// + /// If enabled, module only tries to send group IMs to online users by querying cached presence information. + /// + private bool m_messageOnlineAgentsOnly; + + /// + /// Cache for online users. + /// + /// + /// Group ID is key, presence information for online members is value. + /// Will only be non-null if m_messageOnlineAgentsOnly = true + /// We cache here so that group messages don't constantly have to re-request the online user list to avoid + /// attempted expensive sending of messages to offline users. + /// The tradeoff is that a user that comes online will not receive messages consistently from all other users + /// until caches have updated. + /// Therefore, we set the cache expiry to just 20 seconds. + /// + private ExpiringCache m_usersOnlineCache; + + private int m_usersOnlineCacheExpirySeconds = 20; + + #region Region Module interfaceBase Members + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + + if (groupsConfig == null) + // Do not run this module by default. + return; + + // if groups aren't enabled, we're not needed. + // if we're not specified as the connector to use, then we're not wanted + if ((groupsConfig.GetBoolean("Enabled", false) == false) + || (groupsConfig.GetString("MessagingModule", "") != Name)) + { + m_groupMessagingEnabled = false; + return; + } + + m_groupMessagingEnabled = groupsConfig.GetBoolean("MessagingEnabled", true); + + if (!m_groupMessagingEnabled) + return; + + m_messageOnlineAgentsOnly = groupsConfig.GetBoolean("MessageOnlineUsersOnly", false); + + if (m_messageOnlineAgentsOnly) + m_usersOnlineCache = new ExpiringCache(); + + m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", true); + + m_log.InfoFormat( + "[Groups.Messaging]: GroupsMessagingModule enabled with MessageOnlineOnly = {0}, DebugEnabled = {1}", + m_messageOnlineAgentsOnly, m_debugEnabled); + } + + public void AddRegion(Scene scene) + { + if (!m_groupMessagingEnabled) + return; + + scene.RegisterModuleInterface(this); + m_sceneList.Add(scene); + + scene.EventManager.OnNewClient += OnNewClient; + scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; + scene.EventManager.OnClientLogin += OnClientLogin; + } + + public void RegionLoaded(Scene scene) + { + if (!m_groupMessagingEnabled) + return; + + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData = scene.RequestModuleInterface(); + + // No groups module, no groups messaging + if (m_groupData == null) + { + m_log.Error("[Groups.Messaging]: Could not get IGroupsServicesConnector, GroupsMessagingModule is now disabled."); + RemoveRegion(scene); + return; + } + + m_msgTransferModule = scene.RequestModuleInterface(); + + // No message transfer module, no groups messaging + if (m_msgTransferModule == null) + { + m_log.Error("[Groups.Messaging]: Could not get MessageTransferModule"); + RemoveRegion(scene); + return; + } + + if (m_presenceService == null) + m_presenceService = scene.PresenceService; + + } + + public void RemoveRegion(Scene scene) + { + if (!m_groupMessagingEnabled) + return; + + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_sceneList.Remove(scene); + scene.EventManager.OnNewClient -= OnNewClient; + scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; + scene.EventManager.OnClientLogin -= OnClientLogin; + scene.UnregisterModuleInterface(this); + } + + public void Close() + { + if (!m_groupMessagingEnabled) + return; + + if (m_debugEnabled) m_log.Debug("[Groups.Messaging]: Shutting down GroupsMessagingModule module."); + + m_sceneList.Clear(); + + m_groupData = null; + m_msgTransferModule = null; + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public string Name + { + get { return "Groups Messaging Module V2"; } + } + + public void PostInitialise() + { + // NoOp + } + + #endregion + + + /// + /// Not really needed, but does confirm that the group exists. + /// + public bool StartGroupChatSession(UUID agentID, UUID groupID) + { + if (m_debugEnabled) + m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null); + + if (groupInfo != null) + { + return true; + } + else + { + return false; + } + } + + public void SendMessageToGroup(GridInstantMessage im, UUID groupID) + { + List groupMembers = m_groupData.GetGroupMembers(new UUID(im.fromAgentID).ToString(), groupID); + int groupMembersCount = groupMembers.Count; + + if (m_messageOnlineAgentsOnly) + { + string[] t1 = groupMembers.ConvertAll(gmd => gmd.AgentID.ToString()).ToArray(); + + // We cache in order not to overwhlem the presence service on large grids with many groups. This does + // mean that members coming online will not see all group members until after m_usersOnlineCacheExpirySeconds has elapsed. + // (assuming this is the same across all grid simulators). + PresenceInfo[] onlineAgents; + if (!m_usersOnlineCache.TryGetValue(groupID, out onlineAgents)) + { + onlineAgents = m_presenceService.GetAgents(t1); + m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds); + } + + HashSet onlineAgentsUuidSet = new HashSet(); + Array.ForEach(onlineAgents, pi => onlineAgentsUuidSet.Add(pi.UserID)); + + groupMembers = groupMembers.Where(gmd => onlineAgentsUuidSet.Contains(gmd.AgentID.ToString())).ToList(); + + // if (m_debugEnabled) +// m_log.DebugFormat( +// "[Groups.Messaging]: SendMessageToGroup called for group {0} with {1} visible members, {2} online", +// groupID, groupMembersCount, groupMembers.Count()); + } + else + { + if (m_debugEnabled) + m_log.DebugFormat( + "[Groups.Messaging]: SendMessageToGroup called for group {0} with {1} visible members", + groupID, groupMembers.Count); + } + + int requestStartTick = Environment.TickCount; + + foreach (GroupMembersData member in groupMembers) + { + if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID.ToString(), groupID)) + { + // Don't deliver messages to people who have dropped this session + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} has dropped session, not delivering to them", member.AgentID); + continue; + } + + // Copy Message + GridInstantMessage msg = new GridInstantMessage(); + msg.imSessionID = groupID.Guid; + msg.fromAgentName = im.fromAgentName; + msg.message = im.message; + msg.dialog = im.dialog; + msg.offline = im.offline; + msg.ParentEstateID = im.ParentEstateID; + msg.Position = im.Position; + msg.RegionID = im.RegionID; + msg.binaryBucket = im.binaryBucket; + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + + msg.fromAgentID = im.fromAgentID; + msg.fromGroup = true; + + msg.toAgentID = member.AgentID.Guid; + + IClientAPI client = GetActiveClient(member.AgentID); + if (client == null) + { + // If they're not local, forward across the grid + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Delivering to {0} via Grid", member.AgentID); + m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); + } + else + { + // Deliver locally, directly + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name); + ProcessMessageFromGroupSession(msg); + } + } + + // Temporary for assessing how long it still takes to send messages to large online groups. + if (m_messageOnlineAgentsOnly) + m_log.DebugFormat( + "[Groups.Messaging]: SendMessageToGroup for group {0} with {1} visible members, {2} online took {3}ms", + groupID, groupMembersCount, groupMembers.Count(), Environment.TickCount - requestStartTick); + } + + #region SimGridEventHandlers + + void OnClientLogin(IClientAPI client) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: OnInstantMessage registered for {0}", client.Name); + } + + private void OnNewClient(IClientAPI client) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: OnInstantMessage registered for {0}", client.Name); + + client.OnInstantMessage += OnInstantMessage; + } + + private void OnGridInstantMessage(GridInstantMessage msg) + { + // The instant message module will only deliver messages of dialog types: + // MessageFromAgent, StartTyping, StopTyping, MessageFromObject + // + // Any other message type will not be delivered to a client by the + // Instant Message Module + + + if (m_debugEnabled) + { + m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + DebugGridInstantMessage(msg); + } + + // Incoming message from a group + if ((msg.fromGroup == true) && + ((msg.dialog == (byte)InstantMessageDialog.SessionSend) + || (msg.dialog == (byte)InstantMessageDialog.SessionAdd) + || (msg.dialog == (byte)InstantMessageDialog.SessionDrop))) + { + ProcessMessageFromGroupSession(msg); + } + } + + private void ProcessMessageFromGroupSession(GridInstantMessage msg) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Session message from {0} going to agent {1}", msg.fromAgentName, msg.toAgentID); + + UUID AgentID = new UUID(msg.fromAgentID); + UUID GroupID = new UUID(msg.imSessionID); + + switch (msg.dialog) + { + case (byte)InstantMessageDialog.SessionAdd: + m_groupData.AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID); + break; + + case (byte)InstantMessageDialog.SessionDrop: + m_groupData.AgentDroppedFromGroupChatSession(AgentID.ToString(), GroupID); + break; + + case (byte)InstantMessageDialog.SessionSend: + if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID.ToString(), GroupID) + && !m_groupData.hasAgentBeenInvitedToGroupChatSession(AgentID.ToString(), GroupID) + ) + { + // Agent not in session and hasn't dropped from session + // Add them to the session for now, and Invite them + m_groupData.AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID); + + UUID toAgentID = new UUID(msg.toAgentID); + IClientAPI activeClient = GetActiveClient(toAgentID); + if (activeClient != null) + { + GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null); + if (groupInfo != null) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Sending chatterbox invite instant message"); + + // Force? open the group session dialog??? + // and simultanously deliver the message, so we don't need to do a seperate client.SendInstantMessage(msg); + IEventQueue eq = activeClient.Scene.RequestModuleInterface(); + eq.ChatterboxInvitation( + GroupID + , groupInfo.GroupName + , new UUID(msg.fromAgentID) + , msg.message + , new UUID(msg.toAgentID) + , msg.fromAgentName + , msg.dialog + , msg.timestamp + , msg.offline == 1 + , (int)msg.ParentEstateID + , msg.Position + , 1 + , new UUID(msg.imSessionID) + , msg.fromGroup + , OpenMetaverse.Utils.StringToBytes(groupInfo.GroupName) + ); + + eq.ChatterBoxSessionAgentListUpdates( + new UUID(GroupID) + , new UUID(msg.fromAgentID) + , new UUID(msg.toAgentID) + , false //canVoiceChat + , false //isModerator + , false //text mute + ); + } + } + } + else if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID.ToString(), GroupID)) + { + // User hasn't dropped, so they're in the session, + // maybe we should deliver it. + IClientAPI client = GetActiveClient(new UUID(msg.toAgentID)); + if (client != null) + { + // Deliver locally, directly + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Delivering to {0} locally", client.Name); + client.SendInstantMessage(msg); + } + else + { + m_log.WarnFormat("[Groups.Messaging]: Received a message over the grid for a client that isn't here: {0}", msg.toAgentID); + } + } + break; + + default: + m_log.WarnFormat("[Groups.Messaging]: I don't know how to proccess a {0} message.", ((InstantMessageDialog)msg.dialog).ToString()); + break; + } + } + + #endregion + + + #region ClientEvents + private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) + { + if (m_debugEnabled) + { + m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + DebugGridInstantMessage(im); + } + + // Start group IM session + if ((im.dialog == (byte)InstantMessageDialog.SessionGroupStart)) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups.Messaging]: imSessionID({0}) toAgentID({1})", im.imSessionID, im.toAgentID); + + UUID GroupID = new UUID(im.imSessionID); + UUID AgentID = new UUID(im.fromAgentID); + + GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null); + + if (groupInfo != null) + { + m_groupData.AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID); + + ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.GroupName, GroupID); + + IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); + queue.ChatterBoxSessionAgentListUpdates( + GroupID + , AgentID + , new UUID(im.toAgentID) + , false //canVoiceChat + , false //isModerator + , false //text mute + ); + } + } + + // Send a message from locally connected client to a group + if ((im.dialog == (byte)InstantMessageDialog.SessionSend)) + { + UUID GroupID = new UUID(im.imSessionID); + UUID AgentID = new UUID(im.fromAgentID); + + if (m_debugEnabled) + m_log.DebugFormat("[Groups.Messaging]: Send message to session for group {0} with session ID {1}", GroupID, im.imSessionID.ToString()); + + //If this agent is sending a message, then they want to be in the session + m_groupData.AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID); + + SendMessageToGroup(im, GroupID); + } + } + + #endregion + + void ChatterBoxSessionStartReplyViaCaps(IClientAPI remoteClient, string groupName, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + OSDMap moderatedMap = new OSDMap(4); + moderatedMap.Add("voice", OSD.FromBoolean(false)); + + OSDMap sessionMap = new OSDMap(4); + sessionMap.Add("moderated_mode", moderatedMap); + sessionMap.Add("session_name", OSD.FromString(groupName)); + sessionMap.Add("type", OSD.FromInteger(0)); + sessionMap.Add("voice_enabled", OSD.FromBoolean(false)); + + OSDMap bodyMap = new OSDMap(4); + bodyMap.Add("session_id", OSD.FromUUID(groupID)); + bodyMap.Add("temp_session_id", OSD.FromUUID(groupID)); + bodyMap.Add("success", OSD.FromBoolean(true)); + bodyMap.Add("session_info", sessionMap); + + IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); + + if (queue != null) + { + queue.Enqueue(queue.BuildEvent("ChatterBoxSessionStartReply", bodyMap), remoteClient.AgentId); + } + } + + private void DebugGridInstantMessage(GridInstantMessage im) + { + // Don't log any normal IMs (privacy!) + if (m_debugEnabled && im.dialog != (byte)InstantMessageDialog.MessageFromAgent) + { + m_log.WarnFormat("[Groups.Messaging]: IM: fromGroup({0})", im.fromGroup ? "True" : "False"); + m_log.WarnFormat("[Groups.Messaging]: IM: Dialog({0})", ((InstantMessageDialog)im.dialog).ToString()); + m_log.WarnFormat("[Groups.Messaging]: IM: fromAgentID({0})", im.fromAgentID.ToString()); + m_log.WarnFormat("[Groups.Messaging]: IM: fromAgentName({0})", im.fromAgentName.ToString()); + m_log.WarnFormat("[Groups.Messaging]: IM: imSessionID({0})", im.imSessionID.ToString()); + m_log.WarnFormat("[Groups.Messaging]: IM: message({0})", im.message.ToString()); + m_log.WarnFormat("[Groups.Messaging]: IM: offline({0})", im.offline.ToString()); + m_log.WarnFormat("[Groups.Messaging]: IM: toAgentID({0})", im.toAgentID.ToString()); + m_log.WarnFormat("[Groups.Messaging]: IM: binaryBucket({0})", OpenMetaverse.Utils.BytesToHexString(im.binaryBucket, "BinaryBucket")); + } + } + + #region Client Tools + + /// + /// Try to find an active IClientAPI reference for agentID giving preference to root connections + /// + private IClientAPI GetActiveClient(UUID agentID) + { + if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Looking for local client {0}", agentID); + + IClientAPI child = null; + + // Try root avatar first + foreach (Scene scene in m_sceneList) + { + ScenePresence sp = scene.GetScenePresence(agentID); + if (sp != null) + { + if (!sp.IsChildAgent) + { + if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Found root agent for client : {0}", sp.ControllingClient.Name); + return sp.ControllingClient; + } + else + { + if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Found child agent for client : {0}", sp.ControllingClient.Name); + child = sp.ControllingClient; + } + } + } + + // If we didn't find a root, then just return whichever child we found, or null if none + if (child == null) + { + if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Could not find local client for agent : {0}", agentID); + } + else + { + if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Returning child agent for client : {0}", child.Name); + } + return child; + } + + #endregion + } +} diff --git a/OpenSim/Addons/Groups/GroupsModule.cs b/OpenSim/Addons/Groups/GroupsModule.cs new file mode 100644 index 0000000000..10bfa8f852 --- /dev/null +++ b/OpenSim/Addons/Groups/GroupsModule.cs @@ -0,0 +1,1467 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.Reflection; +using System.Timers; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; + +namespace OpenSim.Groups +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsModule")] + public class GroupsModule : ISharedRegionModule, IGroupsModule + { + /// + /// + + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private List m_sceneList = new List(); + + private IMessageTransferModule m_msgTransferModule = null; + + private IGroupsServicesConnector m_groupData = null; + private IUserManagement m_UserManagement; + + // Configuration settings + private bool m_groupsEnabled = false; + private bool m_groupNoticesEnabled = true; + private bool m_debugEnabled = false; + private int m_levelGroupCreate = 0; + + #region Region Module interfaceBase Members + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + + if (groupsConfig == null) + { + // Do not run this module by default. + return; + } + else + { + m_groupsEnabled = groupsConfig.GetBoolean("Enabled", false); + if (!m_groupsEnabled) + { + return; + } + + if (groupsConfig.GetString("Module", "Default") != Name) + { + m_groupsEnabled = false; + + return; + } + + m_log.InfoFormat("[Groups]: Initializing {0}", this.Name); + + m_groupNoticesEnabled = groupsConfig.GetBoolean("NoticesEnabled", true); + m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", false); + m_levelGroupCreate = groupsConfig.GetInt("LevelGroupCreate", 0); + } + } + + public void AddRegion(Scene scene) + { + if (m_groupsEnabled) + { + scene.RegisterModuleInterface(this); + scene.AddCommand( + "debug", + this, + "debug groups verbose", + "debug groups verbose ", + "This setting turns on very verbose groups debugging", + HandleDebugGroupsVerbose); + } + } + + private void HandleDebugGroupsVerbose(object modules, string[] args) + { + if (args.Length < 4) + { + MainConsole.Instance.Output("Usage: debug groups verbose "); + return; + } + + bool verbose = false; + if (!bool.TryParse(args[3], out verbose)) + { + MainConsole.Instance.Output("Usage: debug groups verbose "); + return; + } + + m_debugEnabled = verbose; + + MainConsole.Instance.OutputFormat("{0} verbose logging set to {1}", Name, m_debugEnabled); + } + + public void RegionLoaded(Scene scene) + { + if (!m_groupsEnabled) + return; + + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + scene.EventManager.OnNewClient += OnNewClient; + scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; + // The InstantMessageModule itself doesn't do this, + // so lets see if things explode if we don't do it + // scene.EventManager.OnClientClosed += OnClientClosed; + + if (m_groupData == null) + { + m_groupData = scene.RequestModuleInterface(); + + // No Groups Service Connector, then nothing works... + if (m_groupData == null) + { + m_groupsEnabled = false; + m_log.Error("[Groups]: Could not get IGroupsServicesConnector"); + RemoveRegion(scene); + return; + } + } + + if (m_msgTransferModule == null) + { + m_msgTransferModule = scene.RequestModuleInterface(); + + // No message transfer module, no notices, group invites, rejects, ejects, etc + if (m_msgTransferModule == null) + { + m_log.Warn("[Groups]: Could not get MessageTransferModule"); + } + } + + if (m_UserManagement == null) + { + m_UserManagement = scene.RequestModuleInterface(); + if (m_UserManagement == null) + m_log.Warn("[Groups]: Could not get UserManagementModule"); + } + + lock (m_sceneList) + { + m_sceneList.Add(scene); + } + + + } + + public void RemoveRegion(Scene scene) + { + if (!m_groupsEnabled) + return; + + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + scene.EventManager.OnNewClient -= OnNewClient; + scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; + + lock (m_sceneList) + { + m_sceneList.Remove(scene); + } + } + + public void Close() + { + if (!m_groupsEnabled) + return; + + if (m_debugEnabled) m_log.Debug("[Groups]: Shutting down Groups module."); + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public string Name + { + get { return "Groups Module V2"; } + } + + public void PostInitialise() + { + // NoOp + } + + #endregion + + #region EventHandlers + private void OnNewClient(IClientAPI client) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + client.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest; + client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest; + client.OnDirFindQuery += OnDirFindQuery; + client.OnRequestAvatarProperties += OnRequestAvatarProperties; + + // Used for Notices and Group Invites/Accept/Reject + client.OnInstantMessage += OnInstantMessage; + + // Send client their groups information. + SendAgentGroupDataUpdate(client, client.AgentId); + } + + private void OnRequestAvatarProperties(IClientAPI remoteClient, UUID avatarID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + //GroupMembershipData[] avatarGroups = m_groupData.GetAgentGroupMemberships(GetRequestingAgentID(remoteClient), avatarID).ToArray(); + GroupMembershipData[] avatarGroups = GetProfileListedGroupMemberships(remoteClient, avatarID); + remoteClient.SendAvatarGroupsReply(avatarID, avatarGroups); + } + + /* + * This becomes very problematic in a shared module. In a shared module you may have more then one + * reference to IClientAPI's, one for 0 or 1 root connections, and 0 or more child connections. + * The OnClientClosed event does not provide anything to indicate which one of those should be closed + * nor does it provide what scene it was from so that the specific reference can be looked up. + * The InstantMessageModule.cs does not currently worry about unregistering the handles, + * and it should be an issue, since it's the client that references us not the other way around + * , so as long as we don't keep a reference to the client laying around, the client can still be GC'ed + private void OnClientClosed(UUID AgentId) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + lock (m_ActiveClients) + { + if (m_ActiveClients.ContainsKey(AgentId)) + { + IClientAPI client = m_ActiveClients[AgentId]; + client.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest; + client.OnAgentDataUpdateRequest -= OnAgentDataUpdateRequest; + client.OnDirFindQuery -= OnDirFindQuery; + client.OnInstantMessage -= OnInstantMessage; + + m_ActiveClients.Remove(AgentId); + } + else + { + if (m_debugEnabled) m_log.WarnFormat("[Groups]: Client closed that wasn't registered here."); + } + + + } + } + */ + + void OnDirFindQuery(IClientAPI remoteClient, UUID queryID, string queryText, uint queryFlags, int queryStart) + { + if (((DirFindFlags)queryFlags & DirFindFlags.Groups) == DirFindFlags.Groups) + { + if (m_debugEnabled) + m_log.DebugFormat( + "[Groups]: {0} called with queryText({1}) queryFlags({2}) queryStart({3})", + System.Reflection.MethodBase.GetCurrentMethod().Name, queryText, (DirFindFlags)queryFlags, queryStart); + + // TODO: This currently ignores pretty much all the query flags including Mature and sort order + remoteClient.SendDirGroupsReply(queryID, m_groupData.FindGroups(GetRequestingAgentIDStr(remoteClient), queryText).ToArray()); + } + + } + + private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + UUID activeGroupID = UUID.Zero; + string activeGroupTitle = string.Empty; + string activeGroupName = string.Empty; + ulong activeGroupPowers = (ulong)GroupPowers.None; + + GroupMembershipData membership = m_groupData.GetAgentActiveMembership(GetRequestingAgentIDStr(remoteClient), dataForAgentID.ToString()); + if (membership != null) + { + activeGroupID = membership.GroupID; + activeGroupTitle = membership.GroupTitle; + activeGroupPowers = membership.GroupPowers; + } + + SendAgentDataUpdate(remoteClient, dataForAgentID, activeGroupID, activeGroupName, activeGroupPowers, activeGroupTitle); + + SendScenePresenceUpdate(dataForAgentID, activeGroupTitle); + } + + private void HandleUUIDGroupNameRequest(UUID GroupID, IClientAPI remoteClient) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + string GroupName; + + GroupRecord group = m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), GroupID, null); + if (group != null) + { + GroupName = group.GroupName; + } + else + { + GroupName = "Unknown"; + } + + remoteClient.SendGroupNameReply(GroupID, GroupName); + } + + private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_log.DebugFormat("[Groups]: IM From {0} to {1} msg {2} type {3}", im.fromAgentID, im.toAgentID, im.message, (InstantMessageDialog)im.dialog); + // Group invitations + if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)) + { + UUID inviteID = new UUID(im.imSessionID); + GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID); + + if (inviteInfo == null) + { + if (m_debugEnabled) m_log.WarnFormat("[Groups]: Received an Invite IM for an invite that does not exist {0}.", inviteID); + return; + } + + //m_log.DebugFormat("[XXX]: Invite is for Agent {0} to Group {1}.", inviteInfo.AgentID, inviteInfo.GroupID); + + UUID fromAgentID = new UUID(im.fromAgentID); + UUID invitee = UUID.Zero; + string tmp = string.Empty; + Util.ParseUniversalUserIdentifier(inviteInfo.AgentID, out invitee, out tmp, out tmp, out tmp, out tmp); + if ((inviteInfo != null) && (fromAgentID == invitee)) + { + // Accept + if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) + { + //m_log.DebugFormat("[XXX]: Received an accept invite notice."); + + // and the sessionid is the role + string reason = string.Empty; + if (!m_groupData.AddAgentToGroup(GetRequestingAgentIDStr(remoteClient), invitee.ToString(), inviteInfo.GroupID, inviteInfo.RoleID, string.Empty, out reason)) + remoteClient.SendAgentAlertMessage("Unable to add you to the group: " + reason, false); + else + { + GridInstantMessage msg = new GridInstantMessage(); + msg.imSessionID = UUID.Zero.Guid; + msg.fromAgentID = UUID.Zero.Guid; + msg.toAgentID = invitee.Guid; + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.fromAgentName = "Groups"; + msg.message = string.Format("You have been added to the group."); + msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageBox; + msg.fromGroup = false; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = UUID.Zero.Guid; + msg.binaryBucket = new byte[0]; + + OutgoingInstantMessage(msg, invitee); + + UpdateAllClientsWithGroupInfo(invitee); + } + + m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID); + + } + + // Reject + if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: Received a reject invite notice."); + m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID); + + m_groupData.RemoveAgentFromGroup(GetRequestingAgentIDStr(remoteClient), inviteInfo.AgentID, inviteInfo.GroupID); + } + } + } + + // Group notices + if ((im.dialog == (byte)InstantMessageDialog.GroupNotice)) + { + if (!m_groupNoticesEnabled) + { + return; + } + + UUID GroupID = new UUID(im.toAgentID); + if (m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), GroupID, null) != null) + { + UUID NoticeID = UUID.Random(); + string Subject = im.message.Substring(0, im.message.IndexOf('|')); + string Message = im.message.Substring(Subject.Length + 1); + + InventoryItemBase item = null; + bool hasAttachment = false; + + if (im.binaryBucket.Length >= 1 && im.binaryBucket[0] > 0) + { + hasAttachment = true; + string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket); + binBucket = binBucket.Remove(0, 14).Trim(); + + OSD binBucketOSD = OSDParser.DeserializeLLSDXml(binBucket); + if (binBucketOSD is OSDMap) + { + OSDMap binBucketMap = (OSDMap)binBucketOSD; + + UUID itemID = binBucketMap["item_id"].AsUUID(); + UUID ownerID = binBucketMap["owner_id"].AsUUID(); + item = new InventoryItemBase(itemID, ownerID); + item = m_sceneList[0].InventoryService.GetItem(item); + } + else + m_log.DebugFormat("[Groups]: Received OSD with unexpected type: {0}", binBucketOSD.GetType()); + } + + if (m_groupData.AddGroupNotice(GetRequestingAgentIDStr(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, + hasAttachment, + (byte)(item == null ? 0 : item.AssetType), + item == null ? null : item.Name, + item == null ? UUID.Zero : item.ID, + item == null ? UUID.Zero.ToString() : item.Owner.ToString())) + { + if (OnNewGroupNotice != null) + { + OnNewGroupNotice(GroupID, NoticeID); + } + + // Send notice out to everyone that wants notices + // Build notice IIM + GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice); + foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetRequestingAgentIDStr(remoteClient), GroupID)) + { + if (member.AcceptNotices) + { + msg.toAgentID = member.AgentID.Guid; + OutgoingInstantMessage(msg, member.AgentID); + } + } + } + } + } + + if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryAccepted) + { + if (im.binaryBucket.Length < 16) // Invalid + return; + + //// 16 bytes are the UUID. Maybe. + UUID folderID = new UUID(im.binaryBucket, 0); + UUID noticeID = new UUID(im.imSessionID); + + GroupNoticeInfo notice = m_groupData.GetGroupNotice(remoteClient.AgentId.ToString(), noticeID); + if (notice != null) + { + UUID giver = new UUID(im.toAgentID); + string tmp = string.Empty; + Util.ParseUniversalUserIdentifier(notice.noticeData.AttachmentOwnerID, out giver, out tmp, out tmp, out tmp, out tmp); + + m_log.DebugFormat("[Groups]: Giving inventory from {0} to {1}", giver, remoteClient.AgentId); + InventoryItemBase itemCopy = ((Scene)(remoteClient.Scene)).GiveInventoryItem(remoteClient.AgentId, + giver, notice.noticeData.AttachmentItemID); + + if (itemCopy == null) + { + remoteClient.SendAgentAlertMessage("Can't find item to give. Nothing given.", false); + return; + } + + remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0); + } + + } + + // Interop, received special 210 code for ejecting a group member + // this only works within the comms servers domain, and won't work hypergrid + // TODO:FIXME: Use a presense server of some kind to find out where the + // client actually is, and try contacting that region directly to notify them, + // or provide the notification via xmlrpc update queue + if ((im.dialog == 210)) + { + // This is sent from the region that the ejectee was ejected from + // if it's being delivered here, then the ejectee is here + // so we need to send local updates to the agent. + + UUID ejecteeID = new UUID(im.toAgentID); + + im.dialog = (byte)InstantMessageDialog.MessageFromAgent; + OutgoingInstantMessage(im, ejecteeID); + + IClientAPI ejectee = GetActiveClient(ejecteeID); + if (ejectee != null) + { + UUID groupID = new UUID(im.imSessionID); + ejectee.SendAgentDropGroup(groupID); + } + } + } + + private void OnGridInstantMessage(GridInstantMessage msg) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Trigger the above event handler + OnInstantMessage(null, msg); + + // If a message from a group arrives here, it may need to be forwarded to a local client + if (msg.fromGroup == true) + { + switch (msg.dialog) + { + case (byte)InstantMessageDialog.GroupInvitation: + case (byte)InstantMessageDialog.GroupNotice: + UUID toAgentID = new UUID(msg.toAgentID); + IClientAPI localClient = GetActiveClient(toAgentID); + if (localClient != null) + { + localClient.SendInstantMessage(msg); + } + break; + } + } + } + + #endregion + + #region IGroupsModule Members + + public event NewGroupNotice OnNewGroupNotice; + + public GroupRecord GetGroupRecord(UUID GroupID) + { + return m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null); + } + + public GroupRecord GetGroupRecord(string name) + { + return m_groupData.GetGroupRecord(UUID.Zero.ToString(), UUID.Zero, name); + } + + public void ActivateGroup(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.SetAgentActiveGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + + // Changing active group changes title, active powers, all kinds of things + // anyone who is in any region that can see this client, should probably be + // updated with new group info. At a minimum, they should get ScenePresence + // updated with new title. + UpdateAllClientsWithGroupInfo(remoteClient.AgentId); + } + + /// + /// Get the Role Titles for an Agent, for a specific group + /// + public List GroupTitlesRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List agentRoles = m_groupData.GetAgentGroupRoles(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + + List titles = new List(); + foreach (GroupRolesData role in agentRoles) + { + GroupTitlesData title = new GroupTitlesData(); + title.Name = role.Name; + if (agentMembership != null) + { + title.Selected = agentMembership.ActiveRole == role.RoleID; + } + title.UUID = role.RoleID; + + titles.Add(title); + } + + return titles; + } + + public List GroupMembersRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) + m_log.DebugFormat( + "[Groups]: GroupMembersRequest called for {0} from client {1}", groupID, remoteClient.Name); + + List data = m_groupData.GetGroupMembers(GetRequestingAgentIDStr(remoteClient), groupID); + + if (m_debugEnabled) + { + foreach (GroupMembersData member in data) + { + m_log.DebugFormat("[Groups]: Member({0}) - IsOwner({1})", member.AgentID, member.IsOwner); + } + } + + return data; + + } + + public List GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List data = m_groupData.GetGroupRoles(GetRequestingAgentIDStr(remoteClient), groupID); + + return data; + } + + public List GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List data = m_groupData.GetGroupRoleMembers(GetRequestingAgentIDStr(remoteClient), groupID); + + if (m_debugEnabled) + { + foreach (GroupRoleMembersData member in data) + { + m_log.DebugFormat("[Groups]: Member({0}) - Role({1})", member.MemberID, member.RoleID); + } + } + return data; + } + + public GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupProfileData profile = new GroupProfileData(); + + // just to get the OwnerRole... + ExtendedGroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), groupID, string.Empty); + GroupMembershipData memberInfo = m_groupData.GetAgentGroupMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + if (groupInfo != null) + { + profile.AllowPublish = groupInfo.AllowPublish; + profile.Charter = groupInfo.Charter; + profile.FounderID = groupInfo.FounderID; + profile.GroupID = groupID; + profile.GroupMembershipCount = groupInfo.MemberCount; + profile.GroupRolesCount = groupInfo.RoleCount; + profile.InsigniaID = groupInfo.GroupPicture; + profile.MaturePublish = groupInfo.MaturePublish; + profile.MembershipFee = groupInfo.MembershipFee; + profile.Money = 0; + profile.Name = groupInfo.GroupName; + profile.OpenEnrollment = groupInfo.OpenEnrollment; + profile.OwnerRole = groupInfo.OwnerRoleID; + profile.ShowInList = groupInfo.ShowInList; + } + if (memberInfo != null) + { + profile.MemberTitle = memberInfo.GroupTitle; + profile.PowersMask = memberInfo.GroupPowers; + } + + return profile; + } + + public GroupMembershipData[] GetMembershipData(UUID agentID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + return m_groupData.GetAgentGroupMemberships(UUID.Zero.ToString(), agentID.ToString()).ToArray(); + } + + public GroupMembershipData GetMembershipData(UUID groupID, UUID agentID) + { + if (m_debugEnabled) + m_log.DebugFormat( + "[Groups]: {0} called with groupID={1}, agentID={2}", + System.Reflection.MethodBase.GetCurrentMethod().Name, groupID, agentID); + + return m_groupData.GetAgentGroupMembership(UUID.Zero.ToString(), agentID.ToString(), groupID); + } + + public void UpdateGroupInfo(IClientAPI remoteClient, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Note: Permissions checking for modification rights is handled by the Groups Server/Service + string reason = string.Empty; + if (!m_groupData.UpdateGroup(GetRequestingAgentIDStr(remoteClient), groupID, charter, showInList, insigniaID, membershipFee, + openEnrollment, allowPublish, maturePublish, out reason)) + remoteClient.SendAgentAlertMessage(reason, false); + } + + public void SetGroupAcceptNotices(IClientAPI remoteClient, UUID groupID, bool acceptNotices, bool listInProfile) + { + // Note: Permissions checking for modification rights is handled by the Groups Server/Service + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.UpdateMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, acceptNotices, listInProfile); + } + + public UUID CreateGroup(IClientAPI remoteClient, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called in {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Scene.RegionInfo.RegionName); + + if (m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), UUID.Zero, name) != null) + { + remoteClient.SendCreateGroupReply(UUID.Zero, false, "A group with the same name already exists."); + return UUID.Zero; + } + + // check user level + ScenePresence avatar = null; + Scene scene = (Scene)remoteClient.Scene; + scene.TryGetScenePresence(remoteClient.AgentId, out avatar); + + if (avatar != null) + { + if (avatar.UserLevel < m_levelGroupCreate) + { + remoteClient.SendCreateGroupReply(UUID.Zero, false, String.Format("Insufficient permissions to create a group. Requires level {0}", m_levelGroupCreate)); + return UUID.Zero; + } + } + + // check funds + // is there is a money module present ? + IMoneyModule money = scene.RequestModuleInterface(); + if (money != null) + { + // do the transaction, that is if the agent has got sufficient funds + if (!money.AmountCovered(remoteClient.AgentId, money.GroupCreationCharge)) { + remoteClient.SendCreateGroupReply(UUID.Zero, false, "Insufficient funds to create a group."); + return UUID.Zero; + } + money.ApplyCharge(remoteClient.AgentId, money.GroupCreationCharge, "Group Creation"); + } + string reason = string.Empty; + UUID groupID = m_groupData.CreateGroup(remoteClient.AgentId, name, charter, showInList, insigniaID, membershipFee, openEnrollment, + allowPublish, maturePublish, remoteClient.AgentId, out reason); + + if (groupID != UUID.Zero) + { + remoteClient.SendCreateGroupReply(groupID, true, "Group created successfullly"); + + // Update the founder with new group information. + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + else + remoteClient.SendCreateGroupReply(groupID, false, reason); + + return groupID; + } + + public GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // ToDo: check if agent is a member of group and is allowed to see notices? + + List notices = m_groupData.GetGroupNotices(GetRequestingAgentIDStr(remoteClient), groupID); + List os_notices = new List(); + foreach (ExtendedGroupNoticeData n in notices) + { + GroupNoticeData osn = n.ToGroupNoticeData(); + os_notices.Add(osn); + } + + return os_notices.ToArray(); + } + + /// + /// Get the title of the agent's current role. + /// + public string GetGroupTitle(UUID avatarID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupMembershipData membership = m_groupData.GetAgentActiveMembership(UUID.Zero.ToString(), avatarID.ToString()); + if (membership != null) + { + return membership.GroupTitle; + } + return string.Empty; + } + + /// + /// Change the current Active Group Role for Agent + /// + public void GroupTitleUpdate(IClientAPI remoteClient, UUID groupID, UUID titleRoleID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.SetAgentActiveGroupRole(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, titleRoleID); + + // TODO: Not sure what all is needed here, but if the active group role change is for the group + // the client currently has set active, then we need to do a scene presence update too + // if (m_groupData.GetAgentActiveMembership(GetRequestingAgentID(remoteClient)).GroupID == GroupID) + + UpdateAllClientsWithGroupInfo(GetRequestingAgentID(remoteClient)); + } + + + public void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, byte updateType) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Security Checks are handled in the Groups Service. + + switch ((OpenMetaverse.GroupRoleUpdate)updateType) + { + case OpenMetaverse.GroupRoleUpdate.Create: + string reason = string.Empty; + if (!m_groupData.AddGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, UUID.Random(), name, description, title, powers, out reason)) + remoteClient.SendAgentAlertMessage("Unable to create role: " + reason, false); + break; + + case OpenMetaverse.GroupRoleUpdate.Delete: + m_groupData.RemoveGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, roleID); + break; + + case OpenMetaverse.GroupRoleUpdate.UpdateAll: + case OpenMetaverse.GroupRoleUpdate.UpdateData: + case OpenMetaverse.GroupRoleUpdate.UpdatePowers: + if (m_debugEnabled) + { + GroupPowers gp = (GroupPowers)powers; + m_log.DebugFormat("[Groups]: Role ({0}) updated with Powers ({1}) ({2})", name, powers.ToString(), gp.ToString()); + } + m_groupData.UpdateGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, roleID, name, description, title, powers); + break; + + case OpenMetaverse.GroupRoleUpdate.NoUpdate: + default: + // No Op + break; + + } + + // TODO: This update really should send out updates for everyone in the role that just got changed. + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + + public void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + // Todo: Security check + + switch (changes) + { + case 0: + // Add + m_groupData.AddAgentToGroupRole(GetRequestingAgentIDStr(remoteClient), memberID.ToString(), groupID, roleID); + + break; + case 1: + // Remove + m_groupData.RemoveAgentFromGroupRole(GetRequestingAgentIDStr(remoteClient), memberID.ToString(), groupID, roleID); + + break; + default: + m_log.ErrorFormat("[Groups]: {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes); + break; + } + + // TODO: This update really should send out updates for everyone in the role that just got changed. + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + + public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called for notice {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, groupNoticeID); + + //GroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), data.GroupID, null); + + GridInstantMessage msg = CreateGroupNoticeIM(remoteClient.AgentId, groupNoticeID, (byte)InstantMessageDialog.GroupNoticeRequested); + //GridInstantMessage msg = new GridInstantMessage(); + //msg.imSessionID = UUID.Zero.Guid; + //msg.fromAgentID = data.GroupID.Guid; + //msg.toAgentID = GetRequestingAgentID(remoteClient).Guid; + //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + //msg.fromAgentName = "Group Notice : " + groupInfo == null ? "Unknown" : groupInfo.GroupName; + //msg.message = data.noticeData.Subject + "|" + data.Message; + //msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNoticeRequested; + //msg.fromGroup = true; + //msg.offline = (byte)0; + //msg.ParentEstateID = 0; + //msg.Position = Vector3.Zero; + //msg.RegionID = UUID.Zero.Guid; + //msg.binaryBucket = data.BinaryBucket; + + OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient)); + } + + public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GridInstantMessage msg = new GridInstantMessage(); + byte[] bucket; + + msg.imSessionID = groupNoticeID.Guid; + msg.toAgentID = agentID.Guid; + msg.dialog = dialog; + // msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNotice; + msg.fromGroup = true; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = UUID.Zero.Guid; + + GroupNoticeInfo info = m_groupData.GetGroupNotice(agentID.ToString(), groupNoticeID); + if (info != null) + { + msg.fromAgentID = info.GroupID.Guid; + msg.timestamp = info.noticeData.Timestamp; + msg.fromAgentName = info.noticeData.FromName; + msg.message = info.noticeData.Subject + "|" + info.Message; + if (info.noticeData.HasAttachment) + { + byte[] name = System.Text.Encoding.UTF8.GetBytes(info.noticeData.AttachmentName); + bucket = new byte[19 + name.Length]; + bucket[0] = 1; // has attachment? + bucket[1] = info.noticeData.AttachmentType; // attachment type + name.CopyTo(bucket, 18); + } + else + { + bucket = new byte[19]; + bucket[0] = 0; // Has att? + bucket[1] = 0; // type + bucket[18] = 0; // null terminated + } + + info.GroupID.ToBytes(bucket, 2); + msg.binaryBucket = bucket; + } + else + { + m_log.DebugFormat("[Groups]: Group Notice {0} not found, composing empty message.", groupNoticeID); + msg.fromAgentID = UUID.Zero.Guid; + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); ; + msg.fromAgentName = string.Empty; + msg.message = string.Empty; + msg.binaryBucket = new byte[0]; + } + + return msg; + } + + public void SendAgentGroupDataUpdate(IClientAPI remoteClient) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Send agent information about his groups + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + + public void JoinGroupRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + string reason = string.Empty; + // Should check to see if OpenEnrollment, or if there's an outstanding invitation + if (m_groupData.AddAgentToGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, UUID.Zero, string.Empty, out reason)) + { + + remoteClient.SendJoinGroupReply(groupID, true); + + // Should this send updates to everyone in the group? + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + else + remoteClient.SendJoinGroupReply(groupID, false); + } + + public void LeaveGroupRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.RemoveAgentFromGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + + remoteClient.SendLeaveGroupReply(groupID, true); + + remoteClient.SendAgentDropGroup(groupID); + + // SL sends out notifcations to the group messaging session that the person has left + // Should this also update everyone who is in the group? + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + + public void EjectGroupMemberRequest(IClientAPI remoteClient, UUID groupID, UUID ejecteeID) + { + EjectGroupMember(remoteClient, GetRequestingAgentID(remoteClient), groupID, ejecteeID); + } + + public void EjectGroupMember(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID ejecteeID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Todo: Security check? + m_groupData.RemoveAgentFromGroup(agentID.ToString(), ejecteeID.ToString(), groupID); + + string agentName; + RegionInfo regionInfo; + + // remoteClient provided or just agentID? + if (remoteClient != null) + { + agentName = remoteClient.Name; + regionInfo = remoteClient.Scene.RegionInfo; + remoteClient.SendEjectGroupMemberReply(agentID, groupID, true); + } + else + { + IClientAPI client = GetActiveClient(agentID); + + if (client != null) + { + agentName = client.Name; + regionInfo = client.Scene.RegionInfo; + client.SendEjectGroupMemberReply(agentID, groupID, true); + } + else + { + regionInfo = m_sceneList[0].RegionInfo; + UserAccount acc = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, agentID); + + if (acc != null) + { + agentName = acc.FirstName + " " + acc.LastName; + } + else + { + agentName = "Unknown member"; + } + } + } + + GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null); + + UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, ejecteeID); + if ((groupInfo == null) || (account == null)) + { + return; + } + + // Send Message to Ejectee + GridInstantMessage msg = new GridInstantMessage(); + + msg.imSessionID = UUID.Zero.Guid; + msg.fromAgentID = agentID.Guid; + // msg.fromAgentID = info.GroupID; + msg.toAgentID = ejecteeID.Guid; + //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.timestamp = 0; + msg.fromAgentName = agentName; + msg.message = string.Format("You have been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName); + msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageFromAgent; + msg.fromGroup = false; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = regionInfo.RegionID.Guid; + msg.binaryBucket = new byte[0]; + OutgoingInstantMessage(msg, ejecteeID); + + // Message to ejector + // Interop, received special 210 code for ejecting a group member + // this only works within the comms servers domain, and won't work hypergrid + // TODO:FIXME: Use a presense server of some kind to find out where the + // client actually is, and try contacting that region directly to notify them, + // or provide the notification via xmlrpc update queue + + msg = new GridInstantMessage(); + msg.imSessionID = UUID.Zero.Guid; + msg.fromAgentID = agentID.Guid; + msg.toAgentID = agentID.Guid; + msg.timestamp = 0; + msg.fromAgentName = agentName; + if (account != null) + { + msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName, account.FirstName + " " + account.LastName); + } + else + { + msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName, "Unknown member"); + } + msg.dialog = (byte)210; //interop + msg.fromGroup = false; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = regionInfo.RegionID.Guid; + msg.binaryBucket = new byte[0]; + OutgoingInstantMessage(msg, agentID); + + + // SL sends out messages to everyone in the group + // Who all should receive updates and what should they be updated with? + UpdateAllClientsWithGroupInfo(ejecteeID); + } + + public void InviteGroupRequest(IClientAPI remoteClient, UUID groupID, UUID invitedAgentID, UUID roleID) + { + InviteGroup(remoteClient, GetRequestingAgentID(remoteClient), groupID, invitedAgentID, roleID); + } + + public void InviteGroup(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID invitedAgentID, UUID roleID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + string agentName = m_UserManagement.GetUserName(agentID); + RegionInfo regionInfo = m_sceneList[0].RegionInfo; + + GroupRecord group = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null); + if (group == null) + { + m_log.DebugFormat("[Groups]: No such group {0}", groupID); + return; + } + + // Todo: Security check, probably also want to send some kind of notification + UUID InviteID = UUID.Random(); + + if (m_groupData.AddAgentToGroupInvite(agentID.ToString(), InviteID, groupID, roleID, invitedAgentID.ToString())) + { + if (m_msgTransferModule != null) + { + Guid inviteUUID = InviteID.Guid; + + GridInstantMessage msg = new GridInstantMessage(); + + msg.imSessionID = inviteUUID; + + // msg.fromAgentID = agentID.Guid; + msg.fromAgentID = groupID.Guid; + msg.toAgentID = invitedAgentID.Guid; + //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.timestamp = 0; + msg.fromAgentName = agentName; + msg.message = string.Format("{0} has invited you to join a group called {1}. There is no cost to join this group.", agentName, group.GroupName); + msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation; + msg.fromGroup = true; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = regionInfo.RegionID.Guid; + msg.binaryBucket = new byte[20]; + + OutgoingInstantMessage(msg, invitedAgentID); + } + } + } + + #endregion + + #region Client/Update Tools + + /// + /// Try to find an active IClientAPI reference for agentID giving preference to root connections + /// + private IClientAPI GetActiveClient(UUID agentID) + { + IClientAPI child = null; + + // Try root avatar first + foreach (Scene scene in m_sceneList) + { + ScenePresence sp = scene.GetScenePresence(agentID); + if (sp != null) + { + if (!sp.IsChildAgent) + { + return sp.ControllingClient; + } + else + { + child = sp.ControllingClient; + } + } + } + + // If we didn't find a root, then just return whichever child we found, or null if none + return child; + } + + /// + /// Send 'remoteClient' the group membership 'data' for agent 'dataForAgentID'. + /// + private void SendGroupMembershipInfoViaCaps(IClientAPI remoteClient, UUID dataForAgentID, GroupMembershipData[] data) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + OSDArray AgentData = new OSDArray(1); + OSDMap AgentDataMap = new OSDMap(1); + AgentDataMap.Add("AgentID", OSD.FromUUID(dataForAgentID)); + AgentData.Add(AgentDataMap); + + + OSDArray GroupData = new OSDArray(data.Length); + OSDArray NewGroupData = new OSDArray(data.Length); + + foreach (GroupMembershipData membership in data) + { + if (GetRequestingAgentID(remoteClient) != dataForAgentID) + { + if (!membership.ListInProfile) + { + // If we're sending group info to remoteclient about another agent, + // filter out groups the other agent doesn't want to share. + continue; + } + } + + OSDMap GroupDataMap = new OSDMap(6); + OSDMap NewGroupDataMap = new OSDMap(1); + + GroupDataMap.Add("GroupID", OSD.FromUUID(membership.GroupID)); + GroupDataMap.Add("GroupPowers", OSD.FromULong(membership.GroupPowers)); + GroupDataMap.Add("AcceptNotices", OSD.FromBoolean(membership.AcceptNotices)); + GroupDataMap.Add("GroupInsigniaID", OSD.FromUUID(membership.GroupPicture)); + GroupDataMap.Add("Contribution", OSD.FromInteger(membership.Contribution)); + GroupDataMap.Add("GroupName", OSD.FromString(membership.GroupName)); + NewGroupDataMap.Add("ListInProfile", OSD.FromBoolean(membership.ListInProfile)); + + GroupData.Add(GroupDataMap); + NewGroupData.Add(NewGroupDataMap); + } + + OSDMap llDataStruct = new OSDMap(3); + llDataStruct.Add("AgentData", AgentData); + llDataStruct.Add("GroupData", GroupData); + llDataStruct.Add("NewGroupData", NewGroupData); + + if (m_debugEnabled) + { + m_log.InfoFormat("[Groups]: {0}", OSDParser.SerializeJsonString(llDataStruct)); + } + + IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); + + if (queue != null) + { + queue.Enqueue(queue.BuildEvent("AgentGroupDataUpdate", llDataStruct), GetRequestingAgentID(remoteClient)); + } + + } + + private void SendScenePresenceUpdate(UUID AgentID, string Title) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: Updating scene title for {0} with title: {1}", AgentID, Title); + + ScenePresence presence = null; + + foreach (Scene scene in m_sceneList) + { + presence = scene.GetScenePresence(AgentID); + if (presence != null) + { + if (presence.Grouptitle != Title) + { + presence.Grouptitle = Title; + + if (! presence.IsChildAgent) + presence.SendAvatarDataToAllAgents(); + } + } + } + } + + /// + /// Send updates to all clients who might be interested in groups data for dataForClientID + /// + private void UpdateAllClientsWithGroupInfo(UUID dataForClientID) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // TODO: Probably isn't nessesary to update every client in every scene. + // Need to examine client updates and do only what's nessesary. + lock (m_sceneList) + { + foreach (Scene scene in m_sceneList) + { + scene.ForEachClient(delegate(IClientAPI client) { SendAgentGroupDataUpdate(client, dataForClientID); }); + } + } + } + + /// + /// Update remoteClient with group information about dataForAgentID + /// + private void SendAgentGroupDataUpdate(IClientAPI remoteClient, UUID dataForAgentID) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called for {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Name); + + // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff + + OnAgentDataUpdateRequest(remoteClient, dataForAgentID, UUID.Zero); + + // Need to send a group membership update to the client + // UDP version doesn't seem to behave nicely. But we're going to send it out here + // with an empty group membership to hopefully remove groups being displayed due + // to the core Groups Stub + //remoteClient.SendGroupMembership(new GroupMembershipData[0]); + + GroupMembershipData[] membershipArray = GetProfileListedGroupMemberships(remoteClient, dataForAgentID); + SendGroupMembershipInfoViaCaps(remoteClient, dataForAgentID, membershipArray); + //remoteClient.SendAvatarGroupsReply(dataForAgentID, membershipArray); + if (remoteClient.AgentId == dataForAgentID) + remoteClient.RefreshGroupMembership(); + } + + /// + /// Get a list of groups memberships for the agent that are marked "ListInProfile" + /// (unless that agent has a godLike aspect, in which case get all groups) + /// + /// + /// + private GroupMembershipData[] GetProfileListedGroupMemberships(IClientAPI requestingClient, UUID dataForAgentID) + { + List membershipData = m_groupData.GetAgentGroupMemberships(requestingClient.AgentId.ToString(), dataForAgentID.ToString()); + GroupMembershipData[] membershipArray; + + // cScene and property accessor 'isGod' are in support of the opertions to bypass 'hidden' group attributes for + // those with a GodLike aspect. + Scene cScene = (Scene)requestingClient.Scene; + bool isGod = cScene.Permissions.IsGod(requestingClient.AgentId); + + if (isGod) + { + membershipArray = membershipData.ToArray(); + } + else + { + if (requestingClient.AgentId != dataForAgentID) + { + Predicate showInProfile = delegate(GroupMembershipData membership) + { + return membership.ListInProfile; + }; + + membershipArray = membershipData.FindAll(showInProfile).ToArray(); + } + else + { + membershipArray = membershipData.ToArray(); + } + } + + if (m_debugEnabled) + { + m_log.InfoFormat("[Groups]: Get group membership information for {0} requested by {1}", dataForAgentID, requestingClient.AgentId); + foreach (GroupMembershipData membership in membershipArray) + { + m_log.InfoFormat("[Groups]: {0} :: {1} - {2} - {3}", dataForAgentID, membership.GroupName, membership.GroupTitle, membership.GroupPowers); + } + } + + return membershipArray; + } + + + private void SendAgentDataUpdate(IClientAPI remoteClient, UUID dataForAgentID, UUID activeGroupID, string activeGroupName, ulong activeGroupPowers, string activeGroupTitle) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff + UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(remoteClient.Scene.RegionInfo.ScopeID, dataForAgentID); + string firstname, lastname; + if (account != null) + { + firstname = account.FirstName; + lastname = account.LastName; + } + else + { + firstname = "Unknown"; + lastname = "Unknown"; + } + + remoteClient.SendAgentDataUpdate(dataForAgentID, activeGroupID, firstname, + lastname, activeGroupPowers, activeGroupName, + activeGroupTitle); + } + + #endregion + + #region IM Backed Processes + + private void OutgoingInstantMessage(GridInstantMessage msg, UUID msgTo) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + IClientAPI localClient = GetActiveClient(msgTo); + if (localClient != null) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: MsgTo ({0}) is local, delivering directly", localClient.Name); + localClient.SendInstantMessage(msg); + } + else if (m_msgTransferModule != null) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: MsgTo ({0}) is not local, delivering via TransferModule", msgTo); + m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { if (m_debugEnabled) m_log.DebugFormat("[Groups]: Message Sent: {0}", success?"Succeeded":"Failed"); }); + } + } + + public void NotifyChange(UUID groupID) + { + // Notify all group members of a chnge in group roles and/or + // permissions + // + } + + #endregion + + private string GetRequestingAgentIDStr(IClientAPI client) + { + return GetRequestingAgentID(client).ToString(); + } + + private UUID GetRequestingAgentID(IClientAPI client) + { + UUID requestingAgentID = UUID.Zero; + if (client != null) + { + requestingAgentID = client.AgentId; + } + return requestingAgentID; + } + + } + +} diff --git a/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs new file mode 100644 index 0000000000..59fec6fa38 --- /dev/null +++ b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs @@ -0,0 +1,289 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.Linq; +using System.Reflection; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Server.Base; + +using OpenMetaverse; +using log4net; + +namespace OpenSim.Groups +{ + public class GroupsServiceHGConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI; + private object m_Lock = new object(); + + public GroupsServiceHGConnector(string url) + { + m_ServerURI = url; + if (!m_ServerURI.EndsWith("/")) + m_ServerURI += "/"; + + m_log.DebugFormat("[Groups.HGConnector]: Groups server at {0}", m_ServerURI); + } + + public bool CreateProxy(string RequestingAgentID, string AgentID, string accessToken, UUID groupID, string url, string name, out string reason) + { + reason = string.Empty; + + Dictionary sendData = new Dictionary(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AgentID"] = AgentID.ToString(); + sendData["AccessToken"] = accessToken; + sendData["GroupID"] = groupID.ToString(); + sendData["Location"] = url; + sendData["Name"] = name; + Dictionary ret = MakeRequest("POSTGROUP", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + { + reason = ret["REASON"].ToString(); + return false; + } + + return true; + + } + + public void RemoveAgentFromGroup(string AgentID, UUID GroupID, string token) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID; + sendData["GroupID"] = GroupID.ToString(); + sendData["AccessToken"] = GroupsDataUtils.Sanitize(token); + MakeRequest("REMOVEAGENTFROMGROUP", sendData); + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName, string token) + { + if (GroupID == UUID.Zero && (GroupName == null || (GroupName != null && GroupName == string.Empty))) + return null; + + Dictionary sendData = new Dictionary(); + if (GroupID != UUID.Zero) + sendData["GroupID"] = GroupID.ToString(); + if (GroupName != null && GroupName != string.Empty) + sendData["Name"] = GroupsDataUtils.Sanitize(GroupName); + + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AccessToken"] = GroupsDataUtils.Sanitize(token); + + Dictionary ret = MakeRequest("GETGROUP", sendData); + + if (ret == null) + return null; + + if (!ret.ContainsKey("RESULT")) + return null; + + if (ret["RESULT"].ToString() == "NULL") + return null; + + return GroupsDataUtils.GroupRecord((Dictionary)ret["RESULT"]); + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID, string token) + { + List members = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AccessToken"] = GroupsDataUtils.Sanitize(token); + Dictionary ret = MakeRequest("GETGROUPMEMBERS", sendData); + + if (ret == null) + return members; + + if (!ret.ContainsKey("RESULT")) + return members; + + if (ret["RESULT"].ToString() == "NULL") + return members; + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + ExtendedGroupMembersData m = GroupsDataUtils.GroupMembersData((Dictionary)v); + members.Add(m); + } + + return members; + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID, string token) + { + List roles = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AccessToken"] = GroupsDataUtils.Sanitize(token); + Dictionary ret = MakeRequest("GETGROUPROLES", sendData); + + if (ret == null) + return roles; + + if (!ret.ContainsKey("RESULT")) + return roles; + + if (ret["RESULT"].ToString() == "NULL") + return roles; + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + GroupRolesData m = GroupsDataUtils.GroupRolesData((Dictionary)v); + roles.Add(m); + } + + return roles; + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, string token) + { + List rmembers = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AccessToken"] = GroupsDataUtils.Sanitize(token); + Dictionary ret = MakeRequest("GETROLEMEMBERS", sendData); + + if (ret == null) + return rmembers; + + if (!ret.ContainsKey("RESULT")) + return rmembers; + + if (ret["RESULT"].ToString() == "NULL") + return rmembers; + + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + ExtendedGroupRoleMembersData m = GroupsDataUtils.GroupRoleMembersData((Dictionary)v); + rmembers.Add(m); + } + + return rmembers; + } + + public bool AddNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = groupID.ToString(); + sendData["NoticeID"] = noticeID.ToString(); + sendData["FromName"] = GroupsDataUtils.Sanitize(fromName); + sendData["Subject"] = GroupsDataUtils.Sanitize(subject); + sendData["Message"] = GroupsDataUtils.Sanitize(message); + sendData["HasAttachment"] = hasAttachment.ToString(); + if (hasAttachment) + { + sendData["AttachmentType"] = attType.ToString(); + sendData["AttachmentName"] = attName.ToString(); + sendData["AttachmentItemID"] = attItemID.ToString(); + sendData["AttachmentOwnerID"] = attOwnerID; + } + sendData["RequestingAgentID"] = RequestingAgentID; + + Dictionary ret = MakeRequest("ADDNOTICE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + return false; + + return true; + } + + public bool VerifyNotice(UUID noticeID, UUID groupID) + { + Dictionary sendData = new Dictionary(); + sendData["NoticeID"] = noticeID.ToString(); + sendData["GroupID"] = groupID.ToString(); + Dictionary ret = MakeRequest("VERIFYNOTICE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + return false; + + return true; + } + + // + // + // + // + // + + #region Make Request + + private Dictionary MakeRequest(string method, Dictionary sendData) + { + sendData["METHOD"] = method; + + string reply = string.Empty; + lock (m_Lock) + reply = SynchronousRestFormsRequester.MakeRequest("POST", + m_ServerURI + "hg-groups", + ServerUtils.BuildQueryString(sendData)); + + //m_log.DebugFormat("[XXX]: reply was {0}", reply); + + if (reply == string.Empty || reply == null) + return null; + + Dictionary replyData = ServerUtils.ParseXmlResponse( + reply); + + return replyData; + } + #endregion + + } +} diff --git a/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs new file mode 100644 index 0000000000..f670272662 --- /dev/null +++ b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs @@ -0,0 +1,717 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.Linq; +using System.Reflection; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Framework.Servers; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Services.Interfaces; + +using OpenMetaverse; +using Mono.Addins; +using log4net; +using Nini.Config; + +namespace OpenSim.Groups +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsServiceHGConnectorModule")] + public class GroupsServiceHGConnectorModule : ISharedRegionModule, IGroupsServicesConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private bool m_Enabled = false; + private IGroupsServicesConnector m_LocalGroupsConnector; + private string m_LocalGroupsServiceLocation; + private IUserManagement m_UserManagement; + private IOfflineIMService m_OfflineIM; + private IMessageTransferModule m_Messaging; + private List m_Scenes; + private ForeignImporter m_ForeignImporter; + private string m_ServiceLocation; + private IConfigSource m_Config; + + private Dictionary m_NetworkConnectors = new Dictionary(); + private RemoteConnectorCacheWrapper m_CacheWrapper; // for caching info of external group services + + #region ISharedRegionModule + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + if (groupsConfig == null) + return; + + if ((groupsConfig.GetBoolean("Enabled", false) == false) + || (groupsConfig.GetString("ServicesConnectorModule", string.Empty) != Name)) + { + return; + } + + m_Config = config; + m_ServiceLocation = groupsConfig.GetString("LocalService", "local"); // local or remote + m_LocalGroupsServiceLocation = groupsConfig.GetString("GroupsExternalURI", "http://127.0.0.1"); + m_Scenes = new List(); + + m_Enabled = true; + + m_log.DebugFormat("[Groups]: Initializing {0} with LocalService {1}", this.Name, m_ServiceLocation); + } + + public string Name + { + get { return "Groups HG Service Connector"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public void AddRegion(Scene scene) + { + if (!m_Enabled) + return; + + m_log.DebugFormat("[Groups]: Registering {0} with {1}", this.Name, scene.RegionInfo.RegionName); + scene.RegisterModuleInterface(this); + m_Scenes.Add(scene); + + scene.EventManager.OnNewClient += OnNewClient; + } + + public void RemoveRegion(Scene scene) + { + if (!m_Enabled) + return; + + scene.UnregisterModuleInterface(this); + m_Scenes.Remove(scene); + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + if (m_UserManagement == null) + { + m_UserManagement = scene.RequestModuleInterface(); + m_OfflineIM = scene.RequestModuleInterface(); + m_Messaging = scene.RequestModuleInterface(); + m_ForeignImporter = new ForeignImporter(m_UserManagement); + + if (m_ServiceLocation.Equals("local")) + { + m_LocalGroupsConnector = new GroupsServiceLocalConnectorModule(m_Config, m_UserManagement); + // Also, if local, create the endpoint for the HGGroupsService + new HGGroupsServiceRobustConnector(m_Config, MainServer.Instance, string.Empty, + scene.RequestModuleInterface(), scene.RequestModuleInterface()); + + } + else + m_LocalGroupsConnector = new GroupsServiceRemoteConnectorModule(m_Config, m_UserManagement); + + m_CacheWrapper = new RemoteConnectorCacheWrapper(m_UserManagement); + } + + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + #endregion + + private void OnNewClient(IClientAPI client) + { + client.OnCompleteMovementToRegion += OnCompleteMovementToRegion; + } + + void OnCompleteMovementToRegion(IClientAPI client, bool arg2) + { + object sp = null; + if (client.Scene.TryGetScenePresence(client.AgentId, out sp)) + { + if (sp is ScenePresence && ((ScenePresence)sp).PresenceType != PresenceType.Npc) + { + AgentCircuitData aCircuit = ((ScenePresence)sp).Scene.AuthenticateHandler.GetAgentCircuitData(client.AgentId); + if (aCircuit != null && (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0 && + m_OfflineIM != null && m_Messaging != null) + { + List ims = m_OfflineIM.GetMessages(aCircuit.AgentID); + if (ims != null && ims.Count > 0) + foreach (GridInstantMessage im in ims) + m_Messaging.SendInstantMessage(im, delegate(bool success) { }); + } + } + } + } + + #region IGroupsServicesConnector + + public UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, + bool allowPublish, bool maturePublish, UUID founderID, out string reason) + { + m_log.DebugFormat("[Groups]: Creating group {0}", name); + reason = string.Empty; + if (m_UserManagement.IsLocalGridUser(RequestingAgentID)) + return m_LocalGroupsConnector.CreateGroup(RequestingAgentID, name, charter, showInList, insigniaID, + membershipFee, openEnrollment, allowPublish, maturePublish, founderID, out reason); + else + { + reason = "Only local grid users are allowed to create a new group"; + return UUID.Zero; + } + } + + public bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, + bool openEnrollment, bool allowPublish, bool maturePublish, out string reason) + { + reason = string.Empty; + string url = string.Empty; + string name = string.Empty; + if (IsLocal(groupID, out url, out name)) + return m_LocalGroupsConnector.UpdateGroup(AgentUUI(RequestingAgentID), groupID, charter, showInList, insigniaID, membershipFee, + openEnrollment, allowPublish, maturePublish, out reason); + else + { + reason = "Changes to remote group not allowed. Please go to the group's original world."; + return false; + } + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName) + { + string url = string.Empty; + string name = string.Empty; + if (IsLocal(GroupID, out url, out name)) + return m_LocalGroupsConnector.GetGroupRecord(AgentUUI(RequestingAgentID), GroupID, GroupName); + else if (url != string.Empty) + { + ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, GroupID); + string accessToken = string.Empty; + if (membership != null) + accessToken = membership.AccessToken; + else + return null; + + GroupsServiceHGConnector c = GetConnector(url); + if (c != null) + { + ExtendedGroupRecord grec = m_CacheWrapper.GetGroupRecord(RequestingAgentID, GroupID, GroupName, delegate + { + return c.GetGroupRecord(AgentUUIForOutside(RequestingAgentID), GroupID, GroupName, accessToken); + }); + + if (grec != null) + ImportForeigner(grec.FounderUUI); + return grec; + } + } + + return null; + } + + public List FindGroups(string RequestingAgentID, string search) + { + return m_LocalGroupsConnector.FindGroups(AgentUUI(RequestingAgentID), search); + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID) + { + string url = string.Empty, gname = string.Empty; + if (IsLocal(GroupID, out url, out gname)) + return m_LocalGroupsConnector.GetGroupMembers(AgentUUI(RequestingAgentID), GroupID); + else if (!string.IsNullOrEmpty(url)) + { + ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, GroupID); + string accessToken = string.Empty; + if (membership != null) + accessToken = membership.AccessToken; + else + return null; + + GroupsServiceHGConnector c = GetConnector(url); + if (c != null) + { + return m_CacheWrapper.GetGroupMembers(RequestingAgentID, GroupID, delegate + { + return c.GetGroupMembers(AgentUUIForOutside(RequestingAgentID), GroupID, accessToken); + }); + + } + } + return new List(); + } + + public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason) + { + reason = string.Empty; + string url = string.Empty, gname = string.Empty; + + if (IsLocal(groupID, out url, out gname)) + return m_LocalGroupsConnector.AddGroupRole(AgentUUI(RequestingAgentID), groupID, roleID, name, description, title, powers, out reason); + else + { + reason = "Operation not allowed outside this group's origin world."; + return false; + } + } + + public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(groupID, out url, out gname)) + return m_LocalGroupsConnector.UpdateGroupRole(AgentUUI(RequestingAgentID), groupID, roleID, name, description, title, powers); + else + { + return false; + } + + } + + public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(groupID, out url, out gname)) + m_LocalGroupsConnector.RemoveGroupRole(AgentUUI(RequestingAgentID), groupID, roleID); + else + { + return; + } + } + + public List GetGroupRoles(string RequestingAgentID, UUID groupID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(groupID, out url, out gname)) + return m_LocalGroupsConnector.GetGroupRoles(AgentUUI(RequestingAgentID), groupID); + else if (!string.IsNullOrEmpty(url)) + { + ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, groupID); + string accessToken = string.Empty; + if (membership != null) + accessToken = membership.AccessToken; + else + return null; + + GroupsServiceHGConnector c = GetConnector(url); + if (c != null) + { + return m_CacheWrapper.GetGroupRoles(RequestingAgentID, groupID, delegate + { + return c.GetGroupRoles(AgentUUIForOutside(RequestingAgentID), groupID, accessToken); + }); + + } + } + + return new List(); + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID groupID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(groupID, out url, out gname)) + return m_LocalGroupsConnector.GetGroupRoleMembers(AgentUUI(RequestingAgentID), groupID); + else if (!string.IsNullOrEmpty(url)) + { + ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, groupID); + string accessToken = string.Empty; + if (membership != null) + accessToken = membership.AccessToken; + else + return null; + + GroupsServiceHGConnector c = GetConnector(url); + if (c != null) + { + return m_CacheWrapper.GetGroupRoleMembers(RequestingAgentID, groupID, delegate + { + return c.GetGroupRoleMembers(AgentUUIForOutside(RequestingAgentID), groupID, accessToken); + }); + + } + } + + return new List(); + } + + public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason) + { + string url = string.Empty; + string name = string.Empty; + reason = string.Empty; + + UUID uid = new UUID(AgentID); + if (IsLocal(GroupID, out url, out name)) + { + if (m_UserManagement.IsLocalGridUser(uid)) // local user + { + // normal case: local group, local user + return m_LocalGroupsConnector.AddAgentToGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID, token, out reason); + } + else // local group, foreign user + { + // the user is accepting the invitation, or joining, where the group resides + token = UUID.Random().ToString(); + bool success = m_LocalGroupsConnector.AddAgentToGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID, token, out reason); + + if (success) + { + url = m_UserManagement.GetUserServerURL(uid, "GroupsServerURI"); + if (url == string.Empty) + { + reason = "User doesn't have a groups server"; + return false; + } + + GroupsServiceHGConnector c = GetConnector(url); + if (c != null) + return c.CreateProxy(AgentUUI(RequestingAgentID), AgentID, token, GroupID, m_LocalGroupsServiceLocation, name, out reason); + } + } + } + else if (m_UserManagement.IsLocalGridUser(uid)) // local user + { + // foreign group, local user. She's been added already by the HG service. + // Let's just check + if (m_LocalGroupsConnector.GetAgentGroupMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID) != null) + return true; + } + + reason = "Operation not allowed outside this group's origin world"; + return false; + } + + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + string url = string.Empty, name = string.Empty; + if (!IsLocal(GroupID, out url, out name) && url != string.Empty) + { + ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID); + if (membership != null) + { + GroupsServiceHGConnector c = GetConnector(url); + if (c != null) + c.RemoveAgentFromGroup(AgentUUIForOutside(AgentID), GroupID, membership.AccessToken); + } + } + + // remove from local service + m_LocalGroupsConnector.RemoveAgentFromGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID); + } + + public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(groupID, out url, out gname)) + return m_LocalGroupsConnector.AddAgentToGroupInvite(AgentUUI(RequestingAgentID), inviteID, groupID, roleID, AgentUUI(agentID)); + else + return false; + } + + public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + return m_LocalGroupsConnector.GetAgentToGroupInvite(AgentUUI(RequestingAgentID), inviteID); ; + } + + public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + m_LocalGroupsConnector.RemoveAgentToGroupInvite(AgentUUI(RequestingAgentID), inviteID); + } + + public void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(GroupID, out url, out gname)) + m_LocalGroupsConnector.AddAgentToGroupRole(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID); + + } + + public void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(GroupID, out url, out gname)) + m_LocalGroupsConnector.RemoveAgentFromGroupRole(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID); + } + + public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(GroupID, out url, out gname)) + return m_LocalGroupsConnector.GetAgentGroupRoles(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID); + else + return new List(); + } + + public void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(GroupID, out url, out gname)) + m_LocalGroupsConnector.SetAgentActiveGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID); + } + + public ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID) + { + return m_LocalGroupsConnector.GetAgentActiveMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID)); + } + + public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(GroupID, out url, out gname)) + m_LocalGroupsConnector.SetAgentActiveGroupRole(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID); + } + + public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile) + { + m_LocalGroupsConnector.UpdateMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, AcceptNotices, ListInProfile); + } + + public ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(GroupID, out url, out gname)) + return m_LocalGroupsConnector.GetAgentGroupMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID); + else + return null; + } + + public List GetAgentGroupMemberships(string RequestingAgentID, string AgentID) + { + return m_LocalGroupsConnector.GetAgentGroupMemberships(AgentUUI(RequestingAgentID), AgentUUI(AgentID)); + } + + public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(groupID, out url, out gname)) + { + if (m_LocalGroupsConnector.AddGroupNotice(AgentUUI(RequestingAgentID), groupID, noticeID, fromName, subject, message, + hasAttachment, attType, attName, attItemID, AgentUUI(attOwnerID))) + { + // then send the notice to every grid for which there are members in this group + List members = m_LocalGroupsConnector.GetGroupMembers(AgentUUI(RequestingAgentID), groupID); + List urls = new List(); + foreach (GroupMembersData m in members) + { + UUID userID = UUID.Zero; + if (!m_UserManagement.IsLocalGridUser(m.AgentID)) + { + string gURL = m_UserManagement.GetUserServerURL(m.AgentID, "GroupsServerURI"); + if (!urls.Contains(gURL)) + urls.Add(gURL); + } + } + + // so we have the list of urls to send the notice to + // this may take a long time... + Util.FireAndForget(delegate + { + foreach (string u in urls) + { + GroupsServiceHGConnector c = GetConnector(u); + if (c != null) + { + c.AddNotice(AgentUUIForOutside(RequestingAgentID), groupID, noticeID, fromName, subject, message, + hasAttachment, attType, attName, attItemID, AgentUUIForOutside(attOwnerID)); + } + } + }); + + return true; + } + + return false; + } + else + return false; + } + + public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID) + { + GroupNoticeInfo notice = m_LocalGroupsConnector.GetGroupNotice(AgentUUI(RequestingAgentID), noticeID); + + if (notice != null && notice.noticeData.HasAttachment && notice.noticeData.AttachmentOwnerID != null) + ImportForeigner(notice.noticeData.AttachmentOwnerID); + + return notice; + } + + public List GetGroupNotices(string RequestingAgentID, UUID GroupID) + { + return m_LocalGroupsConnector.GetGroupNotices(AgentUUI(RequestingAgentID), GroupID); + } + + public void ResetAgentGroupChatSessions(string agentID) + { + } + + public bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public void AgentDroppedFromGroupChatSession(string agentID, UUID groupID) + { + } + + public void AgentInvitedToGroupChatSession(string agentID, UUID groupID) + { + } + + #endregion + + #region hypergrid groups + + private string AgentUUI(string AgentIDStr) + { + UUID AgentID = UUID.Zero; + try + { + AgentID = new UUID(AgentIDStr); + } + catch (FormatException) + { + return AgentID.ToString(); + } + + if (m_UserManagement.IsLocalGridUser(AgentID)) + return AgentID.ToString(); + + AgentCircuitData agent = null; + foreach (Scene scene in m_Scenes) + { + agent = scene.AuthenticateHandler.GetAgentCircuitData(AgentID); + if (agent != null) + break; + } + if (agent == null) // oops + return AgentID.ToString(); + + return Util.ProduceUserUniversalIdentifier(agent); + } + + private string AgentUUIForOutside(string AgentIDStr) + { + UUID AgentID = UUID.Zero; + try + { + AgentID = new UUID(AgentIDStr); + } + catch (FormatException) + { + return AgentID.ToString(); + } + + AgentCircuitData agent = null; + foreach (Scene scene in m_Scenes) + { + agent = scene.AuthenticateHandler.GetAgentCircuitData(AgentID); + if (agent != null) + break; + } + if (agent == null) // oops + return AgentID.ToString(); + + return Util.ProduceUserUniversalIdentifier(agent); + } + + private UUID ImportForeigner(string uID) + { + UUID userID = UUID.Zero; + string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty; + if (Util.ParseUniversalUserIdentifier(uID, out userID, out url, out first, out last, out tmp)) + m_UserManagement.AddUser(userID, first, last, url); + + return userID; + } + + private bool IsLocal(UUID groupID, out string serviceLocation, out string name) + { + serviceLocation = string.Empty; + name = string.Empty; + ExtendedGroupRecord group = m_LocalGroupsConnector.GetGroupRecord(UUID.Zero.ToString(), groupID, string.Empty); + if (group == null) + { + //m_log.DebugFormat("[XXX]: IsLocal? group {0} not found -- no.", groupID); + return false; + } + + serviceLocation = group.ServiceLocation; + name = group.GroupName; + bool isLocal = (group.ServiceLocation == string.Empty); + //m_log.DebugFormat("[XXX]: IsLocal? {0}", isLocal); + return isLocal; + } + + private GroupsServiceHGConnector GetConnector(string url) + { + lock (m_NetworkConnectors) + { + if (m_NetworkConnectors.ContainsKey(url)) + return m_NetworkConnectors[url]; + + GroupsServiceHGConnector c = new GroupsServiceHGConnector(url); + m_NetworkConnectors[url] = c; + } + + return m_NetworkConnectors[url]; + } + #endregion + } +} diff --git a/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs b/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs new file mode 100644 index 0000000000..0e71c72ad6 --- /dev/null +++ b/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs @@ -0,0 +1,443 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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.Reflection; +using System.Text; +using System.Xml; +using System.Collections.Generic; +using System.IO; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using log4net; +using OpenMetaverse; + +namespace OpenSim.Groups +{ + public class HGGroupsServiceRobustConnector : ServiceConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private HGGroupsService m_GroupsService; + private string m_HomeURI = string.Empty; + private string m_ConfigName = "Groups"; + + // Called by Robust shell + public HGGroupsServiceRobustConnector(IConfigSource config, IHttpServer server, string configName) : + this(config, server, configName, null, null) + { + } + + // Called by the sim-bound module + public HGGroupsServiceRobustConnector(IConfigSource config, IHttpServer server, string configName, IOfflineIMService im, IUserAccountService users) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + m_log.DebugFormat("[Groups.RobustHGConnector]: Starting with config name {0}", m_ConfigName); + + string homeURI = Util.GetConfigVarWithDefaultSection(config, "HomeURI", m_ConfigName); //cnf.GetString("HomeURI", string.Empty); + if (homeURI == string.Empty) + throw new Exception(String.Format("[Groups.RobustHGConnector]: please provide the HomeURI [Startup] or in section {0}", m_ConfigName)); + + IConfig cnf = config.Configs[m_ConfigName]; + if (cnf == null) + throw new Exception(String.Format("[Groups.RobustHGConnector]: {0} section does not exist", m_ConfigName)); + + if (im == null) + { + string imDll = cnf.GetString("OfflineIMService", string.Empty); + if (imDll == string.Empty) + throw new Exception(String.Format("[Groups.RobustHGConnector]: please provide OfflineIMService in section {0}", m_ConfigName)); + + Object[] args = new Object[] { config }; + im = ServerUtils.LoadPlugin(imDll, args); + } + + if (users == null) + { + string usersDll = cnf.GetString("UserAccountService", string.Empty); + if (usersDll == string.Empty) + throw new Exception(String.Format("[Groups.RobustHGConnector]: please provide UserAccountService in section {0}", m_ConfigName)); + + Object[] args = new Object[] { config }; + users = ServerUtils.LoadPlugin(usersDll, args); + } + + m_GroupsService = new HGGroupsService(config, im, users, homeURI); + + server.AddStreamHandler(new HGGroupsServicePostHandler(m_GroupsService)); + } + + } + + public class HGGroupsServicePostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private HGGroupsService m_GroupsService; + + public HGGroupsServicePostHandler(HGGroupsService service) : + base("POST", "/hg-groups") + { + m_GroupsService = service; + } + + public override byte[] Handle(string path, Stream requestData, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + StreamReader sr = new StreamReader(requestData); + string body = sr.ReadToEnd(); + sr.Close(); + body = body.Trim(); + + //m_log.DebugFormat("[XXX]: query String: {0}", body); + + try + { + Dictionary request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + string method = request["METHOD"].ToString(); + request.Remove("METHOD"); + + m_log.DebugFormat("[Groups.RobustHGConnector]: {0}", method); + switch (method) + { + case "POSTGROUP": + return HandleAddGroupProxy(request); + case "REMOVEAGENTFROMGROUP": + return HandleRemoveAgentFromGroup(request); + case "GETGROUP": + return HandleGetGroup(request); + case "ADDNOTICE": + return HandleAddNotice(request); + case "VERIFYNOTICE": + return HandleVerifyNotice(request); + case "GETGROUPMEMBERS": + return HandleGetGroupMembers(request); + case "GETGROUPROLES": + return HandleGetGroupRoles(request); + case "GETROLEMEMBERS": + return HandleGetRoleMembers(request); + + } + m_log.DebugFormat("[Groups.RobustHGConnector]: unknown method request: {0}", method); + } + catch (Exception e) + { + m_log.DebugFormat("[Groups.RobustHGConnector]: Exception {0}", e.StackTrace); + } + + return FailureResult(); + } + + byte[] HandleAddGroupProxy(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") + || !request.ContainsKey("AgentID") + || !request.ContainsKey("AccessToken") || !request.ContainsKey("Location")) + NullResult(result, "Bad network data"); + + else + { + string RequestingAgentID = request["RequestingAgentID"].ToString(); + string agentID = request["AgentID"].ToString(); + UUID groupID = new UUID(request["GroupID"].ToString()); + string accessToken = request["AccessToken"].ToString(); + string location = request["Location"].ToString(); + string name = string.Empty; + if (request.ContainsKey("Name")) + name = request["Name"].ToString(); + + string reason = string.Empty; + bool success = m_GroupsService.CreateGroupProxy(RequestingAgentID, agentID, accessToken, groupID, location, name, out reason); + result["REASON"] = reason; + result["RESULT"] = success.ToString(); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleRemoveAgentFromGroup(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("AccessToken") || !request.ContainsKey("AgentID") || + !request.ContainsKey("GroupID")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string agentID = request["AgentID"].ToString(); + string token = request["AccessToken"].ToString(); + string reason = string.Empty; + + m_GroupsService.RemoveAgentFromGroup(agentID, agentID, groupID, token); + } + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + result["RESULT"] = "true"; + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + byte[] HandleGetGroup(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AccessToken")) + NullResult(result, "Bad network data"); + else + { + string RequestingAgentID = request["RequestingAgentID"].ToString(); + string token = request["AccessToken"].ToString(); + + UUID groupID = UUID.Zero; + string groupName = string.Empty; + + if (request.ContainsKey("GroupID")) + groupID = new UUID(request["GroupID"].ToString()); + if (request.ContainsKey("Name")) + groupName = request["Name"].ToString(); + + ExtendedGroupRecord grec = m_GroupsService.GetGroupRecord(RequestingAgentID, groupID, groupName, token); + if (grec == null) + NullResult(result, "Group not found"); + else + result["RESULT"] = GroupsDataUtils.GroupRecord(grec); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetGroupMembers(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AccessToken")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string requestingAgentID = request["RequestingAgentID"].ToString(); + string token = request["AccessToken"].ToString(); + + List members = m_GroupsService.GetGroupMembers(requestingAgentID, groupID, token); + if (members == null || (members != null && members.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (ExtendedGroupMembersData m in members) + { + dict["m-" + i++] = GroupsDataUtils.GroupMembersData(m); + } + + result["RESULT"] = dict; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetGroupRoles(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AccessToken")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string requestingAgentID = request["RequestingAgentID"].ToString(); + string token = request["AccessToken"].ToString(); + + List roles = m_GroupsService.GetGroupRoles(requestingAgentID, groupID, token); + if (roles == null || (roles != null && roles.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (GroupRolesData r in roles) + dict["r-" + i++] = GroupsDataUtils.GroupRolesData(r); + + result["RESULT"] = dict; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetRoleMembers(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AccessToken")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string requestingAgentID = request["RequestingAgentID"].ToString(); + string token = request["AccessToken"].ToString(); + + List rmembers = m_GroupsService.GetGroupRoleMembers(requestingAgentID, groupID, token); + if (rmembers == null || (rmembers != null && rmembers.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (ExtendedGroupRoleMembersData rm in rmembers) + dict["rm-" + i++] = GroupsDataUtils.GroupRoleMembersData(rm); + + result["RESULT"] = dict; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleAddNotice(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("NoticeID") || + !request.ContainsKey("FromName") || !request.ContainsKey("Subject") || !request.ContainsKey("Message") || + !request.ContainsKey("HasAttachment")) + NullResult(result, "Bad network data"); + + else + { + + bool hasAtt = bool.Parse(request["HasAttachment"].ToString()); + byte attType = 0; + string attName = string.Empty; + string attOwner = string.Empty; + UUID attItem = UUID.Zero; + if (request.ContainsKey("AttachmentType")) + attType = byte.Parse(request["AttachmentType"].ToString()); + if (request.ContainsKey("AttachmentName")) + attName = request["AttachmentType"].ToString(); + if (request.ContainsKey("AttachmentItemID")) + attItem = new UUID(request["AttachmentItemID"].ToString()); + if (request.ContainsKey("AttachmentOwnerID")) + attOwner = request["AttachmentOwnerID"].ToString(); + + bool success = m_GroupsService.AddNotice(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()), + new UUID(request["NoticeID"].ToString()), request["FromName"].ToString(), request["Subject"].ToString(), + request["Message"].ToString(), hasAtt, attType, attName, attItem, attOwner); + + result["RESULT"] = success.ToString(); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleVerifyNotice(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("NoticeID") || !request.ContainsKey("GroupID")) + NullResult(result, "Bad network data"); + + else + { + UUID noticeID = new UUID(request["NoticeID"].ToString()); + UUID groupID = new UUID(request["GroupID"].ToString()); + + bool success = m_GroupsService.VerifyNotice(noticeID, groupID); + //m_log.DebugFormat("[XXX]: VerifyNotice returned {0}", success); + result["RESULT"] = success.ToString(); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + // + // + // + // + // + + #region Helpers + + private void NullResult(Dictionary result, string reason) + { + result["RESULT"] = "NULL"; + result["REASON"] = reason; + } + + private byte[] FailureResult() + { + Dictionary result = new Dictionary(); + NullResult(result, "Unknown method"); + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + #endregion + } +} diff --git a/OpenSim/Addons/Groups/IGroupsServicesConnector.cs b/OpenSim/Addons/Groups/IGroupsServicesConnector.cs new file mode 100644 index 0000000000..73deb7a7b4 --- /dev/null +++ b/OpenSim/Addons/Groups/IGroupsServicesConnector.cs @@ -0,0 +1,118 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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; +using OpenSim.Framework; + +namespace OpenSim.Groups +{ + public interface IGroupsServicesConnector + { + UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, + bool openEnrollment, bool allowPublish, bool maturePublish, UUID founderID, out string reason); + bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, + bool openEnrollment, bool allowPublish, bool maturePublish, out string reason); + ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName); + List FindGroups(string RequestingAgentID, string search); + List GetGroupMembers(string RequestingAgentID, UUID GroupID); + + bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason); + bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers); + void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID); + List GetGroupRoles(string RequestingAgentID, UUID GroupID); + List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID); + + bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason); + void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID); + + bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID); + GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID); + void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID); + + void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID); + void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID); + List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID); + + void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID); + ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID); + + void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID); + void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile); + + /// + /// Get information about a specific group to which the user belongs. + /// + /// The agent requesting the information. + /// The agent requested. + /// The group requested. + /// + /// If the user is a member of the group then the data structure is returned. If not, then null is returned. + /// + ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID); + + /// + /// Get information about the groups to which a user belongs. + /// + /// The agent requesting the information. + /// The agent requested. + /// + /// Information about the groups to which the user belongs. If the user belongs to no groups then an empty + /// list is returned. + /// + List GetAgentGroupMemberships(string RequestingAgentID, string AgentID); + + bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID); + GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID); + List GetGroupNotices(string RequestingAgentID, UUID GroupID); + + void ResetAgentGroupChatSessions(string agentID); + bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID); + bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID); + void AgentDroppedFromGroupChatSession(string agentID, UUID groupID); + void AgentInvitedToGroupChatSession(string agentID, UUID groupID); + + } + + public class GroupInviteInfo + { + public UUID GroupID = UUID.Zero; + public UUID RoleID = UUID.Zero; + public string AgentID = string.Empty; + public UUID InviteID = UUID.Zero; + } + + public class GroupNoticeInfo + { + public ExtendedGroupNoticeData noticeData = new ExtendedGroupNoticeData(); + public UUID GroupID = UUID.Zero; + public string Message = string.Empty; + } + +} diff --git a/OpenSim/Addons/Groups/Local/GroupsServiceLocalConnectorModule.cs b/OpenSim/Addons/Groups/Local/GroupsServiceLocalConnectorModule.cs new file mode 100644 index 0000000000..905bc913ff --- /dev/null +++ b/OpenSim/Addons/Groups/Local/GroupsServiceLocalConnectorModule.cs @@ -0,0 +1,347 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.Linq; +using System.Reflection; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Interfaces; + +using OpenMetaverse; +using Mono.Addins; +using log4net; +using Nini.Config; + +namespace OpenSim.Groups +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsServiceLocalConnectorModule")] + public class GroupsServiceLocalConnectorModule : ISharedRegionModule, IGroupsServicesConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private bool m_Enabled = false; + private GroupsService m_GroupsService; + private IUserManagement m_UserManagement; + private List m_Scenes; + private ForeignImporter m_ForeignImporter; + + #region constructors + public GroupsServiceLocalConnectorModule() + { + } + + public GroupsServiceLocalConnectorModule(IConfigSource config, IUserManagement uman) + { + Init(config); + m_UserManagement = uman; + m_ForeignImporter = new ForeignImporter(uman); + } + #endregion + + private void Init(IConfigSource config) + { + m_GroupsService = new GroupsService(config); + m_Scenes = new List(); + } + + #region ISharedRegionModule + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + if (groupsConfig == null) + return; + + if ((groupsConfig.GetBoolean("Enabled", false) == false) + || (groupsConfig.GetString("ServicesConnectorModule", string.Empty) != Name)) + { + return; + } + + Init(config); + m_Enabled = true; + + m_log.DebugFormat("[Groups]: Initializing {0}", this.Name); + } + + public string Name + { + get { return "Groups Local Service Connector"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public void AddRegion(Scene scene) + { + if (!m_Enabled) + return; + + m_log.DebugFormat("[Groups]: Registering {0} with {1}", this.Name, scene.RegionInfo.RegionName); + scene.RegisterModuleInterface(this); + m_Scenes.Add(scene); + } + + public void RemoveRegion(Scene scene) + { + if (!m_Enabled) + return; + + scene.UnregisterModuleInterface(this); + m_Scenes.Remove(scene); + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + if (m_UserManagement == null) + { + m_UserManagement = scene.RequestModuleInterface(); + m_ForeignImporter = new ForeignImporter(m_UserManagement); + } + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + #endregion + + #region IGroupsServicesConnector + + public UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, + bool allowPublish, bool maturePublish, UUID founderID, out string reason) + { + m_log.DebugFormat("[Groups]: Creating group {0}", name); + reason = string.Empty; + return m_GroupsService.CreateGroup(RequestingAgentID.ToString(), name, charter, showInList, insigniaID, + membershipFee, openEnrollment, allowPublish, maturePublish, founderID, out reason); + } + + public bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, + bool openEnrollment, bool allowPublish, bool maturePublish, out string reason) + { + reason = string.Empty; + m_GroupsService.UpdateGroup(RequestingAgentID, groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish); + return true; + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName) + { + if (GroupID != UUID.Zero) + return m_GroupsService.GetGroupRecord(RequestingAgentID, GroupID); + else if (GroupName != null) + return m_GroupsService.GetGroupRecord(RequestingAgentID, GroupName); + + return null; + } + + public List FindGroups(string RequestingAgentID, string search) + { + return m_GroupsService.FindGroups(RequestingAgentID, search); + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID) + { + List _members = m_GroupsService.GetGroupMembers(RequestingAgentID, GroupID); + if (_members != null && _members.Count > 0) + { + List members = _members.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupMembersData)); + return members; + } + + return new List(); + } + + public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason) + { + return m_GroupsService.AddGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers, out reason); + } + + public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers) + { + return m_GroupsService.UpdateGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers); + } + + public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID) + { + m_GroupsService.RemoveGroupRole(RequestingAgentID, groupID, roleID); + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID) + { + return m_GroupsService.GetGroupRoles(RequestingAgentID, GroupID); + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID) + { + List _rm = m_GroupsService.GetGroupRoleMembers(RequestingAgentID, GroupID); + if (_rm != null && _rm.Count > 0) + { + List rm = _rm.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupRoleMembersData)); + return rm; + } + + return new List(); + + } + + public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason) + { + return m_GroupsService.AddAgentToGroup(RequestingAgentID, AgentID, GroupID, RoleID, token, out reason); + } + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + m_GroupsService.RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID); + } + + public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID) + { + return m_GroupsService.AddAgentToGroupInvite(RequestingAgentID, inviteID, groupID, roleID, agentID); + } + + public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + return m_GroupsService.GetAgentToGroupInvite(RequestingAgentID, inviteID); ; + } + + public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + m_GroupsService.RemoveAgentToGroupInvite(RequestingAgentID, inviteID); + } + + public void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + m_GroupsService.AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + } + + public void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + m_GroupsService.RemoveAgentFromGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + } + + public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID) + { + return m_GroupsService.GetAgentGroupRoles(RequestingAgentID, AgentID, GroupID); + } + + public void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + m_GroupsService.SetAgentActiveGroup(RequestingAgentID, AgentID, GroupID); + } + + public ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID) + { + return m_GroupsService.GetAgentActiveMembership(RequestingAgentID, AgentID); + } + + public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + m_GroupsService.SetAgentActiveGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + } + + public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile) + { + m_GroupsService.UpdateMembership(RequestingAgentID, AgentID, GroupID, AcceptNotices, ListInProfile); + } + + public ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID) + { + return m_GroupsService.GetAgentGroupMembership(RequestingAgentID, AgentID, GroupID); ; + } + + public List GetAgentGroupMemberships(string RequestingAgentID, string AgentID) + { + return m_GroupsService.GetAgentGroupMemberships(RequestingAgentID, AgentID); + } + + public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + return m_GroupsService.AddGroupNotice(RequestingAgentID, groupID, noticeID, fromName, subject, message, + hasAttachment, attType, attName, attItemID, attOwnerID); + } + + public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID) + { + GroupNoticeInfo notice = m_GroupsService.GetGroupNotice(RequestingAgentID, noticeID); + + //if (notice != null && notice.noticeData.HasAttachment && notice.noticeData.AttachmentOwnerID != null) + //{ + // UUID userID = UUID.Zero; + // string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty; + // Util.ParseUniversalUserIdentifier(notice.noticeData.AttachmentOwnerID, out userID, out url, out first, out last, out tmp); + // if (url != string.Empty) + // m_UserManagement.AddUser(userID, first, last, url); + //} + + return notice; + } + + public List GetGroupNotices(string RequestingAgentID, UUID GroupID) + { + return m_GroupsService.GetGroupNotices(RequestingAgentID, GroupID); + } + + public void ResetAgentGroupChatSessions(string agentID) + { + } + + public bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public void AgentDroppedFromGroupChatSession(string agentID, UUID groupID) + { + } + + public void AgentInvitedToGroupChatSession(string agentID, UUID groupID) + { + } + + #endregion + } +} diff --git a/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs b/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..5ccd7fea1b --- /dev/null +++ b/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Mono.Addins; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenSim.Addons.Groups")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("http://opensimulator.org")] +[assembly: AssemblyProduct("OpenSim.Addons.Groups")] +[assembly: AssemblyCopyright("Copyright (c) OpenSimulator.org Developers")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("313d4865-d179-4735-9b5a-fe74885878b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.7.6.*")] + +[assembly: Addin("OpenSim.Groups", "0.1")] +[assembly: AddinDependency("OpenSim", "0.5")] diff --git a/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs new file mode 100644 index 0000000000..04328c9c3a --- /dev/null +++ b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs @@ -0,0 +1,642 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.Linq; +using System.Reflection; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Server.Base; + +using OpenMetaverse; +using log4net; + +namespace OpenSim.Groups +{ + public class GroupsServiceRemoteConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI; + private object m_Lock = new object(); + + public GroupsServiceRemoteConnector(string url) + { + m_ServerURI = url; + if (!m_ServerURI.EndsWith("/")) + m_ServerURI += "/"; + + m_log.DebugFormat("[Groups.RemoteConnector]: Groups server at {0}", m_ServerURI); + } + + public ExtendedGroupRecord CreateGroup(string RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, + bool allowPublish, bool maturePublish, UUID founderID, out string reason) + { + reason = string.Empty; + + ExtendedGroupRecord rec = new ExtendedGroupRecord(); + rec.AllowPublish = allowPublish; + rec.Charter = charter; + rec.FounderID = founderID; + rec.GroupName = name; + rec.GroupPicture = insigniaID; + rec.MaturePublish = maturePublish; + rec.MembershipFee = membershipFee; + rec.OpenEnrollment = openEnrollment; + rec.ShowInList = showInList; + + Dictionary sendData = GroupsDataUtils.GroupRecord(rec); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "ADD"; + Dictionary ret = MakeRequest("PUTGROUP", sendData); + + if (ret == null) + return null; + + if (ret["RESULT"].ToString() == "NULL") + { + reason = ret["REASON"].ToString(); + return null; + } + + return GroupsDataUtils.GroupRecord((Dictionary)ret["RESULT"]); + + } + + public ExtendedGroupRecord UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) + { + ExtendedGroupRecord rec = new ExtendedGroupRecord(); + rec.AllowPublish = allowPublish; + rec.Charter = charter; + rec.GroupPicture = insigniaID; + rec.MaturePublish = maturePublish; + rec.GroupID = groupID; + rec.MembershipFee = membershipFee; + rec.OpenEnrollment = openEnrollment; + rec.ShowInList = showInList; + + Dictionary sendData = GroupsDataUtils.GroupRecord(rec); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "UPDATE"; + Dictionary ret = MakeRequest("PUTGROUP", sendData); + + if (ret == null || (ret != null && ret["RESULT"].ToString() == "NULL")) + return null; + + return GroupsDataUtils.GroupRecord((Dictionary)ret["RESULT"]); + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName) + { + if (GroupID == UUID.Zero && (GroupName == null || (GroupName != null && GroupName == string.Empty))) + return null; + + Dictionary sendData = new Dictionary(); + if (GroupID != UUID.Zero) + sendData["GroupID"] = GroupID.ToString(); + if (GroupName != null && GroupName != string.Empty) + sendData["Name"] = GroupsDataUtils.Sanitize(GroupName); + + sendData["RequestingAgentID"] = RequestingAgentID; + + Dictionary ret = MakeRequest("GETGROUP", sendData); + + if (ret == null || (ret != null && ret["RESULT"].ToString() == "NULL")) + return null; + + return GroupsDataUtils.GroupRecord((Dictionary)ret["RESULT"]); + } + + public GroupMembershipData AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason) + { + reason = string.Empty; + + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID; + sendData["GroupID"] = GroupID.ToString(); + sendData["RoleID"] = RoleID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AccessToken"] = token; + Dictionary ret = MakeRequest("ADDAGENTTOGROUP", sendData); + + if (ret == null) + return null; + + if (!ret.ContainsKey("RESULT")) + return null; + + if (ret["RESULT"].ToString() == "NULL") + { + reason = ret["REASON"].ToString(); + return null; + } + + return GroupsDataUtils.GroupMembershipData((Dictionary)ret["RESULT"]); + + } + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID; + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + MakeRequest("REMOVEAGENTFROMGROUP", sendData); + } + + public ExtendedGroupMembershipData GetMembership(string RequestingAgentID, string AgentID, UUID GroupID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID; + if (GroupID != UUID.Zero) + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary ret = MakeRequest("GETMEMBERSHIP", sendData); + + if (ret == null) + return null; + + if (!ret.ContainsKey("RESULT")) + return null; + + if (ret["RESULT"].ToString() == "NULL") + return null; + + return GroupsDataUtils.GroupMembershipData((Dictionary)ret["RESULT"]); + } + + public List GetMemberships(string RequestingAgentID, string AgentID) + { + List memberships = new List(); + + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID; + sendData["ALL"] = "true"; + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary ret = MakeRequest("GETMEMBERSHIP", sendData); + + if (ret == null) + return memberships; + + if (!ret.ContainsKey("RESULT")) + return memberships; + + if (ret["RESULT"].ToString() == "NULL") + return memberships; + + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + GroupMembershipData m = GroupsDataUtils.GroupMembershipData((Dictionary)v); + memberships.Add(m); + } + + return memberships; + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID) + { + List members = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary ret = MakeRequest("GETGROUPMEMBERS", sendData); + + if (ret == null) + return members; + + if (!ret.ContainsKey("RESULT")) + return members; + + if (ret["RESULT"].ToString() == "NULL") + return members; + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + ExtendedGroupMembersData m = GroupsDataUtils.GroupMembersData((Dictionary)v); + members.Add(m); + } + + return members; + } + + public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason) + { + reason = string.Empty; + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = groupID.ToString(); + sendData["RoleID"] = roleID.ToString(); + sendData["Name"] = GroupsDataUtils.Sanitize(name); + sendData["Description"] = GroupsDataUtils.Sanitize(description); + sendData["Title"] = GroupsDataUtils.Sanitize(title); + sendData["Powers"] = powers.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "ADD"; + Dictionary ret = MakeRequest("PUTROLE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + { + reason = ret["REASON"].ToString(); + return false; + } + + return true; + } + + public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers) + { + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = groupID.ToString(); + sendData["RoleID"] = roleID.ToString(); + sendData["Name"] = GroupsDataUtils.Sanitize(name); + sendData["Description"] = GroupsDataUtils.Sanitize(description); + sendData["Title"] = GroupsDataUtils.Sanitize(title); + sendData["Powers"] = powers.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "UPDATE"; + Dictionary ret = MakeRequest("PUTROLE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + return false; + + return true; + } + + public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID) + { + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = groupID.ToString(); + sendData["RoleID"] = roleID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + MakeRequest("REMOVEROLE", sendData); + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID) + { + List roles = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary ret = MakeRequest("GETGROUPROLES", sendData); + + if (ret == null) + return roles; + + if (!ret.ContainsKey("RESULT")) + return roles; + + if (ret["RESULT"].ToString() == "NULL") + return roles; + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + GroupRolesData m = GroupsDataUtils.GroupRolesData((Dictionary)v); + roles.Add(m); + } + + return roles; + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID) + { + List rmembers = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary ret = MakeRequest("GETROLEMEMBERS", sendData); + + if (ret == null) + return rmembers; + + if (!ret.ContainsKey("RESULT")) + return rmembers; + + if (ret["RESULT"].ToString() == "NULL") + return rmembers; + + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + ExtendedGroupRoleMembersData m = GroupsDataUtils.GroupRoleMembersData((Dictionary)v); + rmembers.Add(m); + } + + return rmembers; + } + + public bool AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RoleID"] = RoleID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "ADD"; + + Dictionary ret = MakeRequest("AGENTROLE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + return false; + + return true; + } + + public bool RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RoleID"] = RoleID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "DELETE"; + + Dictionary ret = MakeRequest("AGENTROLE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + return false; + + return true; + } + + public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID) + { + List roles = new List(); + + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary ret = MakeRequest("GETAGENTROLES", sendData); + + if (ret == null) + return roles; + + if (!ret.ContainsKey("RESULT")) + return roles; + + if (ret["RESULT"].ToString() == "NULL") + return roles; + + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + GroupRolesData m = GroupsDataUtils.GroupRolesData((Dictionary)v); + roles.Add(m); + } + + return roles; + } + + public GroupMembershipData SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "GROUP"; + + Dictionary ret = MakeRequest("SETACTIVE", sendData); + + if (ret == null) + return null; + + if (!ret.ContainsKey("RESULT")) + return null; + + if (ret["RESULT"].ToString() == "NULL") + return null; + + return GroupsDataUtils.GroupMembershipData((Dictionary)ret["RESULT"]); + } + + public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RoleID"] = RoleID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "ROLE"; + + MakeRequest("SETACTIVE", sendData); + } + + public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["AcceptNotices"] = AcceptNotices.ToString(); + sendData["ListInProfile"] = ListInProfile.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + MakeRequest("UPDATEMEMBERSHIP", sendData); + } + + public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID) + { + Dictionary sendData = new Dictionary(); + sendData["InviteID"] = inviteID.ToString(); + sendData["GroupID"] = groupID.ToString(); + sendData["RoleID"] = roleID.ToString(); + sendData["AgentID"] = agentID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "ADD"; + + Dictionary ret = MakeRequest("INVITE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") // it may return "NULL" + return false; + + return true; + } + + public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + Dictionary sendData = new Dictionary(); + sendData["InviteID"] = inviteID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "GET"; + + Dictionary ret = MakeRequest("INVITE", sendData); + + if (ret == null) + return null; + + if (!ret.ContainsKey("RESULT")) + return null; + + if (ret["RESULT"].ToString() == "NULL") + return null; + + return GroupsDataUtils.GroupInviteInfo((Dictionary)ret["RESULT"]); + } + + public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + Dictionary sendData = new Dictionary(); + sendData["InviteID"] = inviteID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "DELETE"; + + MakeRequest("INVITE", sendData); + } + + public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = groupID.ToString(); + sendData["NoticeID"] = noticeID.ToString(); + sendData["FromName"] = GroupsDataUtils.Sanitize(fromName); + sendData["Subject"] = GroupsDataUtils.Sanitize(subject); + sendData["Message"] = GroupsDataUtils.Sanitize(message); + sendData["HasAttachment"] = hasAttachment.ToString(); + if (hasAttachment) + { + sendData["AttachmentType"] = attType.ToString(); + sendData["AttachmentName"] = attName.ToString(); + sendData["AttachmentItemID"] = attItemID.ToString(); + sendData["AttachmentOwnerID"] = attOwnerID; + } + sendData["RequestingAgentID"] = RequestingAgentID; + + Dictionary ret = MakeRequest("ADDNOTICE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + return false; + + return true; + } + + public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID) + { + Dictionary sendData = new Dictionary(); + sendData["NoticeID"] = noticeID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + + Dictionary ret = MakeRequest("GETNOTICES", sendData); + + if (ret == null) + return null; + + if (!ret.ContainsKey("RESULT")) + return null; + + if (ret["RESULT"].ToString() == "NULL") + return null; + + return GroupsDataUtils.GroupNoticeInfo((Dictionary)ret["RESULT"]); + } + + public List GetGroupNotices(string RequestingAgentID, UUID GroupID) + { + List notices = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary ret = MakeRequest("GETNOTICES", sendData); + + if (ret == null) + return notices; + + if (!ret.ContainsKey("RESULT")) + return notices; + + if (ret["RESULT"].ToString() == "NULL") + return notices; + + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + ExtendedGroupNoticeData m = GroupsDataUtils.GroupNoticeData((Dictionary)v); + notices.Add(m); + } + + return notices; + } + + #region Make Request + + private Dictionary MakeRequest(string method, Dictionary sendData) + { + sendData["METHOD"] = method; + + string reply = string.Empty; + lock (m_Lock) + reply = SynchronousRestFormsRequester.MakeRequest("POST", + m_ServerURI + "groups", + ServerUtils.BuildQueryString(sendData)); + + if (reply == string.Empty) + return null; + + Dictionary replyData = ServerUtils.ParseXmlResponse( + reply); + + return replyData; + } + #endregion + + } +} diff --git a/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnectorModule.cs b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnectorModule.cs new file mode 100644 index 0000000000..d1c02db721 --- /dev/null +++ b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnectorModule.cs @@ -0,0 +1,437 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.Linq; +using System.Reflection; +using System.Threading; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Server.Base; + +using OpenMetaverse; +using Mono.Addins; +using log4net; +using Nini.Config; + +namespace OpenSim.Groups +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsServiceRemoteConnectorModule")] + public class GroupsServiceRemoteConnectorModule : ISharedRegionModule, IGroupsServicesConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private bool m_Enabled = false; + private GroupsServiceRemoteConnector m_GroupsService; + private IUserManagement m_UserManagement; + private List m_Scenes; + + private RemoteConnectorCacheWrapper m_CacheWrapper; + + #region constructors + public GroupsServiceRemoteConnectorModule() + { + } + + public GroupsServiceRemoteConnectorModule(IConfigSource config, IUserManagement uman) + { + Init(config); + m_UserManagement = uman; + m_CacheWrapper = new RemoteConnectorCacheWrapper(m_UserManagement); + + } + #endregion + + private void Init(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + string url = groupsConfig.GetString("GroupsServerURI", string.Empty); + if (url == string.Empty) + { + m_log.WarnFormat("[Groups.RemoteConnector]: Groups server URL not provided. Groups will not work."); + return; + } + + m_GroupsService = new GroupsServiceRemoteConnector(url); + m_Scenes = new List(); + + } + + #region ISharedRegionModule + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + if (groupsConfig == null) + return; + + if ((groupsConfig.GetBoolean("Enabled", false) == false) + || (groupsConfig.GetString("ServicesConnectorModule", string.Empty) != Name)) + { + return; + } + + Init(config); + + m_Enabled = true; + m_log.DebugFormat("[Groups.RemoteConnector]: Initializing {0}", this.Name); + } + + public string Name + { + get { return "Groups Remote Service Connector"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public void AddRegion(Scene scene) + { + if (!m_Enabled) + return; + + m_log.DebugFormat("[Groups.RemoteConnector]: Registering {0} with {1}", this.Name, scene.RegionInfo.RegionName); + scene.RegisterModuleInterface(this); + m_Scenes.Add(scene); + } + + public void RemoveRegion(Scene scene) + { + if (!m_Enabled) + return; + + scene.UnregisterModuleInterface(this); + m_Scenes.Remove(scene); + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + if (m_UserManagement == null) + { + m_UserManagement = scene.RequestModuleInterface(); + m_CacheWrapper = new RemoteConnectorCacheWrapper(m_UserManagement); + } + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + #endregion + + #region IGroupsServicesConnector + + public UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, + bool allowPublish, bool maturePublish, UUID founderID, out string reason) + { + m_log.DebugFormat("[Groups.RemoteConnector]: Creating group {0}", name); + string r = string.Empty; + + UUID groupID = m_CacheWrapper.CreateGroup(RequestingAgentID, delegate + { + return m_GroupsService.CreateGroup(RequestingAgentID.ToString(), name, charter, showInList, insigniaID, + membershipFee, openEnrollment, allowPublish, maturePublish, founderID, out r); + }); + + reason = r; + return groupID; + } + + public bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, + bool openEnrollment, bool allowPublish, bool maturePublish, out string reason) + { + string r = string.Empty; + + bool success = m_CacheWrapper.UpdateGroup(groupID, delegate + { + return m_GroupsService.UpdateGroup(RequestingAgentID, groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish); + }); + + reason = r; + return success; + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName) + { + if (GroupID == UUID.Zero && (GroupName == null || GroupName != null && GroupName == string.Empty)) + return null; + + return m_CacheWrapper.GetGroupRecord(RequestingAgentID,GroupID,GroupName, delegate + { + return m_GroupsService.GetGroupRecord(RequestingAgentID, GroupID, GroupName); + }); + } + + public List FindGroups(string RequestingAgentID, string search) + { + // TODO! + return new List(); + } + + public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason) + { + string agentFullID = AgentID; + m_log.DebugFormat("[Groups.RemoteConnector]: Add agent {0} to group {1}", agentFullID, GroupID); + string r = string.Empty; + + bool success = m_CacheWrapper.AddAgentToGroup(RequestingAgentID, AgentID, GroupID, delegate + { + return m_GroupsService.AddAgentToGroup(RequestingAgentID, agentFullID, GroupID, RoleID, token, out r); + }); + + reason = r; + return success; + } + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + m_CacheWrapper.RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID, delegate + { + m_GroupsService.RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID); + }); + + } + + public void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + m_CacheWrapper.SetAgentActiveGroup(AgentID, delegate + { + return m_GroupsService.SetAgentActiveGroup(RequestingAgentID, AgentID, GroupID); + }); + } + + public ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID) + { + return m_CacheWrapper.GetAgentActiveMembership(AgentID, delegate + { + return m_GroupsService.GetMembership(RequestingAgentID, AgentID, UUID.Zero); + }); + } + + public ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID) + { + return m_CacheWrapper.GetAgentGroupMembership(AgentID, GroupID, delegate + { + return m_GroupsService.GetMembership(RequestingAgentID, AgentID, GroupID); + }); + } + + public List GetAgentGroupMemberships(string RequestingAgentID, string AgentID) + { + return m_CacheWrapper.GetAgentGroupMemberships(AgentID, delegate + { + return m_GroupsService.GetMemberships(RequestingAgentID, AgentID); + }); + } + + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID) + { + return m_CacheWrapper.GetGroupMembers(RequestingAgentID, GroupID, delegate + { + return m_GroupsService.GetGroupMembers(RequestingAgentID, GroupID); + }); + } + + public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason) + { + string r = string.Empty; + bool success = m_CacheWrapper.AddGroupRole(roleID, description, name, powers, title, delegate + { + return m_GroupsService.AddGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers, out r); + }); + + reason = r; + return success; + } + + public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers) + { + return m_CacheWrapper.UpdateGroupRole(groupID, roleID, name, description, title, powers, delegate + { + return m_GroupsService.UpdateGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers); + }); + } + + public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID) + { + m_CacheWrapper.RemoveGroupRole(RequestingAgentID, groupID, roleID, delegate + { + m_GroupsService.RemoveGroupRole(RequestingAgentID, groupID, roleID); + }); + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID) + { + return m_CacheWrapper.GetGroupRoles(RequestingAgentID, GroupID, delegate + { + return m_GroupsService.GetGroupRoles(RequestingAgentID, GroupID); + }); + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID) + { + return m_CacheWrapper.GetGroupRoleMembers(RequestingAgentID, GroupID, delegate + { + return m_GroupsService.GetGroupRoleMembers(RequestingAgentID, GroupID); + }); + } + + public void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + m_CacheWrapper.AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID, delegate + { + return m_GroupsService.AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + }); + } + + public void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + m_CacheWrapper.RemoveAgentFromGroupRole(RequestingAgentID, AgentID, GroupID, RoleID, delegate + { + return m_GroupsService.RemoveAgentFromGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + }); + } + + public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID) + { + return m_CacheWrapper.GetAgentGroupRoles(RequestingAgentID, AgentID, GroupID, delegate + { + return m_GroupsService.GetAgentGroupRoles(RequestingAgentID, AgentID, GroupID); ; + }); + } + + public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + m_CacheWrapper.SetAgentActiveGroupRole(AgentID, GroupID, delegate + { + m_GroupsService.SetAgentActiveGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + }); + } + + public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile) + { + m_CacheWrapper.UpdateMembership(AgentID, GroupID, AcceptNotices, ListInProfile, delegate + { + m_GroupsService.UpdateMembership(RequestingAgentID, AgentID, GroupID, AcceptNotices, ListInProfile); + }); + } + + public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID) + { + return m_GroupsService.AddAgentToGroupInvite(RequestingAgentID, inviteID, groupID, roleID, agentID); + } + + public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + return m_GroupsService.GetAgentToGroupInvite(RequestingAgentID, inviteID); + } + + public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + m_GroupsService.RemoveAgentToGroupInvite(RequestingAgentID, inviteID); + } + + public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + GroupNoticeInfo notice = new GroupNoticeInfo(); + notice.GroupID = groupID; + notice.Message = message; + notice.noticeData = new ExtendedGroupNoticeData(); + notice.noticeData.AttachmentItemID = attItemID; + notice.noticeData.AttachmentName = attName; + notice.noticeData.AttachmentOwnerID = attOwnerID.ToString(); + notice.noticeData.AttachmentType = attType; + notice.noticeData.FromName = fromName; + notice.noticeData.HasAttachment = hasAttachment; + notice.noticeData.NoticeID = noticeID; + notice.noticeData.Subject = subject; + notice.noticeData.Timestamp = (uint)Util.UnixTimeSinceEpoch(); + + return m_CacheWrapper.AddGroupNotice(groupID, noticeID, notice, delegate + { + return m_GroupsService.AddGroupNotice(RequestingAgentID, groupID, noticeID, fromName, subject, message, + hasAttachment, attType, attName, attItemID, attOwnerID); + }); + } + + public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID) + { + return m_CacheWrapper.GetGroupNotice(noticeID, delegate + { + return m_GroupsService.GetGroupNotice(RequestingAgentID, noticeID); + }); + } + + public List GetGroupNotices(string RequestingAgentID, UUID GroupID) + { + return m_CacheWrapper.GetGroupNotices(GroupID, delegate + { + return m_GroupsService.GetGroupNotices(RequestingAgentID, GroupID); + }); + } + + public void ResetAgentGroupChatSessions(string agentID) + { + } + + public bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public void AgentDroppedFromGroupChatSession(string agentID, UUID groupID) + { + } + + public void AgentInvitedToGroupChatSession(string agentID, UUID groupID) + { + } + + #endregion + } + +} diff --git a/OpenSim/Addons/Groups/Remote/GroupsServiceRobustConnector.cs b/OpenSim/Addons/Groups/Remote/GroupsServiceRobustConnector.cs new file mode 100644 index 0000000000..8c257edcc0 --- /dev/null +++ b/OpenSim/Addons/Groups/Remote/GroupsServiceRobustConnector.cs @@ -0,0 +1,760 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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.Reflection; +using System.Text; +using System.Xml; +using System.Collections.Generic; +using System.IO; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using log4net; +using OpenMetaverse; + +namespace OpenSim.Groups +{ + public class GroupsServiceRobustConnector : ServiceConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private GroupsService m_GroupsService; + private string m_ConfigName = "Groups"; + + public GroupsServiceRobustConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + m_log.DebugFormat("[Groups.RobustConnector]: Starting with config name {0}", m_ConfigName); + + m_GroupsService = new GroupsService(config); + + server.AddStreamHandler(new GroupsServicePostHandler(m_GroupsService)); + } + } + + public class GroupsServicePostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private GroupsService m_GroupsService; + + public GroupsServicePostHandler(GroupsService service) : + base("POST", "/groups") + { + m_GroupsService = service; + } + + public override byte[] Handle(string path, Stream requestData, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + StreamReader sr = new StreamReader(requestData); + string body = sr.ReadToEnd(); + sr.Close(); + body = body.Trim(); + + //m_log.DebugFormat("[XXX]: query String: {0}", body); + + try + { + Dictionary request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + string method = request["METHOD"].ToString(); + request.Remove("METHOD"); + + m_log.DebugFormat("[Groups.Handler]: {0}", method); + switch (method) + { + case "PUTGROUP": + return HandleAddOrUpdateGroup(request); + case "GETGROUP": + return HandleGetGroup(request); + case "ADDAGENTTOGROUP": + return HandleAddAgentToGroup(request); + case "REMOVEAGENTFROMGROUP": + return HandleRemoveAgentFromGroup(request); + case "GETMEMBERSHIP": + return HandleGetMembership(request); + case "GETGROUPMEMBERS": + return HandleGetGroupMembers(request); + case "PUTROLE": + return HandlePutRole(request); + case "REMOVEROLE": + return HandleRemoveRole(request); + case "GETGROUPROLES": + return HandleGetGroupRoles(request); + case "GETROLEMEMBERS": + return HandleGetRoleMembers(request); + case "AGENTROLE": + return HandleAgentRole(request); + case "GETAGENTROLES": + return HandleGetAgentRoles(request); + case "SETACTIVE": + return HandleSetActive(request); + case "UPDATEMEMBERSHIP": + return HandleUpdateMembership(request); + case "INVITE": + return HandleInvite(request); + case "ADDNOTICE": + return HandleAddNotice(request); + case "GETNOTICES": + return HandleGetNotices(request); + } + m_log.DebugFormat("[GROUPS HANDLER]: unknown method request: {0}", method); + } + catch (Exception e) + { + m_log.DebugFormat("[GROUPS HANDLER]: Exception {0}", e.StackTrace); + } + + return FailureResult(); + } + + byte[] HandleAddOrUpdateGroup(Dictionary request) + { + Dictionary result = new Dictionary(); + + ExtendedGroupRecord grec = GroupsDataUtils.GroupRecord(request); + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("OP")) + NullResult(result, "Bad network data"); + + else + { + string RequestingAgentID = request["RequestingAgentID"].ToString(); + string reason = string.Empty; + string op = request["OP"].ToString(); + if (op == "ADD") + { + grec.GroupID = m_GroupsService.CreateGroup(RequestingAgentID, grec.GroupName, grec.Charter, grec.ShowInList, grec.GroupPicture, grec.MembershipFee, + grec.OpenEnrollment, grec.AllowPublish, grec.MaturePublish, grec.FounderID, out reason); + + } + else if (op == "UPDATE") + { + m_GroupsService.UpdateGroup(RequestingAgentID, grec.GroupID, grec.Charter, grec.ShowInList, grec.GroupPicture, grec.MembershipFee, + grec.OpenEnrollment, grec.AllowPublish, grec.MaturePublish); + + } + + grec = m_GroupsService.GetGroupRecord(RequestingAgentID, grec.GroupID); + if (grec == null) + NullResult(result, "Internal Error"); + else + result["RESULT"] = GroupsDataUtils.GroupRecord(grec); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetGroup(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID")) + NullResult(result, "Bad network data"); + else + { + string RequestingAgentID = request["RequestingAgentID"].ToString(); + ExtendedGroupRecord grec = null; + if (request.ContainsKey("GroupID")) + { + UUID groupID = new UUID(request["GroupID"].ToString()); + grec = m_GroupsService.GetGroupRecord(RequestingAgentID, groupID); + } + else if (request.ContainsKey("Name")) + { + string name = request["Name"].ToString(); + grec = m_GroupsService.GetGroupRecord(RequestingAgentID, name); + } + + if (grec == null) + NullResult(result, "Group not found"); + else + result["RESULT"] = GroupsDataUtils.GroupRecord(grec); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleAddAgentToGroup(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AgentID") || + !request.ContainsKey("GroupID") || !request.ContainsKey("RoleID")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + UUID roleID = new UUID(request["RoleID"].ToString()); + string agentID = request["AgentID"].ToString(); + string requestingAgentID = request["RequestingAgentID"].ToString(); + string token = string.Empty; + string reason = string.Empty; + + if (request.ContainsKey("AccessToken")) + token = request["AccessToken"].ToString(); + + if (!m_GroupsService.AddAgentToGroup(requestingAgentID, agentID, groupID, roleID, token, out reason)) + NullResult(result, reason); + else + { + GroupMembershipData membership = m_GroupsService.GetAgentGroupMembership(requestingAgentID, agentID, groupID); + if (membership == null) + NullResult(result, "Internal error"); + else + result["RESULT"] = GroupsDataUtils.GroupMembershipData((ExtendedGroupMembershipData)membership); + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleRemoveAgentFromGroup(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AgentID") || !request.ContainsKey("GroupID")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string agentID = request["AgentID"].ToString(); + string requestingAgentID = request["RequestingAgentID"].ToString(); + string reason = string.Empty; + + m_GroupsService.RemoveAgentFromGroup(requestingAgentID, agentID, groupID); + } + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + result["RESULT"] = "true"; + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + byte[] HandleGetMembership(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AgentID")) + NullResult(result, "Bad network data"); + else + { + string agentID = request["AgentID"].ToString(); + UUID groupID = UUID.Zero; + if (request.ContainsKey("GroupID")) + groupID = new UUID(request["GroupID"].ToString()); + string requestingAgentID = request["RequestingAgentID"].ToString(); + bool all = request.ContainsKey("ALL"); + + if (!all) + { + ExtendedGroupMembershipData membership = null; + if (groupID == UUID.Zero) + { + membership = m_GroupsService.GetAgentActiveMembership(requestingAgentID, agentID); + } + else + { + membership = m_GroupsService.GetAgentGroupMembership(requestingAgentID, agentID, groupID); + } + + if (membership == null) + NullResult(result, "No such membership"); + else + result["RESULT"] = GroupsDataUtils.GroupMembershipData(membership); + } + else + { + List memberships = m_GroupsService.GetAgentGroupMemberships(requestingAgentID, agentID); + if (memberships == null || (memberships != null && memberships.Count == 0)) + { + NullResult(result, "No memberships"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (GroupMembershipData m in memberships) + dict["m-" + i++] = GroupsDataUtils.GroupMembershipData((ExtendedGroupMembershipData)m); + + result["RESULT"] = dict; + } + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetGroupMembers(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string requestingAgentID = request["RequestingAgentID"].ToString(); + + List members = m_GroupsService.GetGroupMembers(requestingAgentID, groupID); + if (members == null || (members != null && members.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (ExtendedGroupMembersData m in members) + { + dict["m-" + i++] = GroupsDataUtils.GroupMembersData(m); + } + + result["RESULT"] = dict; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandlePutRole(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("RoleID") || + !request.ContainsKey("Name") || !request.ContainsKey("Descrption") || !request.ContainsKey("Title") || + !request.ContainsKey("Powers") || !request.ContainsKey("OP")) + NullResult(result, "Bad network data"); + + else + { + string op = request["OP"].ToString(); + string reason = string.Empty; + + bool success = false; + if (op == "ADD") + success = m_GroupsService.AddGroupRole(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()), + new UUID(request["RoleID"].ToString()), request["Name"].ToString(), request["Description"].ToString(), + request["Title"].ToString(), UInt64.Parse(request["Powers"].ToString()), out reason); + + else if (op == "UPDATE") + success = m_GroupsService.UpdateGroupRole(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()), + new UUID(request["RoleID"].ToString()), request["Name"].ToString(), request["Description"].ToString(), + request["Title"].ToString(), UInt64.Parse(request["Powers"].ToString())); + + result["RESULT"] = success.ToString(); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleRemoveRole(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("RoleID")) + NullResult(result, "Bad network data"); + + else + { + m_GroupsService.RemoveGroupRole(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()), + new UUID(request["RoleID"].ToString())); + result["RESULT"] = "true"; + } + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + byte[] HandleGetGroupRoles(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string requestingAgentID = request["RequestingAgentID"].ToString(); + + List roles = m_GroupsService.GetGroupRoles(requestingAgentID, groupID); + if (roles == null || (roles != null && roles.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (GroupRolesData r in roles) + dict["r-" + i++] = GroupsDataUtils.GroupRolesData(r); + + result["RESULT"] = dict; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetRoleMembers(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string requestingAgentID = request["RequestingAgentID"].ToString(); + + List rmembers = m_GroupsService.GetGroupRoleMembers(requestingAgentID, groupID); + if (rmembers == null || (rmembers != null && rmembers.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (ExtendedGroupRoleMembersData rm in rmembers) + dict["rm-" + i++] = GroupsDataUtils.GroupRoleMembersData(rm); + + result["RESULT"] = dict; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleAgentRole(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("RoleID") || + !request.ContainsKey("AgentID") || !request.ContainsKey("OP")) + NullResult(result, "Bad network data"); + + else + { + string op = request["OP"].ToString(); + string reason = string.Empty; + + bool success = false; + if (op == "ADD") + success = m_GroupsService.AddAgentToGroupRole(request["RequestingAgentID"].ToString(), request["AgentID"].ToString(), + new UUID(request["GroupID"].ToString()), new UUID(request["RoleID"].ToString())); + + else if (op == "DELETE") + success = m_GroupsService.RemoveAgentFromGroupRole(request["RequestingAgentID"].ToString(), request["AgentID"].ToString(), + new UUID(request["GroupID"].ToString()), new UUID(request["RoleID"].ToString())); + + result["RESULT"] = success.ToString(); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetAgentRoles(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AgentID")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string agentID = request["AgentID"].ToString(); + string requestingAgentID = request["RequestingAgentID"].ToString(); + + List roles = m_GroupsService.GetAgentGroupRoles(requestingAgentID, agentID, groupID); + if (roles == null || (roles != null && roles.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (GroupRolesData r in roles) + dict["r-" + i++] = GroupsDataUtils.GroupRolesData(r); + + result["RESULT"] = dict; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleSetActive(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || + !request.ContainsKey("AgentID") || !request.ContainsKey("OP")) + { + NullResult(result, "Bad network data"); + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + else + { + string op = request["OP"].ToString(); + string reason = string.Empty; + + if (op == "GROUP") + { + ExtendedGroupMembershipData group = m_GroupsService.SetAgentActiveGroup(request["RequestingAgentID"].ToString(), + request["AgentID"].ToString(), new UUID(request["GroupID"].ToString())); + + if (group == null) + NullResult(result, "Internal error"); + else + result["RESULT"] = GroupsDataUtils.GroupMembershipData(group); + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + + } + else if (op == "ROLE" && request.ContainsKey("RoleID")) + { + m_GroupsService.SetAgentActiveGroupRole(request["RequestingAgentID"].ToString(), request["AgentID"].ToString(), + new UUID(request["GroupID"].ToString()), new UUID(request["RoleID"].ToString())); + result["RESULT"] = "true"; + } + + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + } + + byte[] HandleUpdateMembership(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AgentID") || !request.ContainsKey("GroupID") || + !request.ContainsKey("AcceptNotices") || !request.ContainsKey("ListInProfile")) + NullResult(result, "Bad network data"); + + else + { + m_GroupsService.UpdateMembership(request["RequestingAgentID"].ToString(), request["AgentID"].ToString(), new UUID(request["GroupID"].ToString()), + bool.Parse(request["AcceptNotices"].ToString()), bool.Parse(request["ListInProfile"].ToString())); + + result["RESULT"] = "true"; + } + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + byte[] HandleInvite(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("InviteID")) + { + NullResult(result, "Bad network data"); + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + else + { + string op = request["OP"].ToString(); + string reason = string.Empty; + + if (op == "ADD" && request.ContainsKey("GroupID") && request.ContainsKey("RoleID") && request.ContainsKey("AgentID")) + { + bool success = m_GroupsService.AddAgentToGroupInvite(request["RequestingAgentID"].ToString(), + new UUID(request["InviteID"].ToString()), new UUID(request["GroupID"].ToString()), + new UUID(request["RoleID"].ToString()), request["AgentID"].ToString()); + + result["RESULT"] = success.ToString(); + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + + } + else if (op == "DELETE") + { + m_GroupsService.RemoveAgentToGroupInvite(request["RequestingAgentID"].ToString(), new UUID(request["InviteID"].ToString())); + result["RESULT"] = "true"; + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + else if (op == "GET") + { + GroupInviteInfo invite = m_GroupsService.GetAgentToGroupInvite(request["RequestingAgentID"].ToString(), + new UUID(request["InviteID"].ToString())); + + result["RESULT"] = GroupsDataUtils.GroupInviteInfo(invite); + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + NullResult(result, "Bad OP in request"); + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + } + + byte[] HandleAddNotice(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("NoticeID") || + !request.ContainsKey("FromName") || !request.ContainsKey("Subject") || !request.ContainsKey("Message") || + !request.ContainsKey("HasAttachment")) + NullResult(result, "Bad network data"); + + else + { + + bool hasAtt = bool.Parse(request["HasAttachment"].ToString()); + byte attType = 0; + string attName = string.Empty; + string attOwner = string.Empty; + UUID attItem = UUID.Zero; + if (request.ContainsKey("AttachmentType")) + attType = byte.Parse(request["AttachmentType"].ToString()); + if (request.ContainsKey("AttachmentName")) + attName = request["AttachmentName"].ToString(); + if (request.ContainsKey("AttachmentItemID")) + attItem = new UUID(request["AttachmentItemID"].ToString()); + if (request.ContainsKey("AttachmentOwnerID")) + attOwner = request["AttachmentOwnerID"].ToString(); + + bool success = m_GroupsService.AddGroupNotice(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()), + new UUID(request["NoticeID"].ToString()), request["FromName"].ToString(), request["Subject"].ToString(), + request["Message"].ToString(), hasAtt, attType, attName, attItem, attOwner); + + result["RESULT"] = success.ToString(); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetNotices(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID")) + NullResult(result, "Bad network data"); + + else if (request.ContainsKey("NoticeID")) // just one + { + GroupNoticeInfo notice = m_GroupsService.GetGroupNotice(request["RequestingAgentID"].ToString(), new UUID(request["NoticeID"].ToString())); + + if (notice == null) + NullResult(result, "NO such notice"); + else + result["RESULT"] = GroupsDataUtils.GroupNoticeInfo(notice); + + } + else if (request.ContainsKey("GroupID")) // all notices for group + { + List notices = m_GroupsService.GetGroupNotices(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString())); + + if (notices == null || (notices != null && notices.Count == 0)) + NullResult(result, "No notices"); + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (ExtendedGroupNoticeData n in notices) + dict["n-" + i++] = GroupsDataUtils.GroupNoticeData(n); + + result["RESULT"] = dict; + } + + } + else + NullResult(result, "Bad OP in request"); + + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + + #region Helpers + + private void NullResult(Dictionary result, string reason) + { + result["RESULT"] = "NULL"; + result["REASON"] = reason; + } + + private byte[] FailureResult() + { + Dictionary result = new Dictionary(); + NullResult(result, "Unknown method"); + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + #endregion + } +} diff --git a/OpenSim/Addons/Groups/RemoteConnectorCacheWrapper.cs b/OpenSim/Addons/Groups/RemoteConnectorCacheWrapper.cs new file mode 100644 index 0000000000..f789626d07 --- /dev/null +++ b/OpenSim/Addons/Groups/RemoteConnectorCacheWrapper.cs @@ -0,0 +1,824 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.Reflection; +using System.Threading; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +using OpenMetaverse; + +namespace OpenSim.Groups +{ + public delegate ExtendedGroupRecord GroupRecordDelegate(); + public delegate GroupMembershipData GroupMembershipDelegate(); + public delegate List GroupMembershipListDelegate(); + public delegate List GroupMembersListDelegate(); + public delegate List GroupRolesListDelegate(); + public delegate List RoleMembersListDelegate(); + public delegate GroupNoticeInfo NoticeDelegate(); + public delegate List NoticeListDelegate(); + public delegate void VoidDelegate(); + public delegate bool BooleanDelegate(); + + public class RemoteConnectorCacheWrapper + { + private ForeignImporter m_ForeignImporter; + + private Dictionary m_ActiveRequests = new Dictionary(); + private const int GROUPS_CACHE_TIMEOUT = 5 * 60; // 5 minutes + + // This all important cache cahces objects of different types: + // group- or group- => ExtendedGroupRecord + // active- => GroupMembershipData + // membership-- => GroupMembershipData + // memberships- => List + // members-- => List + // role- => GroupRolesData + // roles- => List ; all roles in the group + // roles-- => List ; roles that the agent has + // rolemembers-- => List + // notice- => GroupNoticeInfo + // notices- => List + private ExpiringCache m_Cache = new ExpiringCache(); + + public RemoteConnectorCacheWrapper(IUserManagement uman) + { + m_ForeignImporter = new ForeignImporter(uman); + } + + public UUID CreateGroup(UUID RequestingAgentID, GroupRecordDelegate d) + { + //m_log.DebugFormat("[Groups.RemoteConnector]: Creating group {0}", name); + //reason = string.Empty; + + //ExtendedGroupRecord group = m_GroupsService.CreateGroup(RequestingAgentID.ToString(), name, charter, showInList, insigniaID, + // membershipFee, openEnrollment, allowPublish, maturePublish, founderID, out reason); + ExtendedGroupRecord group = d(); + + if (group == null) + return UUID.Zero; + + if (group.GroupID != UUID.Zero) + lock (m_Cache) + { + m_Cache.Add("group-" + group.GroupID.ToString(), group, GROUPS_CACHE_TIMEOUT); + if (m_Cache.Contains("memberships-" + RequestingAgentID.ToString())) + m_Cache.Remove("memberships-" + RequestingAgentID.ToString()); + } + + return group.GroupID; + } + + public bool UpdateGroup(UUID groupID, GroupRecordDelegate d) + { + //reason = string.Empty; + //ExtendedGroupRecord group = m_GroupsService.UpdateGroup(RequestingAgentID, groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish); + ExtendedGroupRecord group = d(); + + if (group != null && group.GroupID != UUID.Zero) + lock (m_Cache) + m_Cache.AddOrUpdate("group-" + group.GroupID.ToString(), group, GROUPS_CACHE_TIMEOUT); + return true; + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName, GroupRecordDelegate d) + { + //if (GroupID == UUID.Zero && (GroupName == null || GroupName != null && GroupName == string.Empty)) + // return null; + + object group = null; + bool firstCall = false; + string cacheKey = "group-"; + if (GroupID != UUID.Zero) + cacheKey += GroupID.ToString(); + else + cacheKey += GroupName; + + //m_log.DebugFormat("[XXX]: GetGroupRecord {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out group)) + { + //m_log.DebugFormat("[XXX]: GetGroupRecord {0} cached!", cacheKey); + return (ExtendedGroupRecord)group; + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + //group = m_GroupsService.GetGroupRecord(RequestingAgentID, GroupID, GroupName); + group = d(); + + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, group, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (ExtendedGroupRecord)group; + } + } + else + Thread.Sleep(50); + } + } + + public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, GroupMembershipDelegate d) + { + GroupMembershipData membership = d(); + if (membership == null) + return false; + + lock (m_Cache) + { + // first, remove everything! add a user is a heavy-duty op + m_Cache.Clear(); + + m_Cache.AddOrUpdate("active-" + AgentID.ToString(), membership, GROUPS_CACHE_TIMEOUT); + m_Cache.AddOrUpdate("membership-" + AgentID.ToString() + "-" + GroupID.ToString(), membership, GROUPS_CACHE_TIMEOUT); + } + + + return true; + } + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID, VoidDelegate d) + { + d(); + + lock (m_Cache) + { + string cacheKey = "active-" + AgentID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "memberships-" + AgentID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "membership-" + AgentID.ToString() + "-" + GroupID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "members-" + RequestingAgentID.ToString() + "-" + GroupID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "roles-" + "-" + GroupID.ToString() + "-" + AgentID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + } + } + + public void SetAgentActiveGroup(string AgentID, GroupMembershipDelegate d) + { + GroupMembershipData activeGroup = d(); + if (activeGroup != null) + { + string cacheKey = "active-" + AgentID.ToString(); + lock (m_Cache) + if (m_Cache.Contains(cacheKey)) + m_Cache.AddOrUpdate(cacheKey, activeGroup, GROUPS_CACHE_TIMEOUT); + } + } + + public ExtendedGroupMembershipData GetAgentActiveMembership(string AgentID, GroupMembershipDelegate d) + { + object membership = null; + bool firstCall = false; + string cacheKey = "active-" + AgentID.ToString(); + + //m_log.DebugFormat("[XXX]: GetAgentActiveMembership {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out membership)) + { + //m_log.DebugFormat("[XXX]: GetAgentActiveMembership {0} cached!", cacheKey); + return (ExtendedGroupMembershipData)membership; + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + membership = d(); + + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, membership, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (ExtendedGroupMembershipData)membership; + } + } + else + Thread.Sleep(50); + } + + } + + public ExtendedGroupMembershipData GetAgentGroupMembership(string AgentID, UUID GroupID, GroupMembershipDelegate d) + { + object membership = null; + bool firstCall = false; + string cacheKey = "membership-" + AgentID.ToString() + "-" + GroupID.ToString(); + + //m_log.DebugFormat("[XXX]: GetAgentGroupMembership {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out membership)) + { + //m_log.DebugFormat("[XXX]: GetAgentGroupMembership {0}", cacheKey); + return (ExtendedGroupMembershipData)membership; + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + membership = d(); + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, membership, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (ExtendedGroupMembershipData)membership; + } + } + else + Thread.Sleep(50); + } + } + + public List GetAgentGroupMemberships(string AgentID, GroupMembershipListDelegate d) + { + object memberships = null; + bool firstCall = false; + string cacheKey = "memberships-" + AgentID.ToString(); + + //m_log.DebugFormat("[XXX]: GetAgentGroupMemberships {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out memberships)) + { + //m_log.DebugFormat("[XXX]: GetAgentGroupMemberships {0} cached!", cacheKey); + return (List)memberships; + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + memberships = d(); + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, memberships, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (List)memberships; + } + } + else + Thread.Sleep(50); + } + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID, GroupMembersListDelegate d) + { + object members = null; + bool firstCall = false; + // we need to key in also on the requester, because different ppl have different view privileges + string cacheKey = "members-" + RequestingAgentID.ToString() + "-" + GroupID.ToString(); + + //m_log.DebugFormat("[XXX]: GetGroupMembers {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out members)) + { + List xx = (List)members; + return xx.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupMembersData)); + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + List _members = d(); + + if (_members != null && _members.Count > 0) + members = _members.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupMembersData)); + else + members = new List(); + + lock (m_Cache) + { + //m_Cache.AddOrUpdate(cacheKey, members, GROUPS_CACHE_TIMEOUT); + m_Cache.AddOrUpdate(cacheKey, _members, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + + return (List)members; + } + } + else + Thread.Sleep(50); + } + } + + public bool AddGroupRole(UUID roleID, string description, string name, ulong powers, string title, BooleanDelegate d) + { + if (d()) + { + GroupRolesData role = new GroupRolesData(); + role.Description = description; + role.Members = 0; + role.Name = name; + role.Powers = powers; + role.RoleID = roleID; + role.Title = title; + + lock (m_Cache) + m_Cache.AddOrUpdate("role-" + roleID.ToString(), role, GROUPS_CACHE_TIMEOUT); + + return true; + } + + return false; + } + + public bool UpdateGroupRole(UUID groupID, UUID roleID, string name, string description, string title, ulong powers, BooleanDelegate d) + { + if (d()) + { + object role; + lock (m_Cache) + if (m_Cache.TryGetValue("role-" + roleID.ToString(), out role)) + { + GroupRolesData r = (GroupRolesData)role; + r.Description = description; + r.Name = name; + r.Powers = powers; + r.Title = title; + + m_Cache.Update("role-" + roleID.ToString(), r, GROUPS_CACHE_TIMEOUT); + } + return true; + } + else + { + lock (m_Cache) + { + if (m_Cache.Contains("role-" + roleID.ToString())) + m_Cache.Remove("role-" + roleID.ToString()); + + // also remove these lists, because they will have an outdated role + if (m_Cache.Contains("roles-" + groupID.ToString())) + m_Cache.Remove("roles-" + groupID.ToString()); + + } + + return false; + } + } + + public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, VoidDelegate d) + { + d(); + + lock (m_Cache) + { + if (m_Cache.Contains("role-" + roleID.ToString())) + m_Cache.Remove("role-" + roleID.ToString()); + + // also remove the list, because it will have an removed role + if (m_Cache.Contains("roles-" + groupID.ToString())) + m_Cache.Remove("roles-" + groupID.ToString()); + + if (m_Cache.Contains("roles-" + groupID.ToString() + "-" + RequestingAgentID.ToString())) + m_Cache.Remove("roles-" + groupID.ToString() + "-" + RequestingAgentID.ToString()); + + if (m_Cache.Contains("rolemembers-" + RequestingAgentID.ToString() + "-" + groupID.ToString())) + m_Cache.Remove("rolemembers-" + RequestingAgentID.ToString() + "-" + groupID.ToString()); + } + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID, GroupRolesListDelegate d) + { + object roles = null; + bool firstCall = false; + string cacheKey = "roles-" + GroupID.ToString(); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out roles)) + return (List)roles; + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + roles = d(); + if (roles != null) + { + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, roles, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (List)roles; + } + } + } + else + Thread.Sleep(50); + } + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, RoleMembersListDelegate d) + { + object rmembers = null; + bool firstCall = false; + // we need to key in also on the requester, because different ppl have different view privileges + string cacheKey = "rolemembers-" + RequestingAgentID.ToString() + "-" + GroupID.ToString(); + + //m_log.DebugFormat("[XXX]: GetGroupRoleMembers {0}", cacheKey); + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out rmembers)) + { + List xx = (List)rmembers; + return xx.ConvertAll(m_ForeignImporter.ConvertGroupRoleMembersData); + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + List _rmembers = d(); + + if (_rmembers != null && _rmembers.Count > 0) + rmembers = _rmembers.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupRoleMembersData)); + else + rmembers = new List(); + + lock (m_Cache) + { + // For some strange reason, when I cache the list of GroupRoleMembersData, + // it gets emptied out. The TryGet gets an empty list... + //m_Cache.AddOrUpdate(cacheKey, rmembers, GROUPS_CACHE_TIMEOUT); + // Caching the list of ExtendedGroupRoleMembersData doesn't show that issue + // I don't get it. + m_Cache.AddOrUpdate(cacheKey, _rmembers, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (List)rmembers; + } + } + else + Thread.Sleep(50); + } + } + + public void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, BooleanDelegate d) + { + if (d()) + { + lock (m_Cache) + { + // update the cached role + string cacheKey = "role-" + RoleID.ToString(); + object obj; + if (m_Cache.TryGetValue(cacheKey, out obj)) + { + GroupRolesData r = (GroupRolesData)obj; + r.Members++; + } + + // add this agent to the list of role members + cacheKey = "rolemembers-" + RequestingAgentID.ToString() + "-" + GroupID.ToString(); + if (m_Cache.TryGetValue(cacheKey, out obj)) + { + try + { + // This may throw an exception, in which case the agentID is not a UUID but a full ID + // In that case, let's just remove the whoe things from the cache + UUID id = new UUID(AgentID); + List xx = (List)obj; + List rmlist = xx.ConvertAll(m_ForeignImporter.ConvertGroupRoleMembersData); + GroupRoleMembersData rm = new GroupRoleMembersData(); + rm.MemberID = id; + rm.RoleID = RoleID; + rmlist.Add(rm); + } + catch + { + m_Cache.Remove(cacheKey); + } + } + + // Remove the cached info about this agent's roles + // because we don't have enough local info about the new role + cacheKey = "roles-" + GroupID.ToString() + "-" + AgentID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + } + } + } + + public void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, BooleanDelegate d) + { + if (d()) + { + lock (m_Cache) + { + // update the cached role + string cacheKey = "role-" + RoleID.ToString(); + object obj; + if (m_Cache.TryGetValue(cacheKey, out obj)) + { + GroupRolesData r = (GroupRolesData)obj; + r.Members--; + } + + cacheKey = "roles-" + GroupID.ToString() + "-" + AgentID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "rolemembers-" + RequestingAgentID.ToString() + "-" + GroupID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + } + } + } + + public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID, GroupRolesListDelegate d) + { + object roles = null; + bool firstCall = false; + string cacheKey = "roles-" + GroupID.ToString() + "-" + AgentID.ToString(); + + //m_log.DebugFormat("[XXX]: GetAgentGroupRoles {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out roles)) + { + //m_log.DebugFormat("[XXX]: GetAgentGroupRoles {0} cached!", cacheKey); + return (List)roles; + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + roles = d(); + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, roles, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (List)roles; + } + } + else + Thread.Sleep(50); + } + } + + public void SetAgentActiveGroupRole(string AgentID, UUID GroupID, VoidDelegate d) + { + d(); + + lock (m_Cache) + { + // Invalidate cached info, because it has ActiveRoleID and Powers + string cacheKey = "membership-" + AgentID.ToString() + "-" + GroupID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "memberships-" + AgentID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + } + } + + public void UpdateMembership(string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile, VoidDelegate d) + { + d(); + + lock (m_Cache) + { + string cacheKey = "membership-" + AgentID.ToString() + "-" + GroupID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "memberships-" + AgentID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "active-" + AgentID.ToString(); + object m = null; + if (m_Cache.TryGetValue(cacheKey, out m)) + { + GroupMembershipData membership = (GroupMembershipData)m; + membership.ListInProfile = ListInProfile; + membership.AcceptNotices = AcceptNotices; + } + } + } + + public bool AddGroupNotice(UUID groupID, UUID noticeID, GroupNoticeInfo notice, BooleanDelegate d) + { + if (d()) + { + lock (m_Cache) + { + m_Cache.AddOrUpdate("notice-" + noticeID.ToString(), notice, GROUPS_CACHE_TIMEOUT); + string cacheKey = "notices-" + groupID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + } + + return true; + } + + return false; + } + + public GroupNoticeInfo GetGroupNotice(UUID noticeID, NoticeDelegate d) + { + object notice = null; + bool firstCall = false; + string cacheKey = "notice-" + noticeID.ToString(); + + //m_log.DebugFormat("[XXX]: GetAgentGroupRoles {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out notice)) + { + return (GroupNoticeInfo)notice; + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + GroupNoticeInfo _notice = d(); + + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, _notice, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return _notice; + } + } + else + Thread.Sleep(50); + } + } + + public List GetGroupNotices(UUID GroupID, NoticeListDelegate d) + { + object notices = null; + bool firstCall = false; + string cacheKey = "notices-" + GroupID.ToString(); + + //m_log.DebugFormat("[XXX]: GetGroupNotices {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out notices)) + { + //m_log.DebugFormat("[XXX]: GetGroupNotices {0} cached!", cacheKey); + return (List)notices; + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + notices = d(); + + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, notices, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (List)notices; + } + } + else + Thread.Sleep(50); + } + } + + + } +} diff --git a/OpenSim/Addons/Groups/Service/GroupsService.cs b/OpenSim/Addons/Groups/Service/GroupsService.cs new file mode 100644 index 0000000000..fc567dd6c3 --- /dev/null +++ b/OpenSim/Addons/Groups/Service/GroupsService.cs @@ -0,0 +1,1014 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.Reflection; +using System.Timers; +using log4net; +using Nini.Config; + +using OpenMetaverse; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Groups +{ + public class GroupsService : GroupsServiceBase + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public const GroupPowers DefaultEveryonePowers = GroupPowers.AllowSetHome | + GroupPowers.Accountable | + GroupPowers.JoinChat | + GroupPowers.AllowVoiceChat | + GroupPowers.ReceiveNotices | + GroupPowers.StartProposal | + GroupPowers.VoteOnProposal; + + public const GroupPowers OwnerPowers = GroupPowers.Accountable | + GroupPowers.AllowEditLand | + GroupPowers.AllowFly | + GroupPowers.AllowLandmark | + GroupPowers.AllowRez | + GroupPowers.AllowSetHome | + GroupPowers.AllowVoiceChat | + GroupPowers.AssignMember | + GroupPowers.AssignMemberLimited | + GroupPowers.ChangeActions | + GroupPowers.ChangeIdentity | + GroupPowers.ChangeMedia | + GroupPowers.ChangeOptions | + GroupPowers.CreateRole | + GroupPowers.DeedObject | + GroupPowers.DeleteRole | + GroupPowers.Eject | + GroupPowers.FindPlaces | + GroupPowers.Invite | + GroupPowers.JoinChat | + GroupPowers.LandChangeIdentity | + GroupPowers.LandDeed | + GroupPowers.LandDivideJoin | + GroupPowers.LandEdit | + GroupPowers.LandEjectAndFreeze | + GroupPowers.LandGardening | + GroupPowers.LandManageAllowed | + GroupPowers.LandManageBanned | + GroupPowers.LandManagePasses | + GroupPowers.LandOptions | + GroupPowers.LandRelease | + GroupPowers.LandSetSale | + GroupPowers.ModerateChat | + GroupPowers.ObjectManipulate | + GroupPowers.ObjectSetForSale | + GroupPowers.ReceiveNotices | + GroupPowers.RemoveMember | + GroupPowers.ReturnGroupOwned | + GroupPowers.ReturnGroupSet | + GroupPowers.ReturnNonGroup | + GroupPowers.RoleProperties | + GroupPowers.SendNotices | + GroupPowers.SetLandingPoint | + GroupPowers.StartProposal | + GroupPowers.VoteOnProposal; + + #region Daily Cleanup + + private Timer m_CleanupTimer; + + public GroupsService(IConfigSource config, string configName) + : base(config, configName) + { + } + + public GroupsService(IConfigSource config) + : this(config, string.Empty) + { + // Once a day + m_CleanupTimer = new Timer(24 * 60 * 60 * 1000); + m_CleanupTimer.AutoReset = true; + m_CleanupTimer.Elapsed += new ElapsedEventHandler(m_CleanupTimer_Elapsed); + m_CleanupTimer.Enabled = true; + m_CleanupTimer.Start(); + } + + private void m_CleanupTimer_Elapsed(object sender, ElapsedEventArgs e) + { + m_Database.DeleteOldNotices(); + m_Database.DeleteOldInvites(); + } + + #endregion + + public UUID CreateGroup(string RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, + bool allowPublish, bool maturePublish, UUID founderID, out string reason) + { + reason = string.Empty; + + // Create the group + GroupData data = new GroupData(); + data.GroupID = UUID.Random(); + data.Data = new Dictionary(); + data.Data["Name"] = name; + data.Data["Charter"] = charter; + data.Data["InsigniaID"] = insigniaID.ToString(); + data.Data["FounderID"] = founderID.ToString(); + data.Data["MembershipFee"] = membershipFee.ToString(); + data.Data["OpenEnrollment"] = openEnrollment ? "1" : "0"; + data.Data["ShowInList"] = showInList ? "1" : "0"; + data.Data["AllowPublish"] = allowPublish ? "1" : "0"; + data.Data["MaturePublish"] = maturePublish ? "1" : "0"; + data.Data["OwnerRoleID"] = UUID.Random().ToString(); + + if (!m_Database.StoreGroup(data)) + return UUID.Zero; + + // Create Everyone role + _AddOrUpdateGroupRole(RequestingAgentID, data.GroupID, UUID.Zero, "Everyone", "Everyone in the group", "Member of " + name, (ulong)DefaultEveryonePowers, true); + + // Create Owner role + UUID roleID = UUID.Random(); + _AddOrUpdateGroupRole(RequestingAgentID, data.GroupID, roleID, "Owners", "Owners of the group", "Owner of " + name, (ulong)OwnerPowers, true); + + // Add founder to group + _AddAgentToGroup(RequestingAgentID, founderID.ToString(), data.GroupID, roleID); + + return data.GroupID; + } + + public void UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) + { + GroupData data = m_Database.RetrieveGroup(groupID); + if (data == null) + return; + + // Check perms + if (!HasPower(RequestingAgentID, groupID, GroupPowers.ChangeActions)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at updating group {1} denied because of lack of permission", RequestingAgentID, groupID); + return; + } + + data.GroupID = groupID; + data.Data["Charter"] = charter; + data.Data["ShowInList"] = showInList ? "1" : "0"; + data.Data["InsigniaID"] = insigniaID.ToString(); + data.Data["MembershipFee"] = membershipFee.ToString(); + data.Data["OpenEnrollment"] = openEnrollment ? "1" : "0"; + data.Data["AllowPublish"] = allowPublish ? "1" : "0"; + data.Data["MaturePublish"] = maturePublish ? "1" : "0"; + + m_Database.StoreGroup(data); + + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID) + { + GroupData data = m_Database.RetrieveGroup(GroupID); + + return _GroupDataToRecord(data); + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, string GroupName) + { + GroupData data = m_Database.RetrieveGroup(GroupName); + + return _GroupDataToRecord(data); + } + + public List FindGroups(string RequestingAgentID, string search) + { + List groups = new List(); + + GroupData[] data = m_Database.RetrieveGroups(search); + + if (data != null && data.Length > 0) + { + foreach (GroupData d in data) + { + // Don't list group proxies + if (d.Data.ContainsKey("Location") && d.Data["Location"] != string.Empty) + continue; + + DirGroupsReplyData g = new DirGroupsReplyData(); + g.groupID = d.GroupID; + + if (d.Data.ContainsKey("Name")) + g.groupName = d.Data["Name"]; + else + m_log.DebugFormat("[Groups]: Key Name not found"); + + g.members = m_Database.MemberCount(d.GroupID); + + groups.Add(g); + } + } + + return groups; + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID) + { + List members = new List(); + + GroupData group = m_Database.RetrieveGroup(GroupID); + if (group == null) + return members; + + UUID ownerRoleID = new UUID(group.Data["OwnerRoleID"]); + + RoleData[] roles = m_Database.RetrieveRoles(GroupID); + if (roles == null) + // something wrong with this group + return members; + List rolesList = new List(roles); + + // Is the requester a member of the group? + bool isInGroup = false; + if (m_Database.RetrieveMember(GroupID, RequestingAgentID) != null) + isInGroup = true; + + if (!isInGroup) // reduce the roles to the visible ones + rolesList = rolesList.FindAll(r => (UInt64.Parse(r.Data["Powers"]) & (ulong)GroupPowers.MemberVisible) != 0); + + MembershipData[] datas = m_Database.RetrieveMembers(GroupID); + if (datas == null || (datas != null && datas.Length == 0)) + return members; + + // OK, we have everything we need + + foreach (MembershipData d in datas) + { + RoleMembershipData[] rolememberships = m_Database.RetrieveMemberRoles(GroupID, d.PrincipalID); + List rolemembershipsList = new List(rolememberships); + + ExtendedGroupMembersData m = new ExtendedGroupMembersData(); + + // What's this person's current role in the group? + UUID selectedRole = new UUID(d.Data["SelectedRoleID"]); + RoleData selected = rolesList.Find(r => r.RoleID == selectedRole); + + if (selected != null) + { + m.Title = selected.Data["Title"]; + m.AgentPowers = UInt64.Parse(selected.Data["Powers"]); + + m.AgentID = d.PrincipalID; + m.AcceptNotices = d.Data["AcceptNotices"] == "1" ? true : false; + m.Contribution = Int32.Parse(d.Data["Contribution"]); + m.ListInProfile = d.Data["ListInProfile"] == "1" ? true : false; + + // Is this person an owner of the group? + m.IsOwner = (rolemembershipsList.Find(r => r.RoleID == ownerRoleID) != null) ? true : false; + + members.Add(m); + } + } + + return members; + } + + public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason) + { + reason = string.Empty; + // check that the requesting agent has permissions to add role + if (!HasPower(RequestingAgentID, groupID, GroupPowers.CreateRole)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at creating role in group {1} denied because of lack of permission", RequestingAgentID, groupID); + reason = "Insufficient permission to create role"; + return false; + } + + return _AddOrUpdateGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers, true); + + } + + public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers) + { + // check perms + if (!HasPower(RequestingAgentID, groupID, GroupPowers.ChangeActions)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at changing role in group {1} denied because of lack of permission", RequestingAgentID, groupID); + return false; + } + + return _AddOrUpdateGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers, false); + } + + public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID) + { + // check perms + if (!HasPower(RequestingAgentID, groupID, GroupPowers.DeleteRole)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at deleting role from group {1} denied because of lack of permission", RequestingAgentID, groupID); + return; + } + + // Can't delete Everyone and Owners roles + if (roleID == UUID.Zero) + { + m_log.DebugFormat("[Groups]: Attempt at deleting Everyone role from group {0} denied", groupID); + return; + } + + GroupData group = m_Database.RetrieveGroup(groupID); + if (group == null) + { + m_log.DebugFormat("[Groups]: Attempt at deleting role from non-existing group {0}", groupID); + return; + } + + if (roleID == new UUID(group.Data["OwnerRoleID"])) + { + m_log.DebugFormat("[Groups]: Attempt at deleting Owners role from group {0} denied", groupID); + return; + } + + _RemoveGroupRole(groupID, roleID); + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID) + { + // TODO: check perms + return _GetGroupRoles(GroupID); + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID) + { + // TODO: check perms + + // Is the requester a member of the group? + bool isInGroup = false; + if (m_Database.RetrieveMember(GroupID, RequestingAgentID) != null) + isInGroup = true; + + return _GetGroupRoleMembers(GroupID, isInGroup); + } + + public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason) + { + reason = string.Empty; + + _AddAgentToGroup(RequestingAgentID, AgentID, GroupID, RoleID, token); + + return true; + } + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + // check perms + if (RequestingAgentID != AgentID && !HasPower(RequestingAgentID, GroupID, GroupPowers.Eject)) + return; + + _RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID); + } + + public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID) + { + // Check whether the invitee is already a member of the group + MembershipData m = m_Database.RetrieveMember(groupID, agentID); + if (m != null) + return false; + + // Check permission to invite + if (!HasPower(RequestingAgentID, groupID, GroupPowers.Invite)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at inviting to group {1} denied because of lack of permission", RequestingAgentID, groupID); + return false; + } + + // Check whether there are pending invitations and delete them + InvitationData invite = m_Database.RetrieveInvitation(groupID, agentID); + if (invite != null) + m_Database.DeleteInvite(invite.InviteID); + + invite = new InvitationData(); + invite.InviteID = inviteID; + invite.PrincipalID = agentID; + invite.GroupID = groupID; + invite.RoleID = roleID; + invite.Data = new Dictionary(); + + return m_Database.StoreInvitation(invite); + } + + public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + InvitationData data = m_Database.RetrieveInvitation(inviteID); + + if (data == null) + return null; + + GroupInviteInfo inviteInfo = new GroupInviteInfo(); + inviteInfo.AgentID = data.PrincipalID; + inviteInfo.GroupID = data.GroupID; + inviteInfo.InviteID = data.InviteID; + inviteInfo.RoleID = data.RoleID; + + return inviteInfo; + } + + public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + m_Database.DeleteInvite(inviteID); + } + + public bool AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + //if (!m_Database.CheckOwnerRole(RequestingAgentID, GroupID, RoleID)) + // return; + + // check permissions + bool limited = HasPower(RequestingAgentID, GroupID, GroupPowers.AssignMemberLimited); + bool unlimited = HasPower(RequestingAgentID, GroupID, GroupPowers.AssignMember) | IsOwner(RequestingAgentID, GroupID); + if (!limited || !unlimited) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at assigning {1} to role {2} denied because of lack of permission", RequestingAgentID, AgentID, RoleID); + return false; + } + + // AssignMemberLimited means that the person can assign another person to the same roles that she has in the group + if (!unlimited && limited) + { + // check whether person's has this role + RoleMembershipData rolemembership = m_Database.RetrieveRoleMember(GroupID, RoleID, AgentID); + if (rolemembership == null) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at assigning {1} to role {2} denied because of limited permission", RequestingAgentID, AgentID, RoleID); + return false; + } + } + + _AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + + return true; + } + + public bool RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + // Don't remove from Everyone role! + if (RoleID == UUID.Zero) + return false; + + // check permissions + bool unlimited = HasPower(RequestingAgentID, GroupID, GroupPowers.AssignMember) || IsOwner(RequestingAgentID, GroupID); + if (!unlimited) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at removing {1} from role {2} denied because of lack of permission", RequestingAgentID, AgentID, RoleID); + return false; + } + + RoleMembershipData rolemember = m_Database.RetrieveRoleMember(GroupID, RoleID, AgentID); + + if (rolemember == null) + return false; + + m_Database.DeleteRoleMember(rolemember); + + // Find another role for this person + UUID newRoleID = UUID.Zero; // Everyone + RoleMembershipData[] rdata = m_Database.RetrieveMemberRoles(GroupID, AgentID); + if (rdata != null) + foreach (RoleMembershipData r in rdata) + { + if (r.RoleID != UUID.Zero) + { + newRoleID = r.RoleID; + break; + } + } + + MembershipData member = m_Database.RetrieveMember(GroupID, AgentID); + if (member != null) + { + member.Data["SelectedRoleID"] = newRoleID.ToString(); + m_Database.StoreMember(member); + } + + return true; + } + + public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID) + { + List roles = new List(); + // TODO: check permissions + + RoleMembershipData[] data = m_Database.RetrieveMemberRoles(GroupID, AgentID); + if (data == null || (data != null && data.Length ==0)) + return roles; + + foreach (RoleMembershipData d in data) + { + RoleData rdata = m_Database.RetrieveRole(GroupID, d.RoleID); + if (rdata == null) // hippos + continue; + + GroupRolesData r = new GroupRolesData(); + r.Name = rdata.Data["Name"]; + r.Powers = UInt64.Parse(rdata.Data["Powers"]); + r.RoleID = rdata.RoleID; + r.Title = rdata.Data["Title"]; + + roles.Add(r); + } + + return roles; + } + + public ExtendedGroupMembershipData SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + // TODO: check perms + PrincipalData principal = new PrincipalData(); + principal.PrincipalID = AgentID; + principal.ActiveGroupID = GroupID; + m_Database.StorePrincipal(principal); + + return GetAgentGroupMembership(RequestingAgentID, AgentID, GroupID); + } + + public ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID) + { + // 1. get the principal data for the active group + PrincipalData principal = m_Database.RetrievePrincipal(AgentID); + if (principal == null) + return null; + + return GetAgentGroupMembership(RequestingAgentID, AgentID, principal.ActiveGroupID); + } + + public ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID) + { + return GetAgentGroupMembership(RequestingAgentID, AgentID, GroupID, null); + } + + private ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID, MembershipData membership) + { + // 2. get the active group + GroupData group = m_Database.RetrieveGroup(GroupID); + if (group == null) + return null; + + // 3. get the membership info if we don't have it already + if (membership == null) + { + membership = m_Database.RetrieveMember(group.GroupID, AgentID); + if (membership == null) + return null; + } + + // 4. get the active role + UUID activeRoleID = new UUID(membership.Data["SelectedRoleID"]); + RoleData role = m_Database.RetrieveRole(group.GroupID, activeRoleID); + + ExtendedGroupMembershipData data = new ExtendedGroupMembershipData(); + data.AcceptNotices = membership.Data["AcceptNotices"] == "1" ? true : false; + data.AccessToken = membership.Data["AccessToken"]; + data.Active = true; + data.ActiveRole = activeRoleID; + data.AllowPublish = group.Data["AllowPublish"] == "1" ? true : false; + data.Charter = group.Data["Charter"]; + data.Contribution = Int32.Parse(membership.Data["Contribution"]); + data.FounderID = new UUID(group.Data["FounderID"]); + data.GroupID = new UUID(group.GroupID); + data.GroupName = group.Data["Name"]; + data.GroupPicture = new UUID(group.Data["InsigniaID"]); + if (role != null) + { + data.GroupPowers = UInt64.Parse(role.Data["Powers"]); + data.GroupTitle = role.Data["Title"]; + } + data.ListInProfile = membership.Data["ListInProfile"] == "1" ? true : false; + data.MaturePublish = group.Data["MaturePublish"] == "1" ? true : false; + data.MembershipFee = Int32.Parse(group.Data["MembershipFee"]); + data.OpenEnrollment = group.Data["OpenEnrollment"] == "1" ? true : false; + data.ShowInList = group.Data["ShowInList"] == "1" ? true : false; + + return data; + } + + public List GetAgentGroupMemberships(string RequestingAgentID, string AgentID) + { + List memberships = new List(); + + // 1. Get all the groups that this person is a member of + MembershipData[] mdata = m_Database.RetrieveMemberships(AgentID); + + if (mdata == null || (mdata != null && mdata.Length == 0)) + return memberships; + + foreach (MembershipData d in mdata) + { + GroupMembershipData gmember = GetAgentGroupMembership(RequestingAgentID, AgentID, d.GroupID, d); + if (gmember != null) + { + memberships.Add(gmember); + //m_log.DebugFormat("[XXX]: Member of {0} as {1}", gmember.GroupName, gmember.GroupTitle); + //Util.PrintCallStack(); + } + } + + return memberships; + } + + public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + MembershipData data = m_Database.RetrieveMember(GroupID, AgentID); + if (data == null) + return; + + data.Data["SelectedRoleID"] = RoleID.ToString(); + m_Database.StoreMember(data); + } + + public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile) + { + // TODO: check perms + + MembershipData membership = m_Database.RetrieveMember(GroupID, AgentID); + if (membership == null) + return; + + membership.Data["AcceptNotices"] = AcceptNotices ? "1" : "0"; + membership.Data["ListInProfile"] = ListInProfile ? "1" : "0"; + + m_Database.StoreMember(membership); + } + + public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + // Check perms + if (!HasPower(RequestingAgentID, groupID, GroupPowers.SendNotices)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at sending notice to group {1} denied because of lack of permission", RequestingAgentID, groupID); + return false; + } + + return _AddNotice(groupID, noticeID, fromName, subject, message, hasAttachment, attType, attName, attItemID, attOwnerID); + } + + public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID) + { + NoticeData data = m_Database.RetrieveNotice(noticeID); + + if (data == null) + return null; + + return _NoticeDataToInfo(data); + } + + public List GetGroupNotices(string RequestingAgentID, UUID groupID) + { + NoticeData[] data = m_Database.RetrieveNotices(groupID); + List infos = new List(); + + if (data == null || (data != null && data.Length == 0)) + return infos; + + foreach (NoticeData d in data) + { + ExtendedGroupNoticeData info = _NoticeDataToData(d); + infos.Add(info); + } + + return infos; + } + + public void ResetAgentGroupChatSessions(string agentID) + { + } + + public bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public void AgentDroppedFromGroupChatSession(string agentID, UUID groupID) + { + } + + public void AgentInvitedToGroupChatSession(string agentID, UUID groupID) + { + } + + #region Actions without permission checks + + private void _AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + _AddAgentToGroup(RequestingAgentID, AgentID, GroupID, RoleID, string.Empty); + } + + public void _RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + // 1. Delete membership + m_Database.DeleteMember(GroupID, AgentID); + + // 2. Remove from rolememberships + m_Database.DeleteMemberAllRoles(GroupID, AgentID); + + // 3. if it was active group, inactivate it + PrincipalData principal = m_Database.RetrievePrincipal(AgentID); + if (principal != null && principal.ActiveGroupID == GroupID) + { + principal.ActiveGroupID = UUID.Zero; + m_Database.StorePrincipal(principal); + } + } + + protected void _AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string accessToken) + { + // Check if it's already there + MembershipData data = m_Database.RetrieveMember(GroupID, AgentID); + if (data != null) + return; + + // Add the membership + data = new MembershipData(); + data.PrincipalID = AgentID; + data.GroupID = GroupID; + data.Data = new Dictionary(); + data.Data["SelectedRoleID"] = RoleID.ToString(); + data.Data["Contribution"] = "0"; + data.Data["ListInProfile"] = "1"; + data.Data["AcceptNotices"] = "1"; + data.Data["AccessToken"] = accessToken; + + m_Database.StoreMember(data); + + // Add principal to everyone role + _AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, UUID.Zero); + + // Add principal to role, if different from everyone role + if (RoleID != UUID.Zero) + _AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + + // Make thit this active group + PrincipalData pdata = new PrincipalData(); + pdata.PrincipalID = AgentID; + pdata.ActiveGroupID = GroupID; + m_Database.StorePrincipal(pdata); + + } + + private bool _AddOrUpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, bool add) + { + RoleData data = m_Database.RetrieveRole(groupID, roleID); + + if (add && data != null) // it already exists, can't create + return false; + + if (!add && data == null) // it deosn't exist, can't update + return false; + + if (add) + data = new RoleData(); + + data.GroupID = groupID; + data.RoleID = roleID; + data.Data = new Dictionary(); + data.Data["Name"] = name; + data.Data["Description"] = description; + data.Data["Title"] = title; + data.Data["Powers"] = powers.ToString(); + + return m_Database.StoreRole(data); + } + + private void _RemoveGroupRole(UUID groupID, UUID roleID) + { + m_Database.DeleteRole(groupID, roleID); + } + + private void _AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + RoleMembershipData data = m_Database.RetrieveRoleMember(GroupID, RoleID, AgentID); + if (data != null) + return; + + data = new RoleMembershipData(); + data.GroupID = GroupID; + data.PrincipalID = AgentID; + data.RoleID = RoleID; + m_Database.StoreRoleMember(data); + + // Make it the SelectedRoleID + MembershipData membership = m_Database.RetrieveMember(GroupID, AgentID); + if (membership == null) + { + m_log.DebugFormat("[Groups]: ({0}) No such member {0} in group {1}", AgentID, GroupID); + return; + } + + membership.Data["SelectedRoleID"] = RoleID.ToString(); + m_Database.StoreMember(membership); + + } + + private List _GetGroupRoles(UUID groupID) + { + List roles = new List(); + + RoleData[] data = m_Database.RetrieveRoles(groupID); + + if (data == null || (data != null && data.Length == 0)) + return roles; + + foreach (RoleData d in data) + { + GroupRolesData r = new GroupRolesData(); + r.Description = d.Data["Description"]; + r.Members = m_Database.RoleMemberCount(groupID, d.RoleID); + r.Name = d.Data["Name"]; + r.Powers = UInt64.Parse(d.Data["Powers"]); + r.RoleID = d.RoleID; + r.Title = d.Data["Title"]; + + roles.Add(r); + } + + return roles; + } + + private List _GetGroupRoleMembers(UUID GroupID, bool isInGroup) + { + List rmembers = new List(); + + RoleData[] rdata = new RoleData[0]; + if (!isInGroup) + { + rdata = m_Database.RetrieveRoles(GroupID); + if (rdata == null || (rdata != null && rdata.Length == 0)) + return rmembers; + } + List rlist = new List(rdata); + if (!isInGroup) + rlist = rlist.FindAll(r => (UInt64.Parse(r.Data["Powers"]) & (ulong)GroupPowers.MemberVisible) != 0); + + RoleMembershipData[] data = m_Database.RetrieveRolesMembers(GroupID); + + if (data == null || (data != null && data.Length == 0)) + return rmembers; + + foreach (RoleMembershipData d in data) + { + if (!isInGroup) + { + RoleData rd = rlist.Find(_r => _r.RoleID == d.RoleID); // visible role + if (rd == null) + continue; + } + + ExtendedGroupRoleMembersData r = new ExtendedGroupRoleMembersData(); + r.MemberID = d.PrincipalID; + r.RoleID = d.RoleID; + + rmembers.Add(r); + } + + return rmembers; + } + + protected bool _AddNotice(UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + NoticeData data = new NoticeData(); + data.GroupID = groupID; + data.NoticeID = noticeID; + data.Data = new Dictionary(); + data.Data["FromName"] = fromName; + data.Data["Subject"] = subject; + data.Data["Message"] = message; + data.Data["HasAttachment"] = hasAttachment ? "1" : "0"; + if (hasAttachment) + { + data.Data["AttachmentType"] = attType.ToString(); + data.Data["AttachmentName"] = attName; + data.Data["AttachmentItemID"] = attItemID.ToString(); + data.Data["AttachmentOwnerID"] = attOwnerID; + } + data.Data["TMStamp"] = ((uint)Util.UnixTimeSinceEpoch()).ToString(); + + return m_Database.StoreNotice(data); + } + + #endregion + + #region structure translations + ExtendedGroupRecord _GroupDataToRecord(GroupData data) + { + if (data == null) + return null; + + ExtendedGroupRecord rec = new ExtendedGroupRecord(); + rec.AllowPublish = data.Data["AllowPublish"] == "1" ? true : false; + rec.Charter = data.Data["Charter"]; + rec.FounderID = new UUID(data.Data["FounderID"]); + rec.GroupID = data.GroupID; + rec.GroupName = data.Data["Name"]; + rec.GroupPicture = new UUID(data.Data["InsigniaID"]); + rec.MaturePublish = data.Data["MaturePublish"] == "1" ? true : false; + rec.MembershipFee = Int32.Parse(data.Data["MembershipFee"]); + rec.OpenEnrollment = data.Data["OpenEnrollment"] == "1" ? true : false; + rec.OwnerRoleID = new UUID(data.Data["OwnerRoleID"]); + rec.ShowInList = data.Data["ShowInList"] == "1" ? true : false; + rec.ServiceLocation = data.Data["Location"]; + rec.MemberCount = m_Database.MemberCount(data.GroupID); + rec.RoleCount = m_Database.RoleCount(data.GroupID); + + return rec; + } + + GroupNoticeInfo _NoticeDataToInfo(NoticeData data) + { + GroupNoticeInfo notice = new GroupNoticeInfo(); + notice.GroupID = data.GroupID; + notice.Message = data.Data["Message"]; + notice.noticeData = _NoticeDataToData(data); + + return notice; + } + + ExtendedGroupNoticeData _NoticeDataToData(NoticeData data) + { + ExtendedGroupNoticeData notice = new ExtendedGroupNoticeData(); + notice.FromName = data.Data["FromName"]; + notice.NoticeID = data.NoticeID; + notice.Subject = data.Data["Subject"]; + notice.Timestamp = uint.Parse((string)data.Data["TMStamp"]); + notice.HasAttachment = data.Data["HasAttachment"] == "1" ? true : false; + if (notice.HasAttachment) + { + notice.AttachmentName = data.Data["AttachmentName"]; + notice.AttachmentItemID = new UUID(data.Data["AttachmentItemID"].ToString()); + notice.AttachmentType = byte.Parse(data.Data["AttachmentType"].ToString()); + notice.AttachmentOwnerID = data.Data["AttachmentOwnerID"].ToString(); + } + + + return notice; + } + + #endregion + + #region permissions + private bool HasPower(string agentID, UUID groupID, GroupPowers power) + { + RoleMembershipData[] rmembership = m_Database.RetrieveMemberRoles(groupID, agentID); + if (rmembership == null || (rmembership != null && rmembership.Length == 0)) + return false; + + foreach (RoleMembershipData rdata in rmembership) + { + RoleData role = m_Database.RetrieveRole(groupID, rdata.RoleID); + if ( (UInt64.Parse(role.Data["Powers"]) & (ulong)power) != 0 ) + return true; + } + return false; + } + + private bool IsOwner(string agentID, UUID groupID) + { + GroupData group = m_Database.RetrieveGroup(groupID); + if (group == null) + return false; + + RoleMembershipData rmembership = m_Database.RetrieveRoleMember(groupID, new UUID(group.Data["OwnerRoleID"]), agentID); + if (rmembership == null) + return false; + + return true; + } + #endregion + + } +} diff --git a/OpenSim/Addons/Groups/Service/GroupsServiceBase.cs b/OpenSim/Addons/Groups/Service/GroupsServiceBase.cs new file mode 100644 index 0000000000..2611a3d922 --- /dev/null +++ b/OpenSim/Addons/Groups/Service/GroupsServiceBase.cs @@ -0,0 +1,84 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Data; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Base; + +namespace OpenSim.Groups +{ + public class GroupsServiceBase : ServiceBase + { + protected IGroupsData m_Database = null; + + public GroupsServiceBase(IConfigSource config, string cName) + : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + string realm = "os_groups"; + string configName = (cName == string.Empty) ? "Groups" : cName; + + // + // Try reading the [DatabaseService] section, if it exists + // + IConfig dbConfig = config.Configs["DatabaseService"]; + if (dbConfig != null) + { + if (dllName == String.Empty) + dllName = dbConfig.GetString("StorageProvider", String.Empty); + if (connString == String.Empty) + connString = dbConfig.GetString("ConnectionString", String.Empty); + } + + // + // [Groups] section overrides [DatabaseService], if it exists + // + IConfig groupsConfig = config.Configs[configName]; + if (groupsConfig != null) + { + dllName = groupsConfig.GetString("StorageProvider", dllName); + connString = groupsConfig.GetString("ConnectionString", connString); + realm = groupsConfig.GetString("Realm", realm); + } + + // + // We tried, but this doesn't exist. We can't proceed. + // + if (dllName.Equals(String.Empty)) + throw new Exception("No StorageProvider configured"); + + m_Database = LoadPlugin(dllName, new Object[] { connString, realm }); + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module " + dllName); + } + } +} \ No newline at end of file diff --git a/OpenSim/Addons/Groups/Service/HGGroupsService.cs b/OpenSim/Addons/Groups/Service/HGGroupsService.cs new file mode 100644 index 0000000000..9d7961cd20 --- /dev/null +++ b/OpenSim/Addons/Groups/Service/HGGroupsService.cs @@ -0,0 +1,353 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.Reflection; +using System.Timers; +using log4net; +using Nini.Config; + +using OpenMetaverse; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Groups +{ + public class HGGroupsService : GroupsService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IOfflineIMService m_OfflineIM; + private IUserAccountService m_UserAccounts; + private string m_HomeURI; + + public HGGroupsService(IConfigSource config, IOfflineIMService im, IUserAccountService users, string homeURI) + : base(config, string.Empty) + { + m_OfflineIM = im; + m_UserAccounts = users; + m_HomeURI = homeURI; + if (!m_HomeURI.EndsWith("/")) + m_HomeURI += "/"; + } + + + #region HG specific operations + + public bool CreateGroupProxy(string RequestingAgentID, string agentID, string accessToken, UUID groupID, string serviceLocation, string name, out string reason) + { + reason = string.Empty; + Uri uri = null; + try + { + uri = new Uri(serviceLocation); + } + catch (UriFormatException) + { + reason = "Bad location for group proxy"; + return false; + } + + // Check if it already exists + GroupData grec = m_Database.RetrieveGroup(groupID); + if (grec == null || + (grec != null && grec.Data["Location"] != string.Empty && grec.Data["Location"].ToLower() != serviceLocation.ToLower())) + { + // Create the group + grec = new GroupData(); + grec.GroupID = groupID; + grec.Data = new Dictionary(); + grec.Data["Name"] = name + " @ " + uri.Authority; + grec.Data["Location"] = serviceLocation; + grec.Data["Charter"] = string.Empty; + grec.Data["InsigniaID"] = UUID.Zero.ToString(); + grec.Data["FounderID"] = UUID.Zero.ToString(); + grec.Data["MembershipFee"] = "0"; + grec.Data["OpenEnrollment"] = "0"; + grec.Data["ShowInList"] = "0"; + grec.Data["AllowPublish"] = "0"; + grec.Data["MaturePublish"] = "0"; + grec.Data["OwnerRoleID"] = UUID.Zero.ToString(); + + + if (!m_Database.StoreGroup(grec)) + return false; + } + + if (grec.Data["Location"] == string.Empty) + { + reason = "Cannot add proxy membership to non-proxy group"; + return false; + } + + UUID uid = UUID.Zero; + string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty; + Util.ParseUniversalUserIdentifier(RequestingAgentID, out uid, out url, out first, out last, out tmp); + string fromName = first + "." + last + "@" + url; + + // Invite to group again + InviteToGroup(fromName, groupID, new UUID(agentID), grec.Data["Name"]); + + // Stick the proxy membership in the DB already + // we'll delete it if the agent declines the invitation + MembershipData membership = new MembershipData(); + membership.PrincipalID = agentID; + membership.GroupID = groupID; + membership.Data = new Dictionary(); + membership.Data["SelectedRoleID"] = UUID.Zero.ToString(); + membership.Data["Contribution"] = "0"; + membership.Data["ListInProfile"] = "1"; + membership.Data["AcceptNotices"] = "1"; + membership.Data["AccessToken"] = accessToken; + + m_Database.StoreMember(membership); + + return true; + } + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID, string token) + { + // check the token + MembershipData membership = m_Database.RetrieveMember(GroupID, AgentID); + if (membership != null) + { + if (token != string.Empty && token.Equals(membership.Data["AccessToken"])) + RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID); + else + m_log.DebugFormat("[Groups.HGGroupsService]: access token {0} did not match stored one {1}", token, membership.Data["AccessToken"]); + } + else + m_log.DebugFormat("[Groups.HGGroupsService]: membership not found for {0}", AgentID); + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string groupName, string token) + { + // check the token + if (!VerifyToken(GroupID, RequestingAgentID, token)) + return null; + + ExtendedGroupRecord grec; + if (GroupID == UUID.Zero) + grec = GetGroupRecord(RequestingAgentID, groupName); + else + grec = GetGroupRecord(RequestingAgentID, GroupID); + + if (grec != null) + FillFounderUUI(grec); + + return grec; + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID, string token) + { + if (!VerifyToken(GroupID, RequestingAgentID, token)) + return new List(); + + List members = GetGroupMembers(RequestingAgentID, GroupID); + + // convert UUIDs to UUIs + members.ForEach(delegate (ExtendedGroupMembersData m) + { + if (m.AgentID.ToString().Length == 36) // UUID + { + UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, new UUID(m.AgentID)); + if (account != null) + m.AgentID = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI); + } + }); + + return members; + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID, string token) + { + if (!VerifyToken(GroupID, RequestingAgentID, token)) + return new List(); + + return GetGroupRoles(RequestingAgentID, GroupID); + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, string token) + { + if (!VerifyToken(GroupID, RequestingAgentID, token)) + return new List(); + + List rolemembers = GetGroupRoleMembers(RequestingAgentID, GroupID); + + // convert UUIDs to UUIs + rolemembers.ForEach(delegate(ExtendedGroupRoleMembersData m) + { + if (m.MemberID.ToString().Length == 36) // UUID + { + UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, new UUID(m.MemberID)); + if (account != null) + m.MemberID = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI); + } + }); + + return rolemembers; + } + + public bool AddNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + // check that the group proxy exists + ExtendedGroupRecord grec = GetGroupRecord(RequestingAgentID, groupID); + if (grec == null) + { + m_log.DebugFormat("[Groups.HGGroupsService]: attempt at adding notice to non-existent group proxy"); + return false; + } + + // check that the group is remote + if (grec.ServiceLocation == string.Empty) + { + m_log.DebugFormat("[Groups.HGGroupsService]: attempt at adding notice to local (non-proxy) group"); + return false; + } + + // check that there isn't already a notice with the same ID + if (GetGroupNotice(RequestingAgentID, noticeID) != null) + { + m_log.DebugFormat("[Groups.HGGroupsService]: a notice with the same ID already exists", grec.ServiceLocation); + return false; + } + + // This has good intentions (security) but it will potentially DDS the origin... + // We'll need to send a proof along with the message. Maybe encrypt the message + // using key pairs + // + //// check that the notice actually exists in the origin + //GroupsServiceHGConnector c = new GroupsServiceHGConnector(grec.ServiceLocation); + //if (!c.VerifyNotice(noticeID, groupID)) + //{ + // m_log.DebugFormat("[Groups.HGGroupsService]: notice does not exist at origin {0}", grec.ServiceLocation); + // return false; + //} + + // ok, we're good! + return _AddNotice(groupID, noticeID, fromName, subject, message, hasAttachment, attType, attName, attItemID, attOwnerID); + } + + public bool VerifyNotice(UUID noticeID, UUID groupID) + { + GroupNoticeInfo notice = GetGroupNotice(string.Empty, noticeID); + + if (notice == null) + return false; + + if (notice.GroupID != groupID) + return false; + + return true; + } + + #endregion + + private void InviteToGroup(string fromName, UUID groupID, UUID invitedAgentID, string groupName) + { + // Todo: Security check, probably also want to send some kind of notification + UUID InviteID = UUID.Random(); + + if (AddAgentToGroupInvite(InviteID, groupID, invitedAgentID.ToString())) + { + Guid inviteUUID = InviteID.Guid; + + GridInstantMessage msg = new GridInstantMessage(); + + msg.imSessionID = inviteUUID; + + // msg.fromAgentID = agentID.Guid; + msg.fromAgentID = groupID.Guid; + msg.toAgentID = invitedAgentID.Guid; + //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.timestamp = 0; + msg.fromAgentName = fromName; + msg.message = string.Format("Please confirm your acceptance to join group {0}.", groupName); + msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation; + msg.fromGroup = true; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = UUID.Zero.Guid; + msg.binaryBucket = new byte[20]; + + string reason = string.Empty; + m_OfflineIM.StoreMessage(msg, out reason); + + } + } + + private bool AddAgentToGroupInvite(UUID inviteID, UUID groupID, string agentID) + { + // Check whether the invitee is already a member of the group + MembershipData m = m_Database.RetrieveMember(groupID, agentID); + if (m != null) + return false; + + // Check whether there are pending invitations and delete them + InvitationData invite = m_Database.RetrieveInvitation(groupID, agentID); + if (invite != null) + m_Database.DeleteInvite(invite.InviteID); + + invite = new InvitationData(); + invite.InviteID = inviteID; + invite.PrincipalID = agentID; + invite.GroupID = groupID; + invite.RoleID = UUID.Zero; + invite.Data = new Dictionary(); + + return m_Database.StoreInvitation(invite); + } + + private void FillFounderUUI(ExtendedGroupRecord grec) + { + UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, grec.FounderID); + if (account != null) + grec.FounderUUI = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI); + } + + private bool VerifyToken(UUID groupID, string agentID, string token) + { + // check the token + MembershipData membership = m_Database.RetrieveMember(groupID, agentID); + if (membership != null) + { + if (token != string.Empty && token.Equals(membership.Data["AccessToken"])) + return true; + else + m_log.DebugFormat("[Groups.HGGroupsService]: access token {0} did not match stored one {1}", token, membership.Data["AccessToken"]); + } + else + m_log.DebugFormat("[Groups.HGGroupsService]: membership not found for {0}", agentID); + + return false; + } + } +} diff --git a/OpenSim/Addons/OfflineIM/OfflineIMRegionModule.cs b/OpenSim/Addons/OfflineIM/OfflineIMRegionModule.cs new file mode 100644 index 0000000000..050ebd20f0 --- /dev/null +++ b/OpenSim/Addons/OfflineIM/OfflineIMRegionModule.cs @@ -0,0 +1,267 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Client; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; + +namespace OpenSim.OfflineIM +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "OfflineIMConnectorModule")] + public class OfflineIMRegionModule : ISharedRegionModule, IOfflineIMService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private bool m_Enabled = false; + private List m_SceneList = new List(); + IMessageTransferModule m_TransferModule = null; + private bool m_ForwardOfflineGroupMessages = true; + + private IOfflineIMService m_OfflineIMService; + + public void Initialise(IConfigSource config) + { + IConfig cnf = config.Configs["Messaging"]; + if (cnf == null) + return; + if (cnf != null && cnf.GetString("OfflineMessageModule", string.Empty) != Name) + return; + + m_Enabled = true; + + string serviceLocation = cnf.GetString("OfflineMessageURL", string.Empty); + if (serviceLocation == string.Empty) + m_OfflineIMService = new OfflineIMService(config); + else + m_OfflineIMService = new OfflineIMServiceRemoteConnector(serviceLocation); + + m_ForwardOfflineGroupMessages = cnf.GetBoolean("ForwardOfflineGroupMessages", m_ForwardOfflineGroupMessages); + m_log.DebugFormat("[OfflineIM.V2]: Offline messages enabled by {0}", Name); + } + + public void AddRegion(Scene scene) + { + if (!m_Enabled) + return; + + scene.RegisterModuleInterface(this); + m_SceneList.Add(scene); + scene.EventManager.OnNewClient += OnNewClient; + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + if (m_TransferModule == null) + { + m_TransferModule = scene.RequestModuleInterface(); + if (m_TransferModule == null) + { + scene.EventManager.OnNewClient -= OnNewClient; + + m_SceneList.Clear(); + + m_log.Error("[OfflineIM.V2]: No message transfer module is enabled. Disabling offline messages"); + } + m_TransferModule.OnUndeliveredMessage += UndeliveredMessage; + } + } + + public void RemoveRegion(Scene scene) + { + if (!m_Enabled) + return; + + m_SceneList.Remove(scene); + scene.EventManager.OnNewClient -= OnNewClient; + m_TransferModule.OnUndeliveredMessage -= UndeliveredMessage; + + scene.ForEachClient(delegate(IClientAPI client) + { + client.OnRetrieveInstantMessages -= RetrieveInstantMessages; + client.OnMuteListRequest -= OnMuteListRequest; + }); + } + + public void PostInitialise() + { + } + + public string Name + { + get { return "Offline Message Module V2"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public void Close() + { + m_SceneList.Clear(); + } + + private Scene FindScene(UUID agentID) + { + foreach (Scene s in m_SceneList) + { + ScenePresence presence = s.GetScenePresence(agentID); + if (presence != null && !presence.IsChildAgent) + return s; + } + return null; + } + + private IClientAPI FindClient(UUID agentID) + { + foreach (Scene s in m_SceneList) + { + ScenePresence presence = s.GetScenePresence(agentID); + if (presence != null && !presence.IsChildAgent) + return presence.ControllingClient; + } + return null; + } + + private void OnNewClient(IClientAPI client) + { + client.OnRetrieveInstantMessages += RetrieveInstantMessages; + client.OnMuteListRequest += OnMuteListRequest; + } + + private void RetrieveInstantMessages(IClientAPI client) + { + m_log.DebugFormat("[OfflineIM.V2]: Retrieving stored messages for {0}", client.AgentId); + + List msglist = m_OfflineIMService.GetMessages(client.AgentId); + + if (msglist == null) + m_log.DebugFormat("[OfflineIM.V2]: WARNING null message list."); + + foreach (GridInstantMessage im in msglist) + { + if (im.dialog == (byte)InstantMessageDialog.InventoryOffered) + // send it directly or else the item will be given twice + client.SendInstantMessage(im); + else + { + // Send through scene event manager so all modules get a chance + // to look at this message before it gets delivered. + // + // Needed for proper state management for stored group + // invitations + // + Scene s = FindScene(client.AgentId); + if (s != null) + s.EventManager.TriggerIncomingInstantMessage(im); + } + } + } + + // Apparently this is needed in order for the viewer to request the IMs. + private void OnMuteListRequest(IClientAPI client, uint crc) + { + m_log.DebugFormat("[OfflineIM.V2] Got mute list request for crc {0}", crc); + string filename = "mutes" + client.AgentId.ToString(); + + IXfer xfer = client.Scene.RequestModuleInterface(); + if (xfer != null) + { + xfer.AddNewFile(filename, new Byte[0]); + client.SendMuteListUpdate(filename); + } + } + + private void UndeliveredMessage(GridInstantMessage im) + { + if (im.dialog != (byte)InstantMessageDialog.MessageFromObject && + im.dialog != (byte)InstantMessageDialog.MessageFromAgent && + im.dialog != (byte)InstantMessageDialog.GroupNotice && + im.dialog != (byte)InstantMessageDialog.GroupInvitation && + im.dialog != (byte)InstantMessageDialog.InventoryOffered) + { + return; + } + + if (!m_ForwardOfflineGroupMessages) + { + if (im.dialog == (byte)InstantMessageDialog.GroupNotice || + im.dialog == (byte)InstantMessageDialog.GroupInvitation) + return; + } + + Scene scene = FindScene(new UUID(im.fromAgentID)); + if (scene == null) + scene = m_SceneList[0]; + + string reason = string.Empty; + bool success = m_OfflineIMService.StoreMessage(im, out reason); + + if (im.dialog == (byte)InstantMessageDialog.MessageFromAgent) + { + IClientAPI client = FindClient(new UUID(im.fromAgentID)); + if (client == null) + return; + + client.SendInstantMessage(new GridInstantMessage( + null, new UUID(im.toAgentID), + "System", new UUID(im.fromAgentID), + (byte)InstantMessageDialog.MessageFromAgent, + "User is not logged in. " + + (success ? "Message saved." : "Message not saved: " + reason), + false, new Vector3())); + } + } + + #region IOfflineIM + + public List GetMessages(UUID principalID) + { + return m_OfflineIMService.GetMessages(principalID); + } + + public bool StoreMessage(GridInstantMessage im, out string reason) + { + return m_OfflineIMService.StoreMessage(im, out reason); + } + + #endregion + } +} + diff --git a/OpenSim/Addons/OfflineIM/Properties/AssemblyInfo.cs b/OpenSim/Addons/OfflineIM/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..ffe8a3ec21 --- /dev/null +++ b/OpenSim/Addons/OfflineIM/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Mono.Addins; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenSim.Addons.OfflineIM")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("http://opensimulator.org")] +[assembly: AssemblyProduct("OpenSim.Addons.OfflineIM")] +[assembly: AssemblyCopyright("Copyright (c) OpenSimulator.org Developers")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a16a9905-4393-4872-9fca-4c81bedbd9f2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.7.6.*")] + +[assembly: Addin("OpenSim.OfflineIM", "0.1")] +[assembly: AddinDependency("OpenSim", "0.5")] diff --git a/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRemoteConnector.cs b/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRemoteConnector.cs new file mode 100644 index 0000000000..69feb762f9 --- /dev/null +++ b/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRemoteConnector.cs @@ -0,0 +1,143 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.Linq; +using System.Reflection; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; + +using OpenMetaverse; +using log4net; +using Nini.Config; + +namespace OpenSim.OfflineIM +{ + public class OfflineIMServiceRemoteConnector : IOfflineIMService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = string.Empty; + private object m_Lock = new object(); + + public OfflineIMServiceRemoteConnector(string url) + { + m_ServerURI = url; + m_log.DebugFormat("[OfflineIM.V2.RemoteConnector]: Offline IM server at {0}", m_ServerURI); + } + + public OfflineIMServiceRemoteConnector(IConfigSource config) + { + IConfig cnf = config.Configs["Messaging"]; + if (cnf == null) + { + m_log.WarnFormat("[OfflineIM.V2.RemoteConnector]: Missing Messaging configuration"); + return; + } + + m_ServerURI = cnf.GetString("OfflineMessageURL", string.Empty); + + } + + #region IOfflineIMService + public List GetMessages(UUID principalID) + { + List ims = new List(); + + Dictionary sendData = new Dictionary(); + sendData["PrincipalID"] = principalID; + Dictionary ret = MakeRequest("GET", sendData); + + if (ret == null) + return ims; + + if (!ret.ContainsKey("RESULT")) + return ims; + + if (ret["RESULT"].ToString() == "NULL") + return ims; + + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + GridInstantMessage m = OfflineIMDataUtils.GridInstantMessage((Dictionary)v); + ims.Add(m); + } + + return ims; + } + + public bool StoreMessage(GridInstantMessage im, out string reason) + { + reason = string.Empty; + Dictionary sendData = OfflineIMDataUtils.GridInstantMessage(im); + + Dictionary ret = MakeRequest("STORE", sendData); + + if (ret == null) + { + reason = "Bad response from server"; + return false; + } + + string result = ret["RESULT"].ToString(); + if (result == "NULL" || result.ToLower() == "false") + { + reason = ret["REASON"].ToString(); + return false; + } + + return true; + } + + #endregion + + + #region Make Request + + private Dictionary MakeRequest(string method, Dictionary sendData) + { + sendData["METHOD"] = method; + + string reply = string.Empty; + lock (m_Lock) + reply = SynchronousRestFormsRequester.MakeRequest("POST", + m_ServerURI + "/offlineim", + ServerUtils.BuildQueryString(sendData)); + + Dictionary replyData = ServerUtils.ParseXmlResponse( + reply); + + return replyData; + } + #endregion + + } +} diff --git a/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRobustConnector.cs b/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRobustConnector.cs new file mode 100644 index 0000000000..2b3a01d32d --- /dev/null +++ b/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRobustConnector.cs @@ -0,0 +1,215 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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.Reflection; +using System.Text; +using System.Xml; +using System.Collections.Generic; +using System.IO; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using log4net; +using OpenMetaverse; + +namespace OpenSim.OfflineIM +{ + public class OfflineIMServiceRobustConnector : ServiceConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IOfflineIMService m_OfflineIMService; + private string m_ConfigName = "Messaging"; + + public OfflineIMServiceRobustConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + m_log.DebugFormat("[OfflineIM.V2.RobustConnector]: Starting with config name {0}", m_ConfigName); + + m_OfflineIMService = new OfflineIMService(config); + + server.AddStreamHandler(new OfflineIMServicePostHandler(m_OfflineIMService)); + } + } + + public class OfflineIMServicePostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IOfflineIMService m_OfflineIMService; + + public OfflineIMServicePostHandler(IOfflineIMService service) : + base("POST", "/offlineim") + { + m_OfflineIMService = service; + } + + public override byte[] Handle(string path, Stream requestData, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + StreamReader sr = new StreamReader(requestData); + string body = sr.ReadToEnd(); + sr.Close(); + body = body.Trim(); + + //m_log.DebugFormat("[XXX]: query String: {0}", body); + + try + { + Dictionary request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + string method = request["METHOD"].ToString(); + request.Remove("METHOD"); + + m_log.DebugFormat("[OfflineIM.V2.Handler]: {0}", method); + switch (method) + { + case "GET": + return HandleGet(request); + case "STORE": + return HandleStore(request); + } + m_log.DebugFormat("[OFFLINE IM HANDLER]: unknown method request: {0}", method); + } + catch (Exception e) + { + m_log.DebugFormat("[OFFLINE IM HANDLER]: Exception {0}", e.StackTrace); + } + + return FailureResult(); + } + + byte[] HandleStore(Dictionary request) + { + Dictionary result = new Dictionary(); + + GridInstantMessage im = OfflineIMDataUtils.GridInstantMessage(request); + + string reason = string.Empty; + + bool success = m_OfflineIMService.StoreMessage(im, out reason); + + result["RESULT"] = success.ToString(); + if (!success) + result["REASON"] = reason; + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGet(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("PrincipalID")) + NullResult(result, "Bad network data"); + else + { + UUID principalID = new UUID(request["PrincipalID"].ToString()); + List ims = m_OfflineIMService.GetMessages(principalID); + + Dictionary dict = new Dictionary(); + int i = 0; + foreach (GridInstantMessage m in ims) + dict["im-" + i++] = OfflineIMDataUtils.GridInstantMessage(m); + + result["RESULT"] = dict; + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + #region Helpers + + private void NullResult(Dictionary result, string reason) + { + result["RESULT"] = "NULL"; + result["REASON"] = reason; + } + + private byte[] FailureResult() + { + return BoolResult(false); + } + + private byte[] SuccessResult() + { + return BoolResult(true); + } + + private byte[] BoolResult(bool value) + { + XmlDocument doc = new XmlDocument(); + + XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + doc.AppendChild(xmlnode); + + XmlElement rootElement = doc.CreateElement("", "ServerResponse", + ""); + + doc.AppendChild(rootElement); + + XmlElement result = doc.CreateElement("", "RESULT", ""); + result.AppendChild(doc.CreateTextNode(value.ToString())); + + rootElement.AppendChild(result); + + return DocToBytes(doc); + } + + private byte[] DocToBytes(XmlDocument doc) + { + MemoryStream ms = new MemoryStream(); + XmlTextWriter xw = new XmlTextWriter(ms, null); + xw.Formatting = Formatting.Indented; + doc.WriteTo(xw); + xw.Flush(); + + return ms.ToArray(); + } + + #endregion + } +} diff --git a/OpenSim/Addons/OfflineIM/Service/OfflineIMService.cs b/OpenSim/Addons/OfflineIM/Service/OfflineIMService.cs new file mode 100644 index 0000000000..6ba022cd93 --- /dev/null +++ b/OpenSim/Addons/OfflineIM/Service/OfflineIMService.cs @@ -0,0 +1,131 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.IO; +using System.Reflection; +using System.Runtime.Serialization; +using System.Text; +using System.Timers; +using System.Xml; +using System.Xml.Serialization; +using log4net; +using Nini.Config; + +using OpenMetaverse; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; + +namespace OpenSim.OfflineIM +{ + public class OfflineIMService : OfflineIMServiceBase, IOfflineIMService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private const int MAX_IM = 25; + + private XmlSerializer m_serializer; + private static bool m_Initialized = false; + + public OfflineIMService(IConfigSource config) + : base(config) + { + m_serializer = new XmlSerializer(typeof(GridInstantMessage)); + if (!m_Initialized) + { + m_Database.DeleteOld(); + m_Initialized = true; + } + } + + public List GetMessages(UUID principalID) + { + List ims = new List(); + + OfflineIMData[] messages = m_Database.Get("PrincipalID", principalID.ToString()); + + if (messages == null || (messages != null && messages.Length == 0)) + return ims; + + foreach (OfflineIMData m in messages) + { + using (MemoryStream mstream = new MemoryStream(Encoding.UTF8.GetBytes(m.Data["Message"]))) + { + GridInstantMessage im = (GridInstantMessage)m_serializer.Deserialize(mstream); + ims.Add(im); + } + } + + // Then, delete them + m_Database.Delete("PrincipalID", principalID.ToString()); + + return ims; + } + + public bool StoreMessage(GridInstantMessage im, out string reason) + { + reason = string.Empty; + + // TODO Check limits + UUID principalID = new UUID(im.toAgentID); + long count = m_Database.GetCount("PrincipalID", principalID.ToString()); + if (count >= MAX_IM) + { + reason = "Number of offline IMs has maxed out"; + return false; + } + + string imXml = string.Empty; + using (MemoryStream mstream = new MemoryStream()) + { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(mstream, settings)) + { + m_serializer.Serialize(writer, im); + writer.Flush(); + + mstream.Position = 0; + using (StreamReader sreader = new StreamReader(mstream)) + { + imXml = sreader.ReadToEnd(); + } + } + } + + OfflineIMData data = new OfflineIMData(); + data.PrincipalID = principalID; + data.Data = new Dictionary(); + data.Data["Message"] = imXml; + + return m_Database.Store(data); + + } + } +} diff --git a/OpenSim/Addons/OfflineIM/Service/OfflineIMServiceBase.cs b/OpenSim/Addons/OfflineIM/Service/OfflineIMServiceBase.cs new file mode 100644 index 0000000000..3376be4737 --- /dev/null +++ b/OpenSim/Addons/OfflineIM/Service/OfflineIMServiceBase.cs @@ -0,0 +1,83 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Data; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Base; + +namespace OpenSim.OfflineIM +{ + public class OfflineIMServiceBase : ServiceBase + { + protected IOfflineIMData m_Database = null; + + public OfflineIMServiceBase(IConfigSource config) + : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + string realm = "im_offline"; + + // + // Try reading the [DatabaseService] section, if it exists + // + IConfig dbConfig = config.Configs["DatabaseService"]; + if (dbConfig != null) + { + if (dllName == String.Empty) + dllName = dbConfig.GetString("StorageProvider", String.Empty); + if (connString == String.Empty) + connString = dbConfig.GetString("ConnectionString", String.Empty); + } + + // + // [Messaging] section overrides [DatabaseService], if it exists + // + IConfig imConfig = config.Configs["Messaging"]; + if (imConfig != null) + { + dllName = imConfig.GetString("StorageProvider", dllName); + connString = imConfig.GetString("ConnectionString", connString); + realm = imConfig.GetString("Realm", realm); + } + + // + // We tried, but this doesn't exist. We can't proceed. + // + if (dllName.Equals(String.Empty)) + throw new Exception("No StorageProvider configured"); + + m_Database = LoadPlugin(dllName, new Object[] { connString, realm }); + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module " + dllName); + } + } +} diff --git a/OpenSim/ApplicationPlugins/LoadRegions/Properties/AssemblyInfo.cs b/OpenSim/ApplicationPlugins/LoadRegions/Properties/AssemblyInfo.cs index b81c1e5f28..1b6a3e1a8e 100644 --- a/OpenSim/ApplicationPlugins/LoadRegions/Properties/AssemblyInfo.cs +++ b/OpenSim/ApplicationPlugins/LoadRegions/Properties/AssemblyInfo.cs @@ -63,4 +63,3 @@ using System.Runtime.InteropServices; // [assembly: AssemblyVersion("0.7.6.*")] [assembly : AssemblyVersion("0.7.6.*")] -[assembly : AssemblyFileVersion("0.6.5.0")] \ No newline at end of file diff --git a/OpenSim/ApplicationPlugins/RegionModulesController/Properties/AssemblyInfo.cs b/OpenSim/ApplicationPlugins/RegionModulesController/Properties/AssemblyInfo.cs index be6054d150..5683a72313 100644 --- a/OpenSim/ApplicationPlugins/RegionModulesController/Properties/AssemblyInfo.cs +++ b/OpenSim/ApplicationPlugins/RegionModulesController/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/ApplicationPlugins/RemoteController/Properties/AssemblyInfo.cs b/OpenSim/ApplicationPlugins/RemoteController/Properties/AssemblyInfo.cs index 3ec7a13883..a9d3f74ecd 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/Properties/AssemblyInfo.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Capabilities/Handlers/Properties/AssemblyInfo.cs b/OpenSim/Capabilities/Handlers/Properties/AssemblyInfo.cs index 4ff5fe1d9a..f8f63f437b 100644 --- a/OpenSim/Capabilities/Handlers/Properties/AssemblyInfo.cs +++ b/OpenSim/Capabilities/Handlers/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Data/IGroupsData.cs b/OpenSim/Data/IGroupsData.cs new file mode 100644 index 0000000000..c11e6499a5 --- /dev/null +++ b/OpenSim/Data/IGroupsData.cs @@ -0,0 +1,144 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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.Collections.Generic; +using OpenSim.Data; +using OpenMetaverse; + +namespace OpenSim.Data +{ + public class GroupData + { + public UUID GroupID; + public Dictionary Data; + } + + public class MembershipData + { + public UUID GroupID; + public string PrincipalID; + public Dictionary Data; + } + + public class RoleData + { + public UUID GroupID; + public UUID RoleID; + public Dictionary Data; + } + + public class RoleMembershipData + { + public UUID GroupID; + public UUID RoleID; + public string PrincipalID; + } + + public class PrincipalData + { + public string PrincipalID; + public UUID ActiveGroupID; + } + + public class InvitationData + { + public UUID InviteID; + public UUID GroupID; + public UUID RoleID; + public string PrincipalID; + public Dictionary Data; + } + + public class NoticeData + { + public UUID GroupID; + public UUID NoticeID; + public Dictionary Data; + } + + + public interface IGroupsData + { + // groups table + bool StoreGroup(GroupData data); + GroupData RetrieveGroup(UUID groupID); + GroupData RetrieveGroup(string name); + GroupData[] RetrieveGroups(string pattern); + bool DeleteGroup(UUID groupID); + int GroupsCount(); + + // membership table + MembershipData RetrieveMember(UUID groupID, string pricipalID); + MembershipData[] RetrieveMembers(UUID groupID); + MembershipData[] RetrieveMemberships(string pricipalID); + bool StoreMember(MembershipData data); + bool DeleteMember(UUID groupID, string pricipalID); + int MemberCount(UUID groupID); + + // roles table + bool StoreRole(RoleData data); + RoleData RetrieveRole(UUID groupID, UUID roleID); + RoleData[] RetrieveRoles(UUID groupID); + bool DeleteRole(UUID groupID, UUID roleID); + int RoleCount(UUID groupID); + + // rolememberhip table + RoleMembershipData[] RetrieveRolesMembers(UUID groupID); + RoleMembershipData[] RetrieveRoleMembers(UUID groupID, UUID roleID); + RoleMembershipData[] RetrieveMemberRoles(UUID groupID, string principalID); + RoleMembershipData RetrieveRoleMember(UUID groupID, UUID roleID, string principalID); + int RoleMemberCount(UUID groupID, UUID roleID); + bool StoreRoleMember(RoleMembershipData data); + bool DeleteRoleMember(RoleMembershipData data); + bool DeleteMemberAllRoles(UUID groupID, string principalID); + + // principals table + bool StorePrincipal(PrincipalData data); + PrincipalData RetrievePrincipal(string principalID); + bool DeletePrincipal(string principalID); + + // invites table + bool StoreInvitation(InvitationData data); + InvitationData RetrieveInvitation(UUID inviteID); + InvitationData RetrieveInvitation(UUID groupID, string principalID); + bool DeleteInvite(UUID inviteID); + void DeleteOldInvites(); + + // notices table + bool StoreNotice(NoticeData data); + NoticeData RetrieveNotice(UUID noticeID); + NoticeData[] RetrieveNotices(UUID groupID); + bool DeleteNotice(UUID noticeID); + void DeleteOldNotices(); + + // combinations + MembershipData RetrievePrincipalGroupMembership(string principalID, UUID groupID); + MembershipData[] RetrievePrincipalGroupMemberships(string principalID); + + // Misc + } +} diff --git a/OpenSim/Data/IOfflineIMData.cs b/OpenSim/Data/IOfflineIMData.cs new file mode 100644 index 0000000000..e780304ec8 --- /dev/null +++ b/OpenSim/Data/IOfflineIMData.cs @@ -0,0 +1,49 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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.Collections.Generic; +using OpenSim.Data; +using OpenMetaverse; + +namespace OpenSim.Data +{ + public class OfflineIMData + { + public UUID PrincipalID; + public Dictionary Data; + } + + + public interface IOfflineIMData + { + OfflineIMData[] Get(string field, string val); + long GetCount(string field, string key); + bool Store(OfflineIMData data); + bool Delete(string field, string val); + void DeleteOld(); + } +} diff --git a/OpenSim/Data/MSSQL/Properties/AssemblyInfo.cs b/OpenSim/Data/MSSQL/Properties/AssemblyInfo.cs index 4e96be8da4..9bc580e76a 100644 --- a/OpenSim/Data/MSSQL/Properties/AssemblyInfo.cs +++ b/OpenSim/Data/MSSQL/Properties/AssemblyInfo.cs @@ -62,4 +62,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: [assembly : AssemblyVersion("0.7.6.*")] -[assembly : AssemblyFileVersion("0.6.5.0")] + diff --git a/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs b/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs index f6731c0fa1..dc657c82a8 100644 --- a/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs +++ b/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs @@ -306,5 +306,65 @@ namespace OpenSim.Data.MySQL return ExecuteNonQuery(cmd) > 0; } } + + public long GetCount(string field, string key) + { + return GetCount(new string[] { field }, new string[] { key }); + } + + public long GetCount(string[] fields, string[] keys) + { + if (fields.Length != keys.Length) + return 0; + + List terms = new List(); + + using (MySqlCommand cmd = new MySqlCommand()) + { + for (int i = 0; i < fields.Length; i++) + { + cmd.Parameters.AddWithValue(fields[i], keys[i]); + terms.Add("`" + fields[i] + "` = ?" + fields[i]); + } + + string where = String.Join(" and ", terms.ToArray()); + + string query = String.Format("select count(*) from {0} where {1}", + m_Realm, where); + + cmd.CommandText = query; + + Object result = DoQueryScalar(cmd); + + return Convert.ToInt64(result); + } + } + + public long GetCount(string where) + { + using (MySqlCommand cmd = new MySqlCommand()) + { + string query = String.Format("select count(*) from {0} where {1}", + m_Realm, where); + + cmd.CommandText = query; + + object result = DoQueryScalar(cmd); + + return Convert.ToInt64(result); + } + } + + public object DoQueryScalar(MySqlCommand cmd) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + cmd.Connection = dbcon; + + return cmd.ExecuteScalar(); + } + } + } } diff --git a/OpenSim/Data/MySQL/MySQLGroupsData.cs b/OpenSim/Data/MySQL/MySQLGroupsData.cs new file mode 100644 index 0000000000..2a1bd6c48e --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLGroupsData.cs @@ -0,0 +1,484 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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; +using System.Collections.Generic; +using System.Reflection; + +using OpenSim.Framework; +using OpenSim.Data.MySQL; + +using OpenMetaverse; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + public class MySQLGroupsData : IGroupsData + { + private MySqlGroupsGroupsHandler m_Groups; + private MySqlGroupsMembershipHandler m_Membership; + private MySqlGroupsRolesHandler m_Roles; + private MySqlGroupsRoleMembershipHandler m_RoleMembership; + private MySqlGroupsInvitesHandler m_Invites; + private MySqlGroupsNoticesHandler m_Notices; + private MySqlGroupsPrincipalsHandler m_Principals; + + public MySQLGroupsData(string connectionString, string realm) + { + m_Groups = new MySqlGroupsGroupsHandler(connectionString, realm + "_groups", realm + "_Store"); + m_Membership = new MySqlGroupsMembershipHandler(connectionString, realm + "_membership"); + m_Roles = new MySqlGroupsRolesHandler(connectionString, realm + "_roles"); + m_RoleMembership = new MySqlGroupsRoleMembershipHandler(connectionString, realm + "_rolemembership"); + m_Invites = new MySqlGroupsInvitesHandler(connectionString, realm + "_invites"); + m_Notices = new MySqlGroupsNoticesHandler(connectionString, realm + "_notices"); + m_Principals = new MySqlGroupsPrincipalsHandler(connectionString, realm + "_principals"); + } + + #region groups table + public bool StoreGroup(GroupData data) + { + return m_Groups.Store(data); + } + + public GroupData RetrieveGroup(UUID groupID) + { + GroupData[] groups = m_Groups.Get("GroupID", groupID.ToString()); + if (groups.Length > 0) + return groups[0]; + + return null; + } + + public GroupData RetrieveGroup(string name) + { + GroupData[] groups = m_Groups.Get("Name", name); + if (groups.Length > 0) + return groups[0]; + + return null; + } + + public GroupData[] RetrieveGroups(string pattern) + { + if (string.IsNullOrEmpty(pattern)) + pattern = "1 ORDER BY Name LIMIT 100"; + else + pattern = string.Format("Name LIKE %{0}% ORDER BY Name LIMIT 100", pattern); + + return m_Groups.Get(pattern); + } + + public bool DeleteGroup(UUID groupID) + { + return m_Groups.Delete("GroupID", groupID.ToString()); + } + + public int GroupsCount() + { + return (int)m_Groups.GetCount("Location=\"\""); + } + + #endregion + + #region membership table + public MembershipData[] RetrieveMembers(UUID groupID) + { + return m_Membership.Get("GroupID", groupID.ToString()); + } + + public MembershipData RetrieveMember(UUID groupID, string pricipalID) + { + MembershipData[] m = m_Membership.Get(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), pricipalID }); + if (m != null && m.Length > 0) + return m[0]; + + return null; + } + + public MembershipData[] RetrieveMemberships(string pricipalID) + { + return m_Membership.Get("PrincipalID", pricipalID.ToString()); + } + + public bool StoreMember(MembershipData data) + { + return m_Membership.Store(data); + } + + public bool DeleteMember(UUID groupID, string pricipalID) + { + return m_Membership.Delete(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), pricipalID }); + } + + public int MemberCount(UUID groupID) + { + return (int)m_Membership.GetCount("GroupID", groupID.ToString()); + } + #endregion + + #region roles table + public bool StoreRole(RoleData data) + { + return m_Roles.Store(data); + } + + public RoleData RetrieveRole(UUID groupID, UUID roleID) + { + RoleData[] data = m_Roles.Get(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + + if (data != null && data.Length > 0) + return data[0]; + + return null; + } + + public RoleData[] RetrieveRoles(UUID groupID) + { + //return m_Roles.RetrieveRoles(groupID); + return m_Roles.Get("GroupID", groupID.ToString()); + } + + public bool DeleteRole(UUID groupID, UUID roleID) + { + return m_Roles.Delete(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + } + + public int RoleCount(UUID groupID) + { + return (int)m_Roles.GetCount("GroupID", groupID.ToString()); + } + + + #endregion + + #region rolememberhip table + public RoleMembershipData[] RetrieveRolesMembers(UUID groupID) + { + RoleMembershipData[] data = m_RoleMembership.Get("GroupID", groupID.ToString()); + + return data; + } + + public RoleMembershipData[] RetrieveRoleMembers(UUID groupID, UUID roleID) + { + RoleMembershipData[] data = m_RoleMembership.Get(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + + return data; + } + + public RoleMembershipData[] RetrieveMemberRoles(UUID groupID, string principalID) + { + RoleMembershipData[] data = m_RoleMembership.Get(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), principalID.ToString() }); + + return data; + } + + public RoleMembershipData RetrieveRoleMember(UUID groupID, UUID roleID, string principalID) + { + RoleMembershipData[] data = m_RoleMembership.Get(new string[] { "GroupID", "RoleID", "PrincipalID" }, + new string[] { groupID.ToString(), roleID.ToString(), principalID.ToString() }); + + if (data != null && data.Length > 0) + return data[0]; + + return null; + } + + public int RoleMemberCount(UUID groupID, UUID roleID) + { + return (int)m_RoleMembership.GetCount(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + } + + public bool StoreRoleMember(RoleMembershipData data) + { + return m_RoleMembership.Store(data); + } + + public bool DeleteRoleMember(RoleMembershipData data) + { + return m_RoleMembership.Delete(new string[] { "GroupID", "RoleID", "PrincipalID"}, + new string[] { data.GroupID.ToString(), data.RoleID.ToString(), data.PrincipalID }); + } + + public bool DeleteMemberAllRoles(UUID groupID, string principalID) + { + return m_RoleMembership.Delete(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), principalID }); + } + + #endregion + + #region principals table + public bool StorePrincipal(PrincipalData data) + { + return m_Principals.Store(data); + } + + public PrincipalData RetrievePrincipal(string principalID) + { + PrincipalData[] p = m_Principals.Get("PrincipalID", principalID); + if (p != null && p.Length > 0) + return p[0]; + + return null; + } + + public bool DeletePrincipal(string principalID) + { + return m_Principals.Delete("PrincipalID", principalID); + } + #endregion + + #region invites table + + public bool StoreInvitation(InvitationData data) + { + return m_Invites.Store(data); + } + + public InvitationData RetrieveInvitation(UUID inviteID) + { + InvitationData[] invites = m_Invites.Get("InviteID", inviteID.ToString()); + + if (invites != null && invites.Length > 0) + return invites[0]; + + return null; + } + + public InvitationData RetrieveInvitation(UUID groupID, string principalID) + { + InvitationData[] invites = m_Invites.Get(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), principalID }); + + if (invites != null && invites.Length > 0) + return invites[0]; + + return null; + } + + public bool DeleteInvite(UUID inviteID) + { + return m_Invites.Delete("InviteID", inviteID.ToString()); + } + + public void DeleteOldInvites() + { + m_Invites.DeleteOld(); + } + + #endregion + + #region notices table + + public bool StoreNotice(NoticeData data) + { + return m_Notices.Store(data); + } + + public NoticeData RetrieveNotice(UUID noticeID) + { + NoticeData[] notices = m_Notices.Get("NoticeID", noticeID.ToString()); + + if (notices != null && notices.Length > 0) + return notices[0]; + + return null; + } + + public NoticeData[] RetrieveNotices(UUID groupID) + { + NoticeData[] notices = m_Notices.Get("GroupID", groupID.ToString()); + + return notices; + } + + public bool DeleteNotice(UUID noticeID) + { + return m_Notices.Delete("NoticeID", noticeID.ToString()); + } + + public void DeleteOldNotices() + { + m_Notices.DeleteOld(); + } + + #endregion + + #region combinations + public MembershipData RetrievePrincipalGroupMembership(string principalID, UUID groupID) + { + // TODO + return null; + } + public MembershipData[] RetrievePrincipalGroupMemberships(string principalID) + { + // TODO + return null; + } + + #endregion + } + + public class MySqlGroupsGroupsHandler : MySQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public MySqlGroupsGroupsHandler(string connectionString, string realm, string store) + : base(connectionString, realm, store) + { + } + + } + + public class MySqlGroupsMembershipHandler : MySQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public MySqlGroupsMembershipHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + } + + public class MySqlGroupsRolesHandler : MySQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public MySqlGroupsRolesHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + } + + public class MySqlGroupsRoleMembershipHandler : MySQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public MySqlGroupsRoleMembershipHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + } + + public class MySqlGroupsInvitesHandler : MySQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public MySqlGroupsInvitesHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + public void DeleteOld() + { + uint now = (uint)Util.UnixTimeSinceEpoch(); + + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where TMStamp < ?tstamp", m_Realm); + cmd.Parameters.AddWithValue("?tstamp", now - 14 * 24 * 60 * 60); // > 2 weeks old + + ExecuteNonQuery(cmd); + } + + } + } + + public class MySqlGroupsNoticesHandler : MySQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public MySqlGroupsNoticesHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + public void DeleteOld() + { + uint now = (uint)Util.UnixTimeSinceEpoch(); + + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where TMStamp < ?tstamp", m_Realm); + cmd.Parameters.AddWithValue("?tstamp", now - 14 * 24 * 60 * 60); // > 2 weeks old + + ExecuteNonQuery(cmd); + } + + } + } + + public class MySqlGroupsPrincipalsHandler : MySQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public MySqlGroupsPrincipalsHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + } +} diff --git a/OpenSim/Data/MySQL/MySQLOfflineIMData.cs b/OpenSim/Data/MySQL/MySQLOfflineIMData.cs new file mode 100644 index 0000000000..252f358b7c --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLOfflineIMData.cs @@ -0,0 +1,62 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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; +using System.Collections.Generic; +using System.Reflection; + +using OpenSim.Framework; +using OpenSim.Data.MySQL; + +using OpenMetaverse; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + public class MySQLOfflineIMData : MySQLGenericTableHandler, IOfflineIMData + { + public MySQLOfflineIMData(string connectionString, string realm) + : base(connectionString, realm, "IM_Store") + { + } + + public void DeleteOld() + { + uint now = (uint)Util.UnixTimeSinceEpoch(); + + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where TMStamp < ?tstamp", m_Realm); + cmd.Parameters.AddWithValue("?tstamp", now - 14 * 24 * 60 * 60); // > 2 weeks old + + ExecuteNonQuery(cmd); + } + + } + } +} diff --git a/OpenSim/Data/MySQL/Properties/AssemblyInfo.cs b/OpenSim/Data/MySQL/Properties/AssemblyInfo.cs index 7bfa28dd39..1146d92e9d 100644 --- a/OpenSim/Data/MySQL/Properties/AssemblyInfo.cs +++ b/OpenSim/Data/MySQL/Properties/AssemblyInfo.cs @@ -62,4 +62,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: [assembly : AssemblyVersion("0.7.6.*")] -[assembly : AssemblyFileVersion("0.6.5.0")] + diff --git a/OpenSim/Data/MySQL/Resources/IM_Store.migrations b/OpenSim/Data/MySQL/Resources/IM_Store.migrations new file mode 100644 index 0000000000..7cfcd43888 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/IM_Store.migrations @@ -0,0 +1,24 @@ +:VERSION 1 # -------------------------- + +BEGIN; + +CREATE TABLE `im_offline` ( + `ID` MEDIUMINT NOT NULL AUTO_INCREMENT, + `PrincipalID` char(36) NOT NULL default '', + `Message` text NOT NULL, + `TMStamp` timestamp NOT NULL, + PRIMARY KEY (`ID`), + KEY `PrincipalID` (`PrincipalID`) +) ENGINE=MyISAM; + +COMMIT; + +:VERSION 2 # -------------------------- + +BEGIN; + +INSERT INTO `im_offline` SELECT * from `diva_im_offline`; +DROP TABLE `diva_im_offline`; +DELETE FROM `migrations` WHERE name='diva_im_Store'; + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/os_groups_Store.migrations b/OpenSim/Data/MySQL/Resources/os_groups_Store.migrations new file mode 100644 index 0000000000..9e6f1c112c --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/os_groups_Store.migrations @@ -0,0 +1,115 @@ +:VERSION 1 # -------------------------- + +BEGIN; + +CREATE TABLE `os_groups_groups` ( + `GroupID` char(36) NOT NULL default '', + `Location` varchar(255) NOT NULL default '', + `Name` varchar(255) NOT NULL default '', + `Charter` text NOT NULL, + `InsigniaID` char(36) NOT NULL default '', + `FounderID` char(36) NOT NULL default '', + `MembershipFee` int(11) NOT NULL default '0', + `OpenEnrollment` varchar(255) NOT NULL default '', + `ShowInList` int(4) NOT NULL default '0', + `AllowPublish` int(4) NOT NULL default '0', + `MaturePublish` int(4) NOT NULL default '0', + `OwnerRoleID` char(36) NOT NULL default '', + PRIMARY KEY (`GroupID`), + UNIQUE KEY `Name` (`Name`), + FULLTEXT KEY `Name_2` (`Name`) +) ENGINE=MyISAM; + + +CREATE TABLE `os_groups_membership` ( + `GroupID`char(36) NOT NULL default '', + `PrincipalID` VARCHAR(255) NOT NULL default '', + `SelectedRoleID` char(36) NOT NULL default '', + `Contribution` int(11) NOT NULL default '0', + `ListInProfile` int(4) NOT NULL default '1', + `AcceptNotices` int(4) NOT NULL default '1', + `AccessToken` char(36) NOT NULL default '', + PRIMARY KEY (`GroupID`,`PrincipalID`), + KEY `PrincipalID` (`PrincipalID`) +) ENGINE=MyISAM; + + +CREATE TABLE `os_groups_roles` ( + `GroupID` char(36) NOT NULL default '', + `RoleID` char(36) NOT NULL default '', + `Name` varchar(255) NOT NULL default '', + `Description` varchar(255) NOT NULL default '', + `Title` varchar(255) NOT NULL default '', + `Powers` bigint(20) unsigned NOT NULL default '0', + PRIMARY KEY (`GroupID`,`RoleID`), + KEY `GroupID` (`GroupID`) +) ENGINE=MyISAM; + + +CREATE TABLE `os_groups_rolemembership` ( + `GroupID` char(36) NOT NULL default '', + `RoleID` char(36) NOT NULL default '', + `PrincipalID` VARCHAR(255) NOT NULL default '', + PRIMARY KEY (`GroupID`,`RoleID`,`PrincipalID`), + KEY `PrincipalID` (`PrincipalID`) +) ENGINE=MyISAM; + + +CREATE TABLE `os_groups_invites` ( + `InviteID` char(36) NOT NULL default '', + `GroupID` char(36) NOT NULL default '', + `RoleID` char(36) NOT NULL default '', + `PrincipalID` VARCHAR(255) NOT NULL default '', + `TMStamp` timestamp NOT NULL, + PRIMARY KEY (`InviteID`), + UNIQUE KEY `PrincipalGroup` (`GroupID`,`PrincipalID`) +) ENGINE=MyISAM; + + +CREATE TABLE `os_groups_notices` ( + `GroupID` char(36) NOT NULL default '', + `NoticeID` char(36) NOT NULL default '', + `TMStamp` int(10) unsigned NOT NULL default '0', + `FromName` varchar(255) NOT NULL default '', + `Subject` varchar(255) NOT NULL default '', + `Message` text NOT NULL, + `HasAttachment` int(4) NOT NULL default '0', + `AttachmentType` int(4) NOT NULL default '0', + `AttachmentName` varchar(128) NOT NULL default '', + `AttachmentItemID` char(36) NOT NULL default '', + `AttachmentOwnerID` varchar(255) NOT NULL default '', + PRIMARY KEY (`NoticeID`), + KEY `GroupID` (`GroupID`), + KEY `TMStamp` (`TMStamp`) +) ENGINE=MyISAM; + +CREATE TABLE `os_groups_principals` ( + `PrincipalID` VARCHAR(255) NOT NULL default '', + `ActiveGroupID` char(36) NOT NULL default '', + PRIMARY KEY (`PrincipalID`) +) ENGINE=MyISAM; + +COMMIT; + +:VERSION 2 # -------------------------- + +BEGIN; + +INSERT INTO `os_groups_groups` SELECT * from `diva_groups_groups`; +DROP TABLE `diva_groups_groups`; +INSERT INTO `os_groups_membership` SELECT * from `diva_groups_membership`; +DROP TABLE `diva_groups_membership`; +INSERT INTO `os_groups_roles` SELECT * from `diva_groups_roles`; +DROP TABLE `diva_groups_roles`; +INSERT INTO `os_groups_rolemembership` SELECT * from `diva_groups_rolemembership`; +DROP TABLE `diva_groups_rolemembership`; +INSERT INTO `os_groups_invites` SELECT * from `diva_groups_invites`; +DROP TABLE `diva_groups_invites`; +INSERT INTO `os_groups_notices` SELECT * from `diva_groups_notices`; +DROP TABLE `diva_groups_notices`; +INSERT INTO `os_groups_principals` SELECT * from `diva_groups_principals`; +DROP TABLE `diva_groups_principals`; + +DELETE FROM `migrations` WHERE name='diva_im_Store'; + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/Null/Properties/AssemblyInfo.cs b/OpenSim/Data/Null/Properties/AssemblyInfo.cs index 3931b3dab8..1e02c31bda 100644 --- a/OpenSim/Data/Null/Properties/AssemblyInfo.cs +++ b/OpenSim/Data/Null/Properties/AssemblyInfo.cs @@ -62,4 +62,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: [assembly : AssemblyVersion("0.7.6.*")] -[assembly : AssemblyFileVersion("0.6.5.0")] + diff --git a/OpenSim/Data/Properties/AssemblyInfo.cs b/OpenSim/Data/Properties/AssemblyInfo.cs index 9f342ad571..a85f47306c 100644 --- a/OpenSim/Data/Properties/AssemblyInfo.cs +++ b/OpenSim/Data/Properties/AssemblyInfo.cs @@ -62,4 +62,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: [assembly : AssemblyVersion("0.7.6.*")] -[assembly : AssemblyFileVersion("0.6.5.0")] + diff --git a/OpenSim/Data/SQLite/Properties/AssemblyInfo.cs b/OpenSim/Data/SQLite/Properties/AssemblyInfo.cs index ba52f8258b..992982ce76 100644 --- a/OpenSim/Data/SQLite/Properties/AssemblyInfo.cs +++ b/OpenSim/Data/SQLite/Properties/AssemblyInfo.cs @@ -62,4 +62,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: [assembly : AssemblyVersion("0.7.6.*")] -[assembly : AssemblyFileVersion("0.6.5.0")] + diff --git a/OpenSim/Framework/AssemblyInfo.cs b/OpenSim/Framework/AssemblyInfo.cs index b3db56c3ec..d6b4e6adbf 100644 --- a/OpenSim/Framework/AssemblyInfo.cs +++ b/OpenSim/Framework/AssemblyInfo.cs @@ -60,4 +60,3 @@ using System.Runtime.InteropServices; // [assembly : AssemblyVersion("0.7.6.*")] -[assembly : AssemblyFileVersion("0.6.5.0")] \ No newline at end of file diff --git a/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs b/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs index 077244d4e6..feffa26dbc 100644 --- a/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs b/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs index cf575ac63b..df8eb520e1 100644 --- a/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs @@ -62,4 +62,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: [assembly : AssemblyVersion("0.7.6.*")] -[assembly : AssemblyFileVersion("0.6.5.0")] + diff --git a/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs b/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs index c3b6227e9c..3ef9682a95 100644 --- a/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs b/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs index b0d2d67424..cbdffeb78c 100644 --- a/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Framework/Console/ConsoleUtil.cs b/OpenSim/Framework/Console/ConsoleUtil.cs index dff956adb3..97a86a8099 100644 --- a/OpenSim/Framework/Console/ConsoleUtil.cs +++ b/OpenSim/Framework/Console/ConsoleUtil.cs @@ -49,14 +49,14 @@ namespace OpenSim.Framework.Console = @"Each component of the coord is comma separated. There must be no spaces between the commas. If you don't care about the z component you can simply omit it. If you don't care about the x or y components then you can leave them blank (though a comma is still required) - If you want to specify the maxmimum value of a component then you can use ~ instead of a number + If you want to specify the maximum value of a component then you can use ~ instead of a number If you want to specify the minimum value of a component then you can use -~ instead of a number e.g. - delete object pos 20,20,20 to 40,40,40 + show object pos 20,20,20 to 40,40,40 delete object pos 20,20 to 40,40 - delete object pos ,20,20 to ,40,40 + show object pos ,20,20 to ,40,40 delete object pos ,,30 to ,,~ - delete object pos ,,-~ to ,,30"; + show object pos ,,-~ to ,,30"; public const string MinRawConsoleVectorValue = "-~"; public const string MaxRawConsoleVectorValue = "~"; diff --git a/OpenSim/Framework/Monitoring/AssetStatsCollector.cs b/OpenSim/Framework/Monitoring/AssetStatsCollector.cs index 2a4d45bf0c..6a0f676b42 100644 --- a/OpenSim/Framework/Monitoring/AssetStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/AssetStatsCollector.cs @@ -28,6 +28,8 @@ using System; using System.Timers; +using OpenMetaverse.StructuredData; + namespace OpenSim.Framework.Monitoring { /// @@ -100,5 +102,29 @@ Asset requests yesterday : {3} ({4} per hour) of which {5} were not found", AssetRequestsToday, assetRequestsTodayPerHour, AssetRequestsNotFoundToday, AssetRequestsYesterday, assetRequestsYesterdayPerHour, AssetRequestsNotFoundYesterday); } + + public override string XReport(string uptime, string version) + { + return OSDParser.SerializeJsonString(OReport(uptime, version)); + } + + public override OSDMap OReport(string uptime, string version) + { + double elapsedHours = (DateTime.Now - startTime).TotalHours; + if (elapsedHours <= 0) { elapsedHours = 1; } // prevent divide by zero + + long assetRequestsTodayPerHour = (long)Math.Round(AssetRequestsToday / elapsedHours); + long assetRequestsYesterdayPerHour = (long)Math.Round(AssetRequestsYesterday / 24.0); + + OSDMap ret = new OSDMap(); + ret.Add("AssetRequestsToday", OSD.FromLong(AssetRequestsToday)); + ret.Add("AssetRequestsTodayPerHour", OSD.FromLong(assetRequestsTodayPerHour)); + ret.Add("AssetRequestsNotFoundToday", OSD.FromLong(AssetRequestsNotFoundToday)); + ret.Add("AssetRequestsYesterday", OSD.FromLong(AssetRequestsYesterday)); + ret.Add("AssetRequestsYesterdayPerHour", OSD.FromLong(assetRequestsYesterdayPerHour)); + ret.Add("AssetRequestsNotFoundYesterday", OSD.FromLong(assetRequestsNotFoundYesterday)); + + return ret; + } } } diff --git a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs index 446e3c0697..23dba09c13 100644 --- a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs @@ -80,5 +80,12 @@ namespace OpenSim.Framework.Monitoring { return (string) Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0).ToString() ; } + + public virtual OSDMap OReport(string uptime, string version) + { + OSDMap ret = new OSDMap(); + ret.Add("TotalMemory", new OSDReal(Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0))); + return ret; + } } } diff --git a/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs b/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs index 99f75e34b1..40df562a95 100644 --- a/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs @@ -25,6 +25,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using OpenMetaverse.StructuredData; + namespace OpenSim.Framework.Monitoring { /// @@ -45,5 +47,12 @@ namespace OpenSim.Framework.Monitoring /// A /// string XReport(string uptime, string version); + + /// + /// Report back collected statistical information as an OSDMap of key/values + /// + /// + /// + OSDMap OReport(string uptime, string version); } } diff --git a/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs b/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs index bb83db1d73..36678bbe3e 100644 --- a/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs index 3765efb361..109a58f741 100644 --- a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs @@ -404,6 +404,15 @@ Asset service request failures: {3}" + Environment.NewLine, /// /// public override string XReport(string uptime, string version) + { + return OSDParser.SerializeJsonString(OReport(uptime, version)); + } + + /// + /// Report back collected statistical information as an OSDMap + /// + /// + public override OSDMap OReport(string uptime, string version) { OSDMap args = new OSDMap(30); // args["AssetsInCache"] = OSD.FromString (String.Format ("{0:0.##}", AssetsInCache)); @@ -442,13 +451,11 @@ Asset service request failures: {3}" + Environment.NewLine, args["Uptime"] = OSD.FromString (uptime); args["Version"] = OSD.FromString (version); - string strBuffer = ""; - strBuffer = OSDParser.SerializeJsonString(args); - - return strBuffer; + return args; } } + /// /// Pull packet queue stats from packet queues and report /// @@ -474,5 +481,11 @@ Asset service request failures: {3}" + Environment.NewLine, { return ""; } + + public OSDMap OReport(string uptime, string version) + { + OSDMap ret = new OSDMap(); + return ret; + } } } diff --git a/OpenSim/Framework/Monitoring/Stats/CounterStat.cs b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs new file mode 100755 index 0000000000..d81f1828e6 --- /dev/null +++ b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs @@ -0,0 +1,211 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 System.Linq; +using System.Text; + +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Monitoring +{ +// Create a time histogram of events. The histogram is built in a wrap-around +// array of equally distributed buckets. +// For instance, a minute long histogram of second sized buckets would be: +// new EventHistogram(60, 1000) +public class EventHistogram +{ + private int m_timeBase; + private int m_numBuckets; + private int m_bucketMilliseconds; + private int m_lastBucket; + private int m_totalHistogramMilliseconds; + private long[] m_histogram; + private object histoLock = new object(); + + public EventHistogram(int numberOfBuckets, int millisecondsPerBucket) + { + m_numBuckets = numberOfBuckets; + m_bucketMilliseconds = millisecondsPerBucket; + m_totalHistogramMilliseconds = m_numBuckets * m_bucketMilliseconds; + + m_histogram = new long[m_numBuckets]; + Zero(); + m_lastBucket = 0; + m_timeBase = Util.EnvironmentTickCount(); + } + + public void Event() + { + this.Event(1); + } + + // Record an event at time 'now' in the histogram. + public void Event(int cnt) + { + lock (histoLock) + { + // The time as displaced from the base of the histogram + int bucketTime = Util.EnvironmentTickCountSubtract(m_timeBase); + + // If more than the total time of the histogram, we just start over + if (bucketTime > m_totalHistogramMilliseconds) + { + Zero(); + m_lastBucket = 0; + m_timeBase = Util.EnvironmentTickCount(); + } + else + { + // To which bucket should we add this event? + int bucket = bucketTime / m_bucketMilliseconds; + + // Advance m_lastBucket to the new bucket. Zero any buckets skipped over. + while (bucket != m_lastBucket) + { + // Zero from just after the last bucket to the new bucket or the end + for (int jj = m_lastBucket + 1; jj <= Math.Min(bucket, m_numBuckets - 1); jj++) + { + m_histogram[jj] = 0; + } + m_lastBucket = bucket; + // If the new bucket is off the end, wrap around to the beginning + if (bucket > m_numBuckets) + { + bucket -= m_numBuckets; + m_lastBucket = 0; + m_histogram[m_lastBucket] = 0; + m_timeBase += m_totalHistogramMilliseconds; + } + } + } + m_histogram[m_lastBucket] += cnt; + } + } + + // Get a copy of the current histogram + public long[] GetHistogram() + { + long[] ret = new long[m_numBuckets]; + lock (histoLock) + { + int indx = m_lastBucket + 1; + for (int ii = 0; ii < m_numBuckets; ii++, indx++) + { + if (indx >= m_numBuckets) + indx = 0; + ret[ii] = m_histogram[indx]; + } + } + return ret; + } + + // Get a copy of the current histogram + public OSDArray GetHistogramAsOSDArray() + { + OSDArray ret = new OSDArray(m_numBuckets); + lock (histoLock) + { + int indx = m_lastBucket + 1; + for (int ii = 0; ii < m_numBuckets; ii++, indx++) + { + if (indx >= m_numBuckets) + indx = 0; + ret[ii] = OSD.FromLong(m_histogram[indx]); + } + } + return ret; + } + + // Zero out the histogram + public void Zero() + { + lock (histoLock) + { + for (int ii = 0; ii < m_numBuckets; ii++) + m_histogram[ii] = 0; + } + } +} + +// A statistic that wraps a counter. +// Built this way mostly so histograms and history can be created. +public class CounterStat : Stat +{ + private SortedDictionary m_histograms; + private object counterLock = new object(); + + public CounterStat( + string shortName, + string name, + string description, + string unitName, + string category, + string container, + StatVerbosity verbosity) + : base(shortName, name, description, unitName, category, container, StatType.Push, null, verbosity) + { + m_histograms = new SortedDictionary(); + } + + // Histograms are presumably added at intialization time and the list does not change thereafter. + // Thus no locking of the histogram list. + public void AddHistogram(string histoName, EventHistogram histo) + { + m_histograms.Add(histoName, histo); + } + + public delegate void ProcessHistogram(string name, EventHistogram histo); + public void ForEachHistogram(ProcessHistogram process) + { + foreach (KeyValuePair kvp in m_histograms) + { + process(kvp.Key, kvp.Value); + } + } + + public void Event() + { + this.Event(1); + } + + // Count the underlying counter. + public void Event(int cnt) + { + lock (counterLock) + { + base.Value += cnt; + + foreach (EventHistogram histo in m_histograms.Values) + { + histo.Event(cnt); + } + } + } +} +} diff --git a/OpenSim/Framework/Monitoring/Stats/Stat.cs b/OpenSim/Framework/Monitoring/Stats/Stat.cs index f91251b6ea..c8d91744c1 100644 --- a/OpenSim/Framework/Monitoring/Stats/Stat.cs +++ b/OpenSim/Framework/Monitoring/Stats/Stat.cs @@ -29,12 +29,14 @@ using System; using System.Collections.Generic; using System.Text; +using OpenMetaverse.StructuredData; + namespace OpenSim.Framework.Monitoring { /// /// Holds individual statistic details /// - public class Stat + public class Stat : IDisposable { /// /// Category of this stat (e.g. cache, scene, etc). @@ -181,6 +183,12 @@ namespace OpenSim.Framework.Monitoring Verbosity = verbosity; } + // IDisposable.Dispose() + public virtual void Dispose() + { + return; + } + /// /// Record a value in the sample set. /// @@ -210,6 +218,20 @@ namespace OpenSim.Framework.Monitoring return sb.ToString(); } + public virtual OSDMap ToOSDMap() + { + OSDMap ret = new OSDMap(); + ret.Add("Category", OSD.FromString(Category)); + ret.Add("Container", OSD.FromString(Container)); + ret.Add("ShortName", OSD.FromString(ShortName)); + ret.Add("Name", OSD.FromString(Name)); + ret.Add("Description", OSD.FromString(Description)); + ret.Add("UnitName", OSD.FromString(UnitName)); + ret.Add("Value", OSD.FromReal(Value)); + + return ret; + } + protected void AppendMeasuresOfInterest(StringBuilder sb) { if ((MeasuresOfInterest & MeasuresOfInterest.AverageChangeOverTime) diff --git a/OpenSim/Framework/Monitoring/UserStatsCollector.cs b/OpenSim/Framework/Monitoring/UserStatsCollector.cs index e89c8e6344..81e0fa4e21 100644 --- a/OpenSim/Framework/Monitoring/UserStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/UserStatsCollector.cs @@ -27,6 +27,8 @@ using System.Timers; +using OpenMetaverse.StructuredData; + namespace OpenSim.Framework.Monitoring { /// @@ -88,5 +90,21 @@ namespace OpenSim.Framework.Monitoring Logouts total : {3}", SuccessfulLogins, SuccessfulLoginsToday, SuccessfulLoginsYesterday, Logouts); } + + public override string XReport(string uptime, string version) + { + return OSDParser.SerializeJsonString(OReport(uptime, version)); + } + + public override OSDMap OReport(string uptime, string version) + { + OSDMap ret = new OSDMap(); + ret.Add("SuccessfulLogins", OSD.FromInteger(SuccessfulLogins)); + ret.Add("SuccessfulLoginsToday", OSD.FromInteger(SuccessfulLoginsToday)); + ret.Add("SuccessfulLoginsYesterday", OSD.FromInteger(SuccessfulLoginsYesterday)); + ret.Add("Logouts", OSD.FromInteger(Logouts)); + + return ret; + } } } diff --git a/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs b/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs index f836350fec..d4806f17f6 100644 --- a/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs b/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs index 72fa67981a..1541a5bced 100644 --- a/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs b/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs index 7a122dadbe..a8dff93458 100644 --- a/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs index 386be2de5c..8e592c1e07 100644 --- a/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 05453658c9..724e38bd75 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -875,7 +875,7 @@ namespace OpenSim.Framework return FileName; } - // Nini (config) related Methods + #region Nini (config) related Methods public static IConfigSource ConvertDataRowToXMLConfig(DataRow row, string fileName) { if (!File.Exists(fileName)) @@ -898,6 +898,26 @@ namespace OpenSim.Framework } } + public static string GetConfigVarWithDefaultSection(IConfigSource config, string varname, string section) + { + // First, check the Startup section, the default section + IConfig cnf = config.Configs["Startup"]; + if (cnf == null) + return string.Empty; + string val = cnf.GetString(varname, string.Empty); + + // Then check for an overwrite of the default in the given section + if (!string.IsNullOrEmpty(section)) + { + cnf = config.Configs[section]; + if (cnf != null) + val = cnf.GetString(varname, val); + } + + return val; + } + #endregion + public static float Clip(float x, float min, float max) { return Math.Min(Math.Max(x, min), max); diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs index d7d4708528..9b945f48d3 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs @@ -1445,7 +1445,7 @@ namespace OpenSim.Region.ClientStack.Linden string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { - OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); +// OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); OSDMap resp = new OSDMap(); OSDMap accessPrefs = new OSDMap(); diff --git a/OpenSim/Region/ClientStack/Linden/Caps/Properties/AssemblyInfo.cs b/OpenSim/Region/ClientStack/Linden/Caps/Properties/AssemblyInfo.cs index d29a00174e..595d01a365 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs index fcac182f45..79d56c42fb 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs @@ -56,8 +56,8 @@ namespace OpenSim.Region.ClientStack.Linden [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "RegionConsoleModule")] public class RegionConsoleModule : INonSharedRegionModule, IRegionConsole { - private static readonly ILog m_log = - LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); +// private static readonly ILog m_log = +// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private Scene m_scene; private IEventQueue m_eventQueue; @@ -164,8 +164,8 @@ namespace OpenSim.Region.ClientStack.Linden public class ConsoleHandler : BaseStreamHandler { - private static readonly ILog m_log = - LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); +// private static readonly ILog m_log = +// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private RegionConsoleModule m_consoleModule; private UUID m_agentID; diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index f1fe6e1f5b..cc4d01412b 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -3892,6 +3892,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP { part.Shape.LightEntry = false; } + + if (part.Shape != null && (part.Shape.SculptType == (byte)SculptType.Mesh)) + { + // Ensure that mesh has at least 8 valid faces + part.Shape.ProfileBegin = 12500; + part.Shape.ProfileEnd = 0; + part.Shape.ProfileHollow = 27500; + } } } @@ -12448,6 +12456,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP return String.Empty; } + public OSDMap OReport(string uptime, string version) + { + return new OSDMap(); + } + /// /// Make an asset request to the asset service in response to a client request. /// diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Properties/AssemblyInfo.cs b/OpenSim/Region/ClientStack/Linden/UDP/Properties/AssemblyInfo.cs index 8f9dad35ba..98ef72f6b1 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs index 208bc9eb90..864f33e127 100644 --- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs @@ -1090,7 +1090,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory } bool bakedTextureValid = m_scene.AvatarFactory.ValidateBakedTextureCache(sp); - outputAction("{0} baked appearance texture is {1}", sp.Name, bakedTextureValid ? "OK" : "corrupt"); + outputAction("{0} baked appearance texture is {1}", sp.Name, bakedTextureValid ? "OK" : "incomplete"); } } } diff --git a/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs b/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs index 232a4fe5ae..784a78862f 100644 --- a/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs @@ -65,7 +65,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Lure { m_Enabled = true; - m_ThisGridURL = config.Configs["Messaging"].GetString("Gatekeeper", string.Empty); + m_ThisGridURL = Util.GetConfigVarWithDefaultSection(config, "GatekeeperURI", "Messaging"); + // Legacy. Remove soon! + m_ThisGridURL = config.Configs["Messaging"].GetString("Gatekeeper", m_ThisGridURL); m_log.DebugFormat("[LURE MODULE]: {0} enabled", Name); } } diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs index 964efdaa46..c439ea811f 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs @@ -88,12 +88,11 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess IConfig thisModuleConfig = source.Configs["HGInventoryAccessModule"]; if (thisModuleConfig != null) { - // legacy configuration [obsolete] - m_HomeURI = thisModuleConfig.GetString("ProfileServerURI", string.Empty); - // preferred - m_HomeURI = thisModuleConfig.GetString("HomeURI", m_HomeURI); + m_HomeURI = Util.GetConfigVarWithDefaultSection(source, "HomeURI", "HGInventoryAccessModule"); m_OutboundPermission = thisModuleConfig.GetBoolean("OutboundPermission", true); - m_ThisGatekeeper = thisModuleConfig.GetString("Gatekeeper", string.Empty); + m_ThisGatekeeper = Util.GetConfigVarWithDefaultSection(source, "GatekeeperURI", "HGInventoryAccessModule"); + // Legacy. Renove soon! + m_ThisGatekeeper = thisModuleConfig.GetString("Gatekeeper", m_ThisGatekeeper); m_RestrictInventoryAccessAbroad = thisModuleConfig.GetBoolean("RestrictInventoryAccessAbroad", true); } else diff --git a/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs b/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs index fd8d5e32bd..3c8e0efa05 100755 --- a/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs +++ b/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs @@ -52,6 +52,7 @@ namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging private TimeSpan m_logFileLife; private DateTime m_logFileEndTime; private Object m_logFileWriteLock = new Object(); + private bool m_flushWrite; // set externally when debugging. If let 'null', this does not write any error messages. public ILog ErrorLogger = null; @@ -73,7 +74,9 @@ namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging /// The directory to create the log file in. May be 'null' for default. /// The characters that begin the log file name. May be 'null' for default. /// Maximum age of a log file in minutes. If zero, will set default. - public LogWriter(string dir, string headr, int maxFileTime) + /// Whether to do a flush after every log write. Best left off but + /// if one is looking for a crash, this is a good thing to turn on. + public LogWriter(string dir, string headr, int maxFileTime, bool flushWrite) { m_logDirectory = dir == null ? "." : dir; @@ -86,8 +89,14 @@ namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging m_logFileLife = new TimeSpan(0, m_logMaxFileTimeMin, 0); m_logFileEndTime = DateTime.Now + m_logFileLife; + m_flushWrite = flushWrite; + Enabled = true; } + // Constructor that assumes flushWrite is off. + public LogWriter(string dir, string headr, int maxFileTime) : this(dir, headr, maxFileTime, false) + { + } public void Dispose() { @@ -153,6 +162,8 @@ namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging buff.Append(line); buff.Append("\r\n"); m_logFile.Write(buff.ToString()); + if (m_flushWrite) + m_logFile.Flush(); } } } diff --git a/OpenSim/Region/CoreModules/Properties/AssemblyInfo.cs b/OpenSim/Region/CoreModules/Properties/AssemblyInfo.cs index f6353f913f..bfe0383c26 100644 --- a/OpenSim/Region/CoreModules/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/CoreModules/Properties/AssemblyInfo.cs @@ -31,7 +31,7 @@ using Mono.Addins; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + [assembly: Addin("OpenSim.Region.CoreModules", "0.1")] [assembly: AddinDependency("OpenSim", "0.5")] diff --git a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs index 9fc2dafafc..28db407a47 100644 --- a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs +++ b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs @@ -416,7 +416,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector)) { - m_console.OutputFormat("Error: Start vector {0} does not have a valid format", rawConsoleStartVector); + m_console.OutputFormat("Error: Start vector '{0}' does not have a valid format", rawConsoleStartVector); return; } @@ -425,7 +425,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector)) { - m_console.OutputFormat("Error: End vector {0} does not have a valid format", rawConsoleEndVector); + m_console.OutputFormat("Error: End vector '{0}' does not have a valid format", rawConsoleEndVector); return; } @@ -896,17 +896,17 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector)) { - m_console.OutputFormat("Error: Start vector {0} does not have a valid format", rawConsoleStartVector); + m_console.OutputFormat("Error: Start vector '{0}' does not have a valid format", rawConsoleStartVector); endVector = Vector3.Zero; return false; } - string rawConsoleEndVector = rawComponents.Skip(1).Take(1).Single(); + string rawConsoleEndVector = rawComponents.Skip(2).Take(1).Single(); if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector)) { - m_console.OutputFormat("Error: End vector {0} does not have a valid format", rawConsoleEndVector); + m_console.OutputFormat("Error: End vector '{0}' does not have a valid format", rawConsoleEndVector); return false; } diff --git a/OpenSim/Region/DataSnapshot/DataSnapshotManager.cs b/OpenSim/Region/DataSnapshot/DataSnapshotManager.cs index 5e62f23b0c..13d9d31b35 100644 --- a/OpenSim/Region/DataSnapshot/DataSnapshotManager.cs +++ b/OpenSim/Region/DataSnapshot/DataSnapshotManager.cs @@ -113,9 +113,16 @@ namespace OpenSim.Region.DataSnapshot try { m_enabled = config.Configs["DataSnapshot"].GetBoolean("index_sims", m_enabled); - IConfig conf = config.Configs["GridService"]; - if (conf != null) - m_gridinfo.Add("gatekeeperURL", conf.GetString("Gatekeeper", String.Empty)); + string gatekeeper = Util.GetConfigVarWithDefaultSection(config, "GatekeeperURI", "GridService"); + // Legacy. Remove soon! + if (string.IsNullOrEmpty(gatekeeper)) + { + IConfig conf = config.Configs["GridService"]; + if (conf != null) + gatekeeper = conf.GetString("Gatekeeper", gatekeeper); + } + if (!string.IsNullOrEmpty(gatekeeper)) + m_gridinfo.Add("gatekeeperURL", gatekeeper); m_gridinfo.Add( "name", config.Configs["DataSnapshot"].GetString("gridname", "the lost continent of hippo")); diff --git a/OpenSim/Region/DataSnapshot/Properties/AssemblyInfo.cs b/OpenSim/Region/DataSnapshot/Properties/AssemblyInfo.cs index 0f083c7b35..0e7df07adb 100644 --- a/OpenSim/Region/DataSnapshot/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/DataSnapshot/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Region/Framework/Interfaces/IJsonStoreModule.cs b/OpenSim/Region/Framework/Interfaces/IJsonStoreModule.cs index b40d24fa72..345f01bce2 100644 --- a/OpenSim/Region/Framework/Interfaces/IJsonStoreModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IJsonStoreModule.cs @@ -51,7 +51,6 @@ namespace OpenSim.Region.Framework.Interfaces JsonStoreNodeType GetPathType(UUID storeID, string path); bool TestStore(UUID storeID); - bool TestPath(UUID storeID, string path, bool useJson); bool SetValue(UUID storeID, string path, string value, bool useJson); bool RemoveValue(UUID storeID, string path); diff --git a/OpenSim/Region/Framework/Properties/AssemblyInfo.cs b/OpenSim/Region/Framework/Properties/AssemblyInfo.cs index 2a5828e072..167c248390 100644 --- a/OpenSim/Region/Framework/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/Framework/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index a57e0d1fa6..9bc9c2de5d 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -2075,7 +2075,11 @@ namespace OpenSim.Region.Framework.Scenes // Get terrain height for sub-region in a megaregion if necessary int X = (int)((m_scene.RegionInfo.RegionLocX * Constants.RegionSize) + pos.X); int Y = (int)((m_scene.RegionInfo.RegionLocY * Constants.RegionSize) + pos.Y); - UUID target_regionID = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, X, Y).RegionID; + GridRegion target_region = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, X, Y); + // If X and Y is NaN, target_region will be null + if (target_region == null) + return; + UUID target_regionID = target_region.RegionID; Scene targetScene = m_scene; if (!SceneManager.Instance.TryGetScene(target_regionID, out targetScene)) diff --git a/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs b/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs index d718a2f052..fa35f0ffad 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs @@ -222,7 +222,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance { bool bakedTextureValid = scene.AvatarFactory.ValidateBakedTextureCache(sp); MainConsole.Instance.OutputFormat( - "{0} baked appearance texture is {1}", sp.Name, bakedTextureValid ? "OK" : "corrupt"); + "{0} baked appearance texture is {1}", sp.Name, bakedTextureValid ? "OK" : "incomplete"); } ); } diff --git a/OpenSim/Region/OptionalModules/Properties/AssemblyInfo.cs b/OpenSim/Region/OptionalModules/Properties/AssemblyInfo.cs index 00655312d1..70bda72c07 100644 --- a/OpenSim/Region/OptionalModules/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/OptionalModules/Properties/AssemblyInfo.cs @@ -31,7 +31,7 @@ using Mono.Addins; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + [assembly: Addin("OpenSim.Region.OptionalModules", "0.1")] [assembly: AddinDependency("OpenSim", "0.5")] diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs index ca3989a668..40adba1939 100644 --- a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs +++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs @@ -84,11 +84,11 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore protected static Regex m_PathComponent = new Regex("\\.({[^}]+}|\\[[0-9]+\\]|\\[\\+\\])"); // extract the internals of an array reference - protected static Regex m_SimpleArrayPattern = new Regex("\\[([0-9]+)\\]"); - protected static Regex m_ArrayPattern = new Regex("\\[([0-9]+|\\+)\\]"); + protected static Regex m_SimpleArrayPattern = new Regex("^\\[([0-9]+)\\]$"); + protected static Regex m_ArrayPattern = new Regex("^\\[([0-9]+|\\+)\\]$"); // extract the internals of a has reference - protected static Regex m_HashPattern = new Regex("{([^}]+)}"); + protected static Regex m_HashPattern = new Regex("^{([^}]+)}$"); // ----------------------------------------------------------------- /// @@ -168,28 +168,6 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore return JsonStoreNodeType.Undefined; } - // ----------------------------------------------------------------- - /// - /// - /// - // ----------------------------------------------------------------- - public bool TestPath(string expr, bool useJson) - { - Stack path; - if (! ParsePathExpression(expr,out path)) - return false; - - OSD result = ProcessPathExpression(ValueStore,path); - - if (result == null) - return false; - - if (useJson || OSDBaseType(result.Type)) - return true; - - return false; - } - // ----------------------------------------------------------------- /// /// diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs index fb350683a6..e78a2f4fa3 100644 --- a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs +++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs @@ -297,38 +297,6 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore return JsonStoreNodeType.Undefined; } - // ----------------------------------------------------------------- - /// - /// - /// - // ----------------------------------------------------------------- - public bool TestPath(UUID storeID, string path, bool useJson) - { - if (! m_enabled) return false; - - JsonStore map = null; - lock (m_JsonValueStore) - { - if (! m_JsonValueStore.TryGetValue(storeID,out map)) - { - m_log.InfoFormat("[JsonStore] Missing store {0}",storeID); - return false; - } - } - - try - { - lock (map) - return map.TestPath(path,useJson); - } - catch (Exception e) - { - m_log.Error(string.Format("[JsonStore]: Path test failed for {0} in {1}", path, storeID), e); - } - - return false; - } - // ----------------------------------------------------------------- /// /// diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs index ef08c05b9c..e13eb56439 100644 --- a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs +++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs @@ -168,32 +168,6 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore { m_comms.RegisterScriptInvocations(this); m_comms.RegisterConstants(this); - - // m_comms.RegisterScriptInvocation(this, "JsonCreateStore"); - // m_comms.RegisterScriptInvocation(this, "JsonAttachObjectStore"); - // m_comms.RegisterScriptInvocation(this, "JsonDestroyStore"); - // m_comms.RegisterScriptInvocation(this, "JsonTestStore"); - - // m_comms.RegisterScriptInvocation(this, "JsonReadNotecard"); - // m_comms.RegisterScriptInvocation(this, "JsonWriteNotecard"); - - // m_comms.RegisterScriptInvocation(this, "JsonTestPathList"); - // m_comms.RegisterScriptInvocation(this, "JsonTestPath"); - // m_comms.RegisterScriptInvocation(this, "JsonTestPathJson"); - - // m_comms.RegisterScriptInvocation(this, "JsonGetValue"); - // m_comms.RegisterScriptInvocation(this, "JsonGetValueJson"); - - // m_comms.RegisterScriptInvocation(this, "JsonTakeValue"); - // m_comms.RegisterScriptInvocation(this, "JsonTakeValueJson"); - - // m_comms.RegisterScriptInvocation(this, "JsonReadValue"); - // m_comms.RegisterScriptInvocation(this, "JsonReadValueJson"); - - // m_comms.RegisterScriptInvocation(this, "JsonSetValue"); - // m_comms.RegisterScriptInvocation(this, "JsonSetValueJson"); - - // m_comms.RegisterScriptInvocation(this, "JsonRemoveValue"); } catch (Exception e) { @@ -341,18 +315,6 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore return (int)m_store.GetPathType(storeID,path); } - [ScriptInvocation] - public int JsonTestPath(UUID hostID, UUID scriptID, UUID storeID, string path) - { - return m_store.TestPath(storeID,path,false) ? 1 : 0; - } - - [ScriptInvocation] - public int JsonTestPathJson(UUID hostID, UUID scriptID, UUID storeID, string path) - { - return m_store.TestPath(storeID,path,true) ? 1 : 0; - } - // ----------------------------------------------------------------- /// /// diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/Tests/JsonStoreScriptModuleTests.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/Tests/JsonStoreScriptModuleTests.cs index 3d9ad163a2..b64dbd40e9 100644 --- a/OpenSim/Region/OptionalModules/Scripting/JsonStore/Tests/JsonStoreScriptModuleTests.cs +++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/Tests/JsonStoreScriptModuleTests.cs @@ -158,8 +158,8 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests Assert.That(dsrv, Is.EqualTo(1)); - int tprv = (int)InvokeOp("JsonTestPath", storeId, "Hello"); - Assert.That(tprv, Is.EqualTo(0)); + int tprv = (int)InvokeOp("JsonGetPathType", storeId, "Hello"); + Assert.That(tprv, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_UNDEF)); } [Test] @@ -277,8 +277,8 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests int returnValue = (int)InvokeOp( "JsonRemoveValue", storeId, "Hello"); Assert.That(returnValue, Is.EqualTo(1)); - int result = (int)InvokeOp("JsonTestPath", storeId, "Hello"); - Assert.That(result, Is.EqualTo(0)); + int result = (int)InvokeOp("JsonGetPathType", storeId, "Hello"); + Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_UNDEF)); string returnValue2 = (string)InvokeOp("JsonGetValue", storeId, "Hello"); Assert.That(returnValue2, Is.EqualTo("")); @@ -291,8 +291,8 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests int returnValue = (int)InvokeOp( "JsonRemoveValue", storeId, "Hello"); Assert.That(returnValue, Is.EqualTo(1)); - int result = (int)InvokeOp("JsonTestPath", storeId, "Hello"); - Assert.That(result, Is.EqualTo(0)); + int result = (int)InvokeOp("JsonGetPathType", storeId, "Hello"); + Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_UNDEF)); string returnValue2 = (string)InvokeOp("JsonGetJson", storeId, "Hello"); Assert.That(returnValue2, Is.EqualTo("")); @@ -306,11 +306,11 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests int returnValue = (int)InvokeOp( "JsonRemoveValue", storeId, "Hello[0]"); Assert.That(returnValue, Is.EqualTo(1)); - int result = (int)InvokeOp("JsonTestPath", storeId, "Hello[0]"); - Assert.That(result, Is.EqualTo(1)); + int result = (int)InvokeOp("JsonGetPathType", storeId, "Hello[0]"); + Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_VALUE)); - result = (int)InvokeOp("JsonTestPath", storeId, "Hello[1]"); - Assert.That(result, Is.EqualTo(0)); + result = (int)InvokeOp("JsonGetPathType", storeId, "Hello[1]"); + Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_UNDEF)); string stringReturnValue = (string)InvokeOp("JsonGetValue", storeId, "Hello[0]"); Assert.That(stringReturnValue, Is.EqualTo("value2")); @@ -400,7 +400,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests // } [Test] - public void TestGetArrayLength() + public void TestJsonGetArrayLength() { TestHelpers.InMethod(); // TestHelpers.EnableLogging(); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs b/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs index ae54499716..3a27d2c605 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs @@ -438,6 +438,28 @@ public override BulletConstraint Create6DofConstraintToPoint(BulletWorld world, joinPoint, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); } +public override BulletConstraint Create6DofConstraintFixed(BulletWorld world, BulletBody obj1, + Vector3 frameInBloc, Quaternion frameInBrot, + bool useLinearReferenceFrameB, bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.Create6DofConstraintFixed2(worldu.ptr, bodyu1.ptr, + frameInBloc, frameInBrot, useLinearReferenceFrameB, disableCollisionsBetweenLinkedBodies)); +} + +public override BulletConstraint Create6DofSpringConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frame1loc, Quaternion frame1rot, + Vector3 frame2loc, Quaternion frame2rot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.Create6DofSpringConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, frame1loc, frame1rot, + frame2loc, frame2rot, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); +} + public override BulletConstraint CreateHingeConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, Vector3 pivotinA, Vector3 pivotinB, Vector3 axisInA, Vector3 axisInB, @@ -450,6 +472,52 @@ public override BulletConstraint CreateHingeConstraint(BulletWorld world, Bullet pivotinA, pivotinB, axisInA, axisInB, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); } +public override BulletConstraint CreateSliderConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frame1loc, Quaternion frame1rot, + Vector3 frame2loc, Quaternion frame2rot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.CreateSliderConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, frame1loc, frame1rot, + frame2loc, frame2rot, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); +} + +public override BulletConstraint CreateConeTwistConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frame1loc, Quaternion frame1rot, + Vector3 frame2loc, Quaternion frame2rot, + bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.CreateConeTwistConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, frame1loc, frame1rot, + frame2loc, frame2rot, disableCollisionsBetweenLinkedBodies)); +} + +public override BulletConstraint CreateGearConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 axisInA, Vector3 axisInB, + float ratio, bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.CreateGearConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, axisInA, axisInB, + ratio, disableCollisionsBetweenLinkedBodies)); +} + +public override BulletConstraint CreatePoint2PointConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 pivotInA, Vector3 pivotInB, + bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.CreatePoint2PointConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, pivotInA, pivotInB, + disableCollisionsBetweenLinkedBodies)); +} + public override void SetConstraintEnable(BulletConstraint constrain, float numericTrueFalse) { BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; @@ -1425,12 +1493,46 @@ public static extern IntPtr Create6DofConstraintToPoint2(IntPtr world, IntPtr ob Vector3 joinPoint, bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr Create6DofConstraintFixed2(IntPtr world, IntPtr obj1, + Vector3 frameInBloc, Quaternion frameInBrot, + bool useLinearReferenceFrameB, bool disableCollisionsBetweenLinkedBodies); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr Create6DofSpringConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, + Vector3 frame1loc, Quaternion frame1rot, + Vector3 frame2loc, Quaternion frame2rot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern IntPtr CreateHingeConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, Vector3 pivotinA, Vector3 pivotinB, Vector3 axisInA, Vector3 axisInB, bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateSliderConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, + Vector3 frameInAloc, Quaternion frameInArot, + Vector3 frameInBloc, Quaternion frameInBrot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateConeTwistConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, + Vector3 frameInAloc, Quaternion frameInArot, + Vector3 frameInBloc, Quaternion frameInBrot, + bool disableCollisionsBetweenLinkedBodies); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateGearConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, + Vector3 axisInA, Vector3 axisInB, + float ratio, bool disableCollisionsBetweenLinkedBodies); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreatePoint2PointConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, + Vector3 pivotInA, Vector3 pivotInB, + bool disableCollisionsBetweenLinkedBodies); + + [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern void SetConstraintEnable2(IntPtr constrain, float numericTrueFalse); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs b/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs index 39e62dd1f3..6fc10e95ce 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs @@ -559,8 +559,9 @@ private sealed class BulletConstraintXNA : BulletConstraint } - //BulletSimAPI.Create6DofConstraint(m_world.ptr, m_body1.ptr, m_body2.ptr,frame1, frame1rot,frame2, frame2rot,useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); - public override BulletConstraint Create6DofConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) + public override BulletConstraint Create6DofConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, + bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) { DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; @@ -584,7 +585,24 @@ private sealed class BulletConstraintXNA : BulletConstraint return new BulletConstraintXNA(consttr); } - + public override BulletConstraint Create6DofConstraintFixed(BulletWorld pWorld, BulletBody pBody1, + Vector3 pframe1, Quaternion pframe1rot, + bool pUseLinearReferenceFrameB, bool pdisableCollisionsBetweenLinkedBodies) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody body1 = (pBody1 as BulletBodyXNA).rigidBody; + IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z); + IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W); + IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot); + frame1._origin = frame1v; + + Generic6DofConstraint consttr = new Generic6DofConstraint(body1, ref frame1, pUseLinearReferenceFrameB); + consttr.CalculateTransforms(); + world.AddConstraint(consttr,pdisableCollisionsBetweenLinkedBodies); + + return new BulletConstraintXNA(consttr); + } + /// /// /// @@ -1133,8 +1151,8 @@ private sealed class BulletConstraintXNA : BulletConstraint p.numberOfSolverIterations = o[0].numberOfSolverIterations; p.linksetImplementation = BSParam.LinksetImplementation; - p.linkConstraintUseFrameOffset = BSParam.LinkConstraintUseFrameOffset; - p.linkConstraintEnableTransMotor = BSParam.LinkConstraintEnableTransMotor; + p.linkConstraintUseFrameOffset = BSParam.NumericBool(BSParam.LinkConstraintUseFrameOffset); + p.linkConstraintEnableTransMotor = BSParam.NumericBool(BSParam.LinkConstraintEnableTransMotor); p.linkConstraintTransMotorMaxVel = BSParam.LinkConstraintTransMotorMaxVel; p.linkConstraintTransMotorMaxForce = BSParam.LinkConstraintTransMotorMaxForce; p.linkConstraintERP = BSParam.LinkConstraintERP; @@ -1443,129 +1461,130 @@ private sealed class BulletConstraintXNA : BulletConstraint public BSPhysicsShapeType BSShapeTypeFromBroadPhaseNativeType(BroadphaseNativeTypes pin) { + BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; switch (pin) { case BroadphaseNativeTypes.BOX_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_BOX; + ret = BSPhysicsShapeType.SHAPE_BOX; break; case BroadphaseNativeTypes.TRIANGLE_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.TETRAHEDRAL_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.CONVEX_TRIANGLEMESH_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_MESH; + ret = BSPhysicsShapeType.SHAPE_MESH; break; case BroadphaseNativeTypes.CONVEX_HULL_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_HULL; + ret = BSPhysicsShapeType.SHAPE_HULL; break; case BroadphaseNativeTypes.CONVEX_POINT_CLOUD_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.CUSTOM_POLYHEDRAL_SHAPE_TYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; //implicit convex shapes case BroadphaseNativeTypes.IMPLICIT_CONVEX_SHAPES_START_HERE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.SPHERE_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_SPHERE; + ret = BSPhysicsShapeType.SHAPE_SPHERE; break; case BroadphaseNativeTypes.MULTI_SPHERE_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.CAPSULE_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_CAPSULE; + ret = BSPhysicsShapeType.SHAPE_CAPSULE; break; case BroadphaseNativeTypes.CONE_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_CONE; + ret = BSPhysicsShapeType.SHAPE_CONE; break; case BroadphaseNativeTypes.CONVEX_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.CYLINDER_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_CYLINDER; + ret = BSPhysicsShapeType.SHAPE_CYLINDER; break; case BroadphaseNativeTypes.UNIFORM_SCALING_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.MINKOWSKI_SUM_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.MINKOWSKI_DIFFERENCE_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.BOX_2D_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.CONVEX_2D_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.CUSTOM_CONVEX_SHAPE_TYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; //concave shape case BroadphaseNativeTypes.CONCAVE_SHAPES_START_HERE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; //keep all the convex shapetype below here, for the check IsConvexShape in broadphase proxy! case BroadphaseNativeTypes.TRIANGLE_MESH_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_MESH; + ret = BSPhysicsShapeType.SHAPE_MESH; break; case BroadphaseNativeTypes.SCALED_TRIANGLE_MESH_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_MESH; + ret = BSPhysicsShapeType.SHAPE_MESH; break; ///used for demo integration FAST/Swift collision library and Bullet case BroadphaseNativeTypes.FAST_CONCAVE_MESH_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_MESH; + ret = BSPhysicsShapeType.SHAPE_MESH; break; //terrain case BroadphaseNativeTypes.TERRAIN_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_HEIGHTMAP; + ret = BSPhysicsShapeType.SHAPE_HEIGHTMAP; break; ///Used for GIMPACT Trimesh integration case BroadphaseNativeTypes.GIMPACT_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_MESH; + ret = BSPhysicsShapeType.SHAPE_MESH; break; ///Multimaterial mesh case BroadphaseNativeTypes.MULTIMATERIAL_TRIANGLE_MESH_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_MESH; + ret = BSPhysicsShapeType.SHAPE_MESH; break; case BroadphaseNativeTypes.EMPTY_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.STATIC_PLANE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_GROUNDPLANE; + ret = BSPhysicsShapeType.SHAPE_GROUNDPLANE; break; case BroadphaseNativeTypes.CUSTOM_CONCAVE_SHAPE_TYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.CONCAVE_SHAPES_END_HERE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.COMPOUND_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_COMPOUND; + ret = BSPhysicsShapeType.SHAPE_COMPOUND; break; case BroadphaseNativeTypes.SOFTBODY_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_MESH; + ret = BSPhysicsShapeType.SHAPE_MESH; break; case BroadphaseNativeTypes.HFFLUID_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.HFFLUID_BUOYANT_CONVEX_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; case BroadphaseNativeTypes.INVALID_SHAPE_PROXYTYPE: - return BSPhysicsShapeType.SHAPE_UNKNOWN; + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; break; } - return BSPhysicsShapeType.SHAPE_UNKNOWN; + return ret; } public override void RemoveChildShapeFromCompoundShape(BulletShape cShape, BulletShape removeShape) { /* TODO */ } @@ -1579,7 +1598,39 @@ private sealed class BulletConstraintXNA : BulletConstraint return new BulletShapeXNA(m_planeshape, BSPhysicsShapeType.SHAPE_GROUNDPLANE); } - public override BulletConstraint CreateHingeConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, Vector3 ppivotInA, Vector3 ppivotInB, Vector3 paxisInA, Vector3 paxisInB, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) + public override BulletConstraint Create6DofSpringConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, + bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) + + { + Generic6DofSpringConstraint constrain = null; + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody body1 = (pBody1 as BulletBodyXNA).rigidBody; + RigidBody body2 = (pBody2 as BulletBodyXNA).rigidBody; + if (body1 != null && body2 != null) + { + IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z); + IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W); + IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot); + frame1._origin = frame1v; + + IndexedVector3 frame2v = new IndexedVector3(pframe2.X, pframe2.Y, pframe2.Z); + IndexedQuaternion frame2rot = new IndexedQuaternion(pframe2rot.X, pframe2rot.Y, pframe2rot.Z, pframe2rot.W); + IndexedMatrix frame2 = IndexedMatrix.CreateFromQuaternion(frame2rot); + frame2._origin = frame1v; + + constrain = new Generic6DofSpringConstraint(body1, body2, ref frame1, ref frame2, puseLinearReferenceFrameA); + world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies); + + constrain.CalculateTransforms(); + } + + return new BulletConstraintXNA(constrain); + } + + public override BulletConstraint CreateHingeConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 ppivotInA, Vector3 ppivotInB, Vector3 paxisInA, Vector3 paxisInB, + bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) { HingeConstraint constrain = null; DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; @@ -1591,6 +1642,100 @@ private sealed class BulletConstraintXNA : BulletConstraint IndexedVector3 pivotInB = new IndexedVector3(ppivotInB.X, ppivotInB.Y, ppivotInB.Z); IndexedVector3 axisInA = new IndexedVector3(paxisInA.X, paxisInA.Y, paxisInA.Z); IndexedVector3 axisInB = new IndexedVector3(paxisInB.X, paxisInB.Y, paxisInB.Z); + constrain = new HingeConstraint(rb1, rb2, ref pivotInA, ref pivotInB, ref axisInA, ref axisInB, puseLinearReferenceFrameA); + world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies); + } + return new BulletConstraintXNA(constrain); + } + + public override BulletConstraint CreateSliderConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 pframe1, Quaternion pframe1rot, + Vector3 pframe2, Quaternion pframe2rot, + bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) + { + SliderConstraint constrain = null; + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody rb1 = (pBody1 as BulletBodyXNA).rigidBody; + RigidBody rb2 = (pBody2 as BulletBodyXNA).rigidBody; + if (rb1 != null && rb2 != null) + { + IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z); + IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W); + IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot); + frame1._origin = frame1v; + + IndexedVector3 frame2v = new IndexedVector3(pframe2.X, pframe2.Y, pframe2.Z); + IndexedQuaternion frame2rot = new IndexedQuaternion(pframe2rot.X, pframe2rot.Y, pframe2rot.Z, pframe2rot.W); + IndexedMatrix frame2 = IndexedMatrix.CreateFromQuaternion(frame2rot); + frame2._origin = frame1v; + + constrain = new SliderConstraint(rb1, rb2, ref frame1, ref frame2, puseLinearReferenceFrameA); + world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies); + } + return new BulletConstraintXNA(constrain); + } + + public override BulletConstraint CreateConeTwistConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 pframe1, Quaternion pframe1rot, + Vector3 pframe2, Quaternion pframe2rot, + bool pdisableCollisionsBetweenLinkedBodies) + { + ConeTwistConstraint constrain = null; + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody rb1 = (pBody1 as BulletBodyXNA).rigidBody; + RigidBody rb2 = (pBody2 as BulletBodyXNA).rigidBody; + if (rb1 != null && rb2 != null) + { + IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z); + IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W); + IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot); + frame1._origin = frame1v; + + IndexedVector3 frame2v = new IndexedVector3(pframe2.X, pframe2.Y, pframe2.Z); + IndexedQuaternion frame2rot = new IndexedQuaternion(pframe2rot.X, pframe2rot.Y, pframe2rot.Z, pframe2rot.W); + IndexedMatrix frame2 = IndexedMatrix.CreateFromQuaternion(frame2rot); + frame2._origin = frame1v; + + constrain = new ConeTwistConstraint(rb1, rb2, ref frame1, ref frame2); + world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies); + } + return new BulletConstraintXNA(constrain); + } + + public override BulletConstraint CreateGearConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 paxisInA, Vector3 paxisInB, + float pratio, bool pdisableCollisionsBetweenLinkedBodies) + { + Generic6DofConstraint constrain = null; + /* BulletXNA does not have a gear constraint + GearConstraint constrain = null; + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody rb1 = (pBody1 as BulletBodyXNA).rigidBody; + RigidBody rb2 = (pBody2 as BulletBodyXNA).rigidBody; + if (rb1 != null && rb2 != null) + { + IndexedVector3 axis1 = new IndexedVector3(paxisInA.X, paxisInA.Y, paxisInA.Z); + IndexedVector3 axis2 = new IndexedVector3(paxisInB.X, paxisInB.Y, paxisInB.Z); + constrain = new GearConstraint(rb1, rb2, ref axis1, ref axis2, pratio); + world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies); + } + */ + return new BulletConstraintXNA(constrain); + } + + public override BulletConstraint CreatePoint2PointConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 ppivotInA, Vector3 ppivotInB, + bool pdisableCollisionsBetweenLinkedBodies) + { + Point2PointConstraint constrain = null; + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody rb1 = (pBody1 as BulletBodyXNA).rigidBody; + RigidBody rb2 = (pBody2 as BulletBodyXNA).rigidBody; + if (rb1 != null && rb2 != null) + { + IndexedVector3 pivotInA = new IndexedVector3(ppivotInA.X, ppivotInA.Y, ppivotInA.Z); + IndexedVector3 pivotInB = new IndexedVector3(ppivotInB.X, ppivotInB.Y, ppivotInB.Z); + constrain = new Point2PointConstraint(rb1, rb2, ref pivotInA, ref pivotInB); world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies); } return new BulletConstraintXNA(constrain); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs b/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs index 3f83ef0ed2..5765b0dc86 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs @@ -365,11 +365,38 @@ public abstract BulletConstraint Create6DofConstraintToPoint(BulletWorld world, Vector3 joinPoint, bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); +public abstract BulletConstraint Create6DofConstraintFixed(BulletWorld world, BulletBody obj1, + Vector3 frameInBloc, Quaternion frameInBrot, + bool useLinearReferenceFrameB, bool disableCollisionsBetweenLinkedBodies); + +public abstract BulletConstraint Create6DofSpringConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frame1loc, Quaternion frame1rot, + Vector3 frame2loc, Quaternion frame2rot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + public abstract BulletConstraint CreateHingeConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, Vector3 pivotinA, Vector3 pivotinB, Vector3 axisInA, Vector3 axisInB, bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); +public abstract BulletConstraint CreateSliderConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frameInAloc, Quaternion frameInArot, + Vector3 frameInBloc, Quaternion frameInBrot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + +public abstract BulletConstraint CreateConeTwistConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frameInAloc, Quaternion frameInArot, + Vector3 frameInBloc, Quaternion frameInBrot, + bool disableCollisionsBetweenLinkedBodies); + +public abstract BulletConstraint CreateGearConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 axisInA, Vector3 axisInB, + float ratio, bool disableCollisionsBetweenLinkedBodies); + +public abstract BulletConstraint CreatePoint2PointConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 pivotInA, Vector3 pivotInB, + bool disableCollisionsBetweenLinkedBodies); + public abstract void SetConstraintEnable(BulletConstraint constrain, float numericTrueFalse); public abstract void SetConstraintNumSolverIterations(BulletConstraint constrain, float iterations); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 8dca7c65d5..1f186c371c 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -443,6 +443,7 @@ public sealed class BSCharacter : BSPhysObject PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() { DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); + PositionSanityCheck(); ForcePosition = _position; }); } @@ -456,7 +457,6 @@ public sealed class BSCharacter : BSPhysObject _position = value; if (PhysBody.HasPhysicalBody) { - PositionSanityCheck(); PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); } } @@ -512,9 +512,8 @@ public sealed class BSCharacter : BSPhysObject // just assign to "Position" because of potential call loops. PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate() { - DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); - if (PhysBody.HasPhysicalBody) - PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); + DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); + ForcePosition = _position; }); ret = true; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs index ecb1b32884..476a0e5570 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs @@ -57,6 +57,7 @@ public sealed class BSConstraint6Dof : BSConstraint obj1.ID, obj1.AddrString, obj2.ID, obj2.AddrString); } + // 6 Dof constraint based on a midpoint between the two constrained bodies public BSConstraint6Dof(BulletWorld world, BulletBody obj1, BulletBody obj2, Vector3 joinPoint, bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) @@ -94,6 +95,21 @@ public sealed class BSConstraint6Dof : BSConstraint } } + // A 6 Dof constraint that is fixed in the world and constrained to a on-the-fly created static object + public BSConstraint6Dof(BulletWorld world, BulletBody obj1, Vector3 frameInBloc, Quaternion frameInBrot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) + : base(world) + { + m_body1 = obj1; + m_body2 = obj1; // Look out for confusion down the road + m_constraint = PhysicsScene.PE.Create6DofConstraintFixed(m_world, m_body1, + frameInBloc, frameInBrot, + useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies); + m_enabled = true; + world.physicsScene.DetailLog("{0},BS6DofConstraint,createFixed,wID={1},rID={2},rBody={3}", + BSScene.DetailLogZero, world.worldID, obj1.ID, obj1.AddrString); + } + public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot) { bool ret = false; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraintCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraintCollection.cs index 2aeff25035..5c8d94e07e 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraintCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraintCollection.cs @@ -117,8 +117,7 @@ public sealed class BSConstraintCollection : IDisposable if (this.TryGetConstraint(body1, body2, out constrain)) { // remove the constraint from our collection - RemoveAndDestroyConstraint(constrain); - ret = true; + ret = RemoveAndDestroyConstraint(constrain); } } @@ -126,17 +125,19 @@ public sealed class BSConstraintCollection : IDisposable } // The constraint MUST exist in the collection + // Could be called if the constraint was previously removed. + // Return 'true' if the constraint was actually removed and disposed. public bool RemoveAndDestroyConstraint(BSConstraint constrain) { + bool removed = false; lock (m_constraints) { // remove the constraint from our collection - m_constraints.Remove(constrain); + removed = m_constraints.Remove(constrain); } - // tell the engine that all its structures need to be freed + // Dispose() is safe to call multiple times constrain.Dispose(); - // we destroyed something - return true; + return removed; } // Remove all constraints that reference the passed body. diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index 41d353a807..e6933f90bf 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -144,7 +144,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin enableAngularVerticalAttraction = true; enableAngularDeflection = false; enableAngularBanking = false; - if (BSParam.VehicleDebuggingEnabled != ConfigurationParameters.numericFalse) + if (BSParam.VehicleDebuggingEnabled) { enableAngularVerticalAttraction = true; enableAngularDeflection = false; @@ -607,8 +607,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin // TODO: possibly set AngularFactor and LinearFactor for the type of vehicle. // Maybe compute linear and angular factor and damping from params. PhysicsScene.PE.SetAngularDamping(Prim.PhysBody, BSParam.VehicleAngularDamping); - PhysicsScene.PE.SetLinearFactor(Prim.PhysBody, BSParam.VehicleLinearFactorV); - PhysicsScene.PE.SetAngularFactorV(Prim.PhysBody, BSParam.VehicleAngularFactorV); + PhysicsScene.PE.SetLinearFactor(Prim.PhysBody, BSParam.VehicleLinearFactor); + PhysicsScene.PE.SetAngularFactorV(Prim.PhysBody, BSParam.VehicleAngularFactor); // Vehicles report collision events so we know when it's on the ground PhysicsScene.PE.AddToCollisionFlags(Prim.PhysBody, CollisionFlags.BS_VEHICLE_COLLISIONS); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs index 4ce58c7d75..e05562a2c9 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs @@ -485,8 +485,8 @@ public sealed class BSLinksetCompound : BSLinkset } OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement; OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; - PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, cPrim.PhysShape, offsetPos, offsetRot); - DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addNonNative,indx={1},rShape={2},cShape={3},offPos={4},offRot={5}", + PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, cPrim.PhysShape, offsetPos, offsetRot); + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addNonNative,indx={1},rShape={2},cShape={3},offPos={4},offRot={5}", LinksetRoot.LocalID, memberIndex, LinksetRoot.PhysShape, cPrim.PhysShape, offsetPos, offsetRot); } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs index cc814d16d0..6d252caab2 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs @@ -223,8 +223,8 @@ public sealed class BSLinksetConstraints : BSLinkset constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); // tweek the constraint to increase stability - constrain.UseFrameOffset(BSParam.BoolNumeric(BSParam.LinkConstraintUseFrameOffset)); - constrain.TranslationalLimitMotor(BSParam.BoolNumeric(BSParam.LinkConstraintEnableTransMotor), + constrain.UseFrameOffset(BSParam.LinkConstraintUseFrameOffset); + constrain.TranslationalLimitMotor(BSParam.LinkConstraintEnableTransMotor, BSParam.LinkConstraintTransMotorMaxVel, BSParam.LinkConstraintTransMotorMaxForce); constrain.SetCFMAndERP(BSParam.LinkConstraintCFM, BSParam.LinkConstraintERP); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs index 329169f79e..dc57b676e5 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs @@ -37,6 +37,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin { public static class BSParam { + private static string LogHeader = "[BULLETSIM PARAMETERS]"; + // Level of Detail values kept as float because that's what the Meshmerizer wants public static float MeshLOD { get; private set; } public static float MeshCircularLOD { get; private set; } @@ -44,6 +46,8 @@ public static class BSParam public static float MeshMegaPrimThreshold { get; private set; } public static float SculptLOD { get; private set; } + public static int CrossingFailuresBeforeOutOfBounds { get; private set; } + public static float MinimumObjectMass { get; private set; } public static float MaximumObjectMass { get; private set; } public static float MaxLinearVelocity { get; private set; } @@ -71,24 +75,23 @@ public static class BSParam public static float TerrainRestitution { get; private set; } public static float TerrainCollisionMargin { get; private set; } - public static float DefaultFriction; - public static float DefaultDensity; - public static float DefaultRestitution; - public static float CollisionMargin; - public static float Gravity; + public static float DefaultFriction { get; private set; } + public static float DefaultDensity { get; private set; } + public static float DefaultRestitution { get; private set; } + public static float CollisionMargin { get; private set; } + public static float Gravity { get; private set; } // Physics Engine operation - public static float MaxPersistantManifoldPoolSize; - public static float MaxCollisionAlgorithmPoolSize; - public static float ShouldDisableContactPoolDynamicAllocation; - public static float ShouldForceUpdateAllAabbs; - public static float ShouldRandomizeSolverOrder; - public static float ShouldSplitSimulationIslands; - public static float ShouldEnableFrictionCaching; - public static float NumberOfSolverIterations; - public static bool UseSingleSidedMeshes { get { return UseSingleSidedMeshesF != ConfigurationParameters.numericFalse; } } - public static float UseSingleSidedMeshesF; - public static float GlobalContactBreakingThreshold; + public static float MaxPersistantManifoldPoolSize { get; private set; } + public static float MaxCollisionAlgorithmPoolSize { get; private set; } + public static bool ShouldDisableContactPoolDynamicAllocation { get; private set; } + public static bool ShouldForceUpdateAllAabbs { get; private set; } + public static bool ShouldRandomizeSolverOrder { get; private set; } + public static bool ShouldSplitSimulationIslands { get; private set; } + public static bool ShouldEnableFrictionCaching { get; private set; } + public static float NumberOfSolverIterations { get; private set; } + public static bool UseSingleSidedMeshes { get; private set; } + public static float GlobalContactBreakingThreshold { get; private set; } // Avatar parameters public static float AvatarFriction { get; private set; } @@ -112,16 +115,15 @@ public static class BSParam public static float VehicleAngularDamping { get; private set; } public static float VehicleFriction { get; private set; } public static float VehicleRestitution { get; private set; } - public static float VehicleLinearFactor { get; private set; } - public static Vector3 VehicleLinearFactorV { get; private set; } - public static float VehicleAngularFactor { get; private set; } - public static Vector3 VehicleAngularFactorV { get; private set; } + public static Vector3 VehicleLinearFactor { get; private set; } + public static Vector3 VehicleAngularFactor { get; private set; } public static float VehicleGroundGravityFudge { get; private set; } - public static float VehicleDebuggingEnabled { get; private set; } + public static bool VehicleDebuggingEnabled { get; private set; } + // Linkset implementation parameters public static float LinksetImplementation { get; private set; } - public static float LinkConstraintUseFrameOffset { get; private set; } - public static float LinkConstraintEnableTransMotor { get; private set; } + public static bool LinkConstraintUseFrameOffset { get; private set; } + public static bool LinkConstraintEnableTransMotor { get; private set; } public static float LinkConstraintTransMotorMaxVel { get; private set; } public static float LinkConstraintTransMotorMaxForce { get; private set; } public static float LinkConstraintERP { get; private set; } @@ -141,40 +143,106 @@ public static class BSParam public const float MinRestitution = 0f; public const float MaxRestitution = 1f; - // =========================================================================== - public delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); - public delegate float ParamGet(BSScene scene); - public delegate void ParamSet(BSScene scene, string paramName, uint localID, float val); - public delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val); + // ===================================================================================== + // ===================================================================================== - public struct ParameterDefn + // Base parameter definition that gets and sets parameter values via a string + public abstract class ParameterDefnBase { public string name; // string name of the parameter public string desc; // a short description of what the parameter means - public float defaultValue; // default value if not specified anywhere else - public ParamUser userParam; // get the value from the configuration file - public ParamGet getter; // return the current value stored for this parameter - public ParamSet setter; // set the current value for this parameter - public SetOnObject onObject; // set the value on an object in the physical domain - public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s) + public ParameterDefnBase(string pName, string pDesc) { - name = n; - desc = d; - defaultValue = v; - userParam = u; - getter = g; - setter = s; - onObject = null; + name = pName; + desc = pDesc; } - public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o) + // Set the parameter value to the default + public abstract void AssignDefault(BSScene s); + // Get the value as a string + public abstract string GetValue(BSScene s); + // Set the value to this string value + public abstract void SetValue(BSScene s, string valAsString); + // set the value on a particular object (usually sets in physics engine) + public abstract void SetOnObject(BSScene s, BSPhysObject obj); + public abstract bool HasSetOnObject { get; } + } + + // Specific parameter definition for a parameter of a specific type. + public delegate T PGetValue(BSScene s); + public delegate void PSetValue(BSScene s, T val); + public delegate void PSetOnObject(BSScene scene, BSPhysObject obj); + public sealed class ParameterDefn : ParameterDefnBase + { + T defaultValue; + PSetValue setter; + PGetValue getter; + PSetOnObject objectSet; + public ParameterDefn(string pName, string pDesc, T pDefault, PGetValue pGetter, PSetValue pSetter) + : base(pName, pDesc) { - name = n; - desc = d; - defaultValue = v; - userParam = u; - getter = g; - setter = s; - onObject = o; + defaultValue = pDefault; + setter = pSetter; + getter = pGetter; + objectSet = null; + } + public ParameterDefn(string pName, string pDesc, T pDefault, PGetValue pGetter, PSetValue pSetter, PSetOnObject pObjSetter) + : base(pName, pDesc) + { + defaultValue = pDefault; + setter = pSetter; + getter = pGetter; + objectSet = pObjSetter; + } + public override void AssignDefault(BSScene s) + { + setter(s, defaultValue); + } + public override string GetValue(BSScene s) + { + return String.Format("{0}", getter(s)); + } + public override void SetValue(BSScene s, string valAsString) + { + // Get the generic type of the setter + Type genericType = setter.GetType().GetGenericArguments()[0]; + // Find the 'Parse' method on that type + System.Reflection.MethodInfo parser = null; + try + { + parser = genericType.GetMethod("Parse", new Type[] { typeof(String) } ); + } + catch (Exception e) + { + s.Logger.ErrorFormat("{0} Exception getting parser for type '{1}': {2}", LogHeader, genericType, e); + parser = null; + } + if (parser != null) + { + // Parse the input string + try + { + T setValue = (T)parser.Invoke(genericType, new Object[] { valAsString }); + setter(s, setValue); + // s.Logger.DebugFormat("{0} Parameter {1} = {2}", LogHeader, name, setValue); + } + catch + { + s.Logger.ErrorFormat("{0} Failed parsing parameter value '{1}' as type '{2}'", LogHeader, valAsString, genericType); + } + } + else + { + s.Logger.ErrorFormat("{0} Could not find parameter parser for type '{1}'", LogHeader, genericType); + } + } + public override bool HasSetOnObject + { + get { return objectSet != null; } + } + public override void SetOnObject(BSScene s, BSPhysObject obj) + { + if (objectSet != null) + objectSet(s, obj); } } @@ -184,462 +252,380 @@ public static class BSParam // location somewhere in the program and make an entry in this table with the // getters and setters. // It is easiest to find an existing definition and copy it. - // Parameter values are floats. Booleans are converted to a floating value. // - // A ParameterDefn() takes the following parameters: + // A ParameterDefn() takes the following parameters: // -- the text name of the parameter. This is used for console input and ini file. // -- a short text description of the parameter. This shows up in the console listing. - // -- a default value (float) - // -- a delegate for fetching the parameter from the ini file. - // Should handle fetching the right type from the ini file and converting it. - // -- a delegate for getting the value as a float - // -- a delegate for setting the value from a float + // -- a default value + // -- a delegate for getting the value + // -- a delegate for setting the value // -- an optional delegate to update the value in the world. Most often used to // push the new value to an in-world object. // // The single letter parameters for the delegates are: // s = BSScene // o = BSPhysObject - // p = string parameter name - // l = localID of referenced object // v = value (float) - // cf = parameter configuration class (for fetching values from ini file) - private static ParameterDefn[] ParameterDefinitions = + private static ParameterDefnBase[] ParameterDefinitions = { - new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", - ConfigurationParameters.numericTrue, - (s,cf,p,v) => { ShouldMeshSculptedPrim = cf.GetBoolean(p, BSParam.BoolNumeric(v)); }, - (s) => { return BSParam.NumericBool(ShouldMeshSculptedPrim); }, - (s,p,l,v) => { ShouldMeshSculptedPrim = BSParam.BoolNumeric(v); } ), - new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects", - ConfigurationParameters.numericFalse, - (s,cf,p,v) => { ShouldForceSimplePrimMeshing = cf.GetBoolean(p, BSParam.BoolNumeric(v)); }, - (s) => { return BSParam.NumericBool(ShouldForceSimplePrimMeshing); }, - (s,p,l,v) => { ShouldForceSimplePrimMeshing = BSParam.BoolNumeric(v); } ), - new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects", - ConfigurationParameters.numericTrue, - (s,cf,p,v) => { ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, BSParam.BoolNumeric(v)); }, - (s) => { return BSParam.NumericBool(ShouldUseHullsForPhysicalObjects); }, - (s,p,l,v) => { ShouldUseHullsForPhysicalObjects = BSParam.BoolNumeric(v); } ), - new ParameterDefn("ShouldRemoveZeroWidthTriangles", "If true, remove degenerate triangles from meshes", - ConfigurationParameters.numericTrue, - (s,cf,p,v) => { ShouldRemoveZeroWidthTriangles = cf.GetBoolean(p, BSParam.BoolNumeric(v)); }, - (s) => { return BSParam.NumericBool(ShouldRemoveZeroWidthTriangles); }, - (s,p,l,v) => { ShouldRemoveZeroWidthTriangles = BSParam.BoolNumeric(v); } ), + new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", + true, + (s) => { return ShouldMeshSculptedPrim; }, + (s,v) => { ShouldMeshSculptedPrim = v; } ), + new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects", + false, + (s) => { return ShouldForceSimplePrimMeshing; }, + (s,v) => { ShouldForceSimplePrimMeshing = v; } ), + new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects", + true, + (s) => { return ShouldUseHullsForPhysicalObjects; }, + (s,v) => { ShouldUseHullsForPhysicalObjects = v; } ), + new ParameterDefn("ShouldRemoveZeroWidthTriangles", "If true, remove degenerate triangles from meshes", + true, + (s) => { return ShouldRemoveZeroWidthTriangles; }, + (s,v) => { ShouldRemoveZeroWidthTriangles = v; } ), - new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", + new ParameterDefn("CrossingFailuresBeforeOutOfBounds", "How forgiving we are about getting into adjactent regions", + 5, + (s) => { return CrossingFailuresBeforeOutOfBounds; }, + (s,v) => { CrossingFailuresBeforeOutOfBounds = v; } ), + + new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", 32f, - (s,cf,p,v) => { MeshLOD = (float)cf.GetInt(p, (int)v); }, (s) => { return MeshLOD; }, - (s,p,l,v) => { MeshLOD = v; } ), - new ParameterDefn("MeshLevelOfDetailCircular", "Level of detail for prims with circular cuts or shapes", + (s,v) => { MeshLOD = v; } ), + new ParameterDefn("MeshLevelOfDetailCircular", "Level of detail for prims with circular cuts or shapes", 32f, - (s,cf,p,v) => { MeshCircularLOD = (float)cf.GetInt(p, (int)v); }, (s) => { return MeshCircularLOD; }, - (s,p,l,v) => { MeshCircularLOD = v; } ), - new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD", + (s,v) => { MeshCircularLOD = v; } ), + new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD", 10f, - (s,cf,p,v) => { MeshMegaPrimThreshold = (float)cf.GetInt(p, (int)v); }, (s) => { return MeshMegaPrimThreshold; }, - (s,p,l,v) => { MeshMegaPrimThreshold = v; } ), - new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters", + (s,v) => { MeshMegaPrimThreshold = v; } ), + new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters", 32f, - (s,cf,p,v) => { MeshMegaPrimLOD = (float)cf.GetInt(p, (int)v); }, (s) => { return MeshMegaPrimLOD; }, - (s,p,l,v) => { MeshMegaPrimLOD = v; } ), - new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)", + (s,v) => { MeshMegaPrimLOD = v; } ), + new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)", 32f, - (s,cf,p,v) => { SculptLOD = (float)cf.GetInt(p, (int)v); }, (s) => { return SculptLOD; }, - (s,p,l,v) => { SculptLOD = v; } ), + (s,v) => { SculptLOD = v; } ), - new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps", - 10f, - (s,cf,p,v) => { s.m_maxSubSteps = cf.GetInt(p, (int)v); }, - (s) => { return (float)s.m_maxSubSteps; }, - (s,p,l,v) => { s.m_maxSubSteps = (int)v; } ), - new ParameterDefn("FixedTimeStep", "In simulation step, seconds of one substep (1/60)", + new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps", + 10, + (s) => { return s.m_maxSubSteps; }, + (s,v) => { s.m_maxSubSteps = (int)v; } ), + new ParameterDefn("FixedTimeStep", "In simulation step, seconds of one substep (1/60)", 1f / 60f, - (s,cf,p,v) => { s.m_fixedTimeStep = cf.GetFloat(p, v); }, - (s) => { return (float)s.m_fixedTimeStep; }, - (s,p,l,v) => { s.m_fixedTimeStep = v; } ), - new ParameterDefn("NominalFrameRate", "The base frame rate we claim", + (s) => { return s.m_fixedTimeStep; }, + (s,v) => { s.m_fixedTimeStep = v; } ), + new ParameterDefn("NominalFrameRate", "The base frame rate we claim", 55f, - (s,cf,p,v) => { s.NominalFrameRate = cf.GetInt(p, (int)v); }, - (s) => { return (float)s.NominalFrameRate; }, - (s,p,l,v) => { s.NominalFrameRate = (int)v; } ), - new ParameterDefn("MaxCollisionsPerFrame", "Max collisions returned at end of each frame", - 2048f, - (s,cf,p,v) => { s.m_maxCollisionsPerFrame = cf.GetInt(p, (int)v); }, - (s) => { return (float)s.m_maxCollisionsPerFrame; }, - (s,p,l,v) => { s.m_maxCollisionsPerFrame = (int)v; } ), - new ParameterDefn("MaxUpdatesPerFrame", "Max updates returned at end of each frame", - 8000f, - (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); }, - (s) => { return (float)s.m_maxUpdatesPerFrame; }, - (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), + (s) => { return s.NominalFrameRate; }, + (s,v) => { s.NominalFrameRate = (int)v; } ), + new ParameterDefn("MaxCollisionsPerFrame", "Max collisions returned at end of each frame", + 2048, + (s) => { return s.m_maxCollisionsPerFrame; }, + (s,v) => { s.m_maxCollisionsPerFrame = (int)v; } ), + new ParameterDefn("MaxUpdatesPerFrame", "Max updates returned at end of each frame", + 8000, + (s) => { return s.m_maxUpdatesPerFrame; }, + (s,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), - new ParameterDefn("MinObjectMass", "Minimum object mass (0.0001)", + new ParameterDefn("MinObjectMass", "Minimum object mass (0.0001)", 0.0001f, - (s,cf,p,v) => { MinimumObjectMass = cf.GetFloat(p, v); }, (s) => { return MinimumObjectMass; }, - (s,p,l,v) => { MinimumObjectMass = v; } ), - new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", + (s,v) => { MinimumObjectMass = v; } ), + new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", 10000.01f, - (s,cf,p,v) => { MaximumObjectMass = cf.GetFloat(p, v); }, (s) => { return MaximumObjectMass; }, - (s,p,l,v) => { MaximumObjectMass = v; } ), - new ParameterDefn("MaxLinearVelocity", "Maximum velocity magnitude that can be assigned to an object", + (s,v) => { MaximumObjectMass = v; } ), + new ParameterDefn("MaxLinearVelocity", "Maximum velocity magnitude that can be assigned to an object", 1000.0f, - (s,cf,p,v) => { MaxLinearVelocity = cf.GetFloat(p, v); }, (s) => { return MaxLinearVelocity; }, - (s,p,l,v) => { MaxLinearVelocity = v; } ), - new ParameterDefn("MaxAngularVelocity", "Maximum rotational velocity magnitude that can be assigned to an object", + (s,v) => { MaxLinearVelocity = v; } ), + new ParameterDefn("MaxAngularVelocity", "Maximum rotational velocity magnitude that can be assigned to an object", 1000.0f, - (s,cf,p,v) => { MaxAngularVelocity = cf.GetFloat(p, v); }, (s) => { return MaxAngularVelocity; }, - (s,p,l,v) => { MaxAngularVelocity = v; } ), + (s,v) => { MaxAngularVelocity = v; } ), // LL documentation says thie number should be 20f for llApplyImpulse and 200f for llRezObject - new ParameterDefn("MaxAddForceMagnitude", "Maximum force that can be applied by llApplyImpulse (SL says 20f)", + new ParameterDefn("MaxAddForceMagnitude", "Maximum force that can be applied by llApplyImpulse (SL says 20f)", 20000.0f, - (s,cf,p,v) => { MaxAddForceMagnitude = cf.GetFloat(p, v); }, (s) => { return MaxAddForceMagnitude; }, - (s,p,l,v) => { MaxAddForceMagnitude = v; } ), + (s,v) => { MaxAddForceMagnitude = v; } ), // Density is passed around as 100kg/m3. This scales that to 1kg/m3. - new ParameterDefn("DensityScaleFactor", "Conversion for simulator/viewer density (100kg/m3) to physical density (1kg/m3)", + new ParameterDefn("DensityScaleFactor", "Conversion for simulator/viewer density (100kg/m3) to physical density (1kg/m3)", 0.01f, - (s,cf,p,v) => { DensityScaleFactor = cf.GetFloat(p, v); }, (s) => { return DensityScaleFactor; }, - (s,p,l,v) => { DensityScaleFactor = v; } ), + (s,v) => { DensityScaleFactor = v; } ), - new ParameterDefn("PID_D", "Derivitive factor for motion smoothing", + new ParameterDefn("PID_D", "Derivitive factor for motion smoothing", 2200f, - (s,cf,p,v) => { PID_D = cf.GetFloat(p, v); }, (s) => { return (float)PID_D; }, - (s,p,l,v) => { PID_D = v; } ), - new ParameterDefn("PID_P", "Parameteric factor for motion smoothing", + (s,v) => { PID_D = v; } ), + new ParameterDefn("PID_P", "Parameteric factor for motion smoothing", 900f, - (s,cf,p,v) => { PID_P = cf.GetFloat(p, v); }, (s) => { return (float)PID_P; }, - (s,p,l,v) => { PID_P = v; } ), + (s,v) => { PID_P = v; } ), - new ParameterDefn("DefaultFriction", "Friction factor used on new objects", + new ParameterDefn("DefaultFriction", "Friction factor used on new objects", 0.2f, - (s,cf,p,v) => { DefaultFriction = cf.GetFloat(p, v); }, (s) => { return DefaultFriction; }, - (s,p,l,v) => { DefaultFriction = v; s.UnmanagedParams[0].defaultFriction = v; } ), - new ParameterDefn("DefaultDensity", "Density for new objects" , + (s,v) => { DefaultFriction = v; s.UnmanagedParams[0].defaultFriction = v; } ), + new ParameterDefn("DefaultDensity", "Density for new objects" , 10.000006836f, // Aluminum g/cm3 - (s,cf,p,v) => { DefaultDensity = cf.GetFloat(p, v); }, (s) => { return DefaultDensity; }, - (s,p,l,v) => { DefaultDensity = v; s.UnmanagedParams[0].defaultDensity = v; } ), - new ParameterDefn("DefaultRestitution", "Bouncyness of an object" , + (s,v) => { DefaultDensity = v; s.UnmanagedParams[0].defaultDensity = v; } ), + new ParameterDefn("DefaultRestitution", "Bouncyness of an object" , 0f, - (s,cf,p,v) => { DefaultRestitution = cf.GetFloat(p, v); }, (s) => { return DefaultRestitution; }, - (s,p,l,v) => { DefaultRestitution = v; s.UnmanagedParams[0].defaultRestitution = v; } ), - new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)", + (s,v) => { DefaultRestitution = v; s.UnmanagedParams[0].defaultRestitution = v; } ), + new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)", 0.04f, - (s,cf,p,v) => { CollisionMargin = cf.GetFloat(p, v); }, (s) => { return CollisionMargin; }, - (s,p,l,v) => { CollisionMargin = v; s.UnmanagedParams[0].collisionMargin = v; } ), - new ParameterDefn("Gravity", "Vertical force of gravity (negative means down)", + (s,v) => { CollisionMargin = v; s.UnmanagedParams[0].collisionMargin = v; } ), + new ParameterDefn("Gravity", "Vertical force of gravity (negative means down)", -9.80665f, - (s,cf,p,v) => { Gravity = cf.GetFloat(p, v); }, (s) => { return Gravity; }, - (s,p,l,v) => { Gravity = v; s.UnmanagedParams[0].gravity = v; }, - (s,o,v) => { s.PE.SetGravity(o.PhysBody, new Vector3(0f,0f,v)); } ), + (s,v) => { Gravity = v; s.UnmanagedParams[0].gravity = v; }, + (s,o) => { s.PE.SetGravity(o.PhysBody, new Vector3(0f,0f,Gravity)); } ), - new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)", + new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)", 0f, - (s,cf,p,v) => { LinearDamping = cf.GetFloat(p, v); }, (s) => { return LinearDamping; }, - (s,p,l,v) => { LinearDamping = v; }, - (s,o,v) => { s.PE.SetDamping(o.PhysBody, v, AngularDamping); } ), - new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", + (s,v) => { LinearDamping = v; }, + (s,o) => { s.PE.SetDamping(o.PhysBody, LinearDamping, AngularDamping); } ), + new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", 0f, - (s,cf,p,v) => { AngularDamping = cf.GetFloat(p, v); }, (s) => { return AngularDamping; }, - (s,p,l,v) => { AngularDamping = v; }, - (s,o,v) => { s.PE.SetDamping(o.PhysBody, LinearDamping, v); } ), - new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", + (s,v) => { AngularDamping = v; }, + (s,o) => { s.PE.SetDamping(o.PhysBody, LinearDamping, AngularDamping); } ), + new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", 0.2f, - (s,cf,p,v) => { DeactivationTime = cf.GetFloat(p, v); }, (s) => { return DeactivationTime; }, - (s,p,l,v) => { DeactivationTime = v; }, - (s,o,v) => { s.PE.SetDeactivationTime(o.PhysBody, v); } ), - new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", + (s,v) => { DeactivationTime = v; }, + (s,o) => { s.PE.SetDeactivationTime(o.PhysBody, DeactivationTime); } ), + new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", 0.8f, - (s,cf,p,v) => { LinearSleepingThreshold = cf.GetFloat(p, v); }, (s) => { return LinearSleepingThreshold; }, - (s,p,l,v) => { LinearSleepingThreshold = v;}, - (s,o,v) => { s.PE.SetSleepingThresholds(o.PhysBody, v, v); } ), - new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", + (s,v) => { LinearSleepingThreshold = v;}, + (s,o) => { s.PE.SetSleepingThresholds(o.PhysBody, LinearSleepingThreshold, AngularSleepingThreshold); } ), + new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", 1.0f, - (s,cf,p,v) => { AngularSleepingThreshold = cf.GetFloat(p, v); }, (s) => { return AngularSleepingThreshold; }, - (s,p,l,v) => { AngularSleepingThreshold = v;}, - (s,o,v) => { s.PE.SetSleepingThresholds(o.PhysBody, v, v); } ), - new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , + (s,v) => { AngularSleepingThreshold = v;}, + (s,o) => { s.PE.SetSleepingThresholds(o.PhysBody, LinearSleepingThreshold, AngularSleepingThreshold); } ), + new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , 0.0f, // set to zero to disable - (s,cf,p,v) => { CcdMotionThreshold = cf.GetFloat(p, v); }, (s) => { return CcdMotionThreshold; }, - (s,p,l,v) => { CcdMotionThreshold = v;}, - (s,o,v) => { s.PE.SetCcdMotionThreshold(o.PhysBody, v); } ), - new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , + (s,v) => { CcdMotionThreshold = v;}, + (s,o) => { s.PE.SetCcdMotionThreshold(o.PhysBody, CcdMotionThreshold); } ), + new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , 0.2f, - (s,cf,p,v) => { CcdSweptSphereRadius = cf.GetFloat(p, v); }, (s) => { return CcdSweptSphereRadius; }, - (s,p,l,v) => { CcdSweptSphereRadius = v;}, - (s,o,v) => { s.PE.SetCcdSweptSphereRadius(o.PhysBody, v); } ), - new ParameterDefn("ContactProcessingThreshold", "Distance above which contacts can be discarded (0 means no discard)" , + (s,v) => { CcdSweptSphereRadius = v;}, + (s,o) => { s.PE.SetCcdSweptSphereRadius(o.PhysBody, CcdSweptSphereRadius); } ), + new ParameterDefn("ContactProcessingThreshold", "Distance above which contacts can be discarded (0 means no discard)" , 0.0f, - (s,cf,p,v) => { ContactProcessingThreshold = cf.GetFloat(p, v); }, (s) => { return ContactProcessingThreshold; }, - (s,p,l,v) => { ContactProcessingThreshold = v;}, - (s,o,v) => { s.PE.SetContactProcessingThreshold(o.PhysBody, v); } ), + (s,v) => { ContactProcessingThreshold = v;}, + (s,o) => { s.PE.SetContactProcessingThreshold(o.PhysBody, ContactProcessingThreshold); } ), - new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)", + new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)", (float)BSTerrainPhys.TerrainImplementation.Mesh, - (s,cf,p,v) => { TerrainImplementation = cf.GetFloat(p,v); }, (s) => { return TerrainImplementation; }, - (s,p,l,v) => { TerrainImplementation = v; } ), - new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , + (s,v) => { TerrainImplementation = v; } ), + new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , 0.3f, - (s,cf,p,v) => { TerrainFriction = cf.GetFloat(p, v); }, (s) => { return TerrainFriction; }, - (s,p,l,v) => { TerrainFriction = v; /* TODO: set on real terrain */} ), - new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" , + (s,v) => { TerrainFriction = v; /* TODO: set on real terrain */} ), + new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" , 0.8f, - (s,cf,p,v) => { TerrainHitFraction = cf.GetFloat(p, v); }, (s) => { return TerrainHitFraction; }, - (s,p,l,v) => { TerrainHitFraction = v; /* TODO: set on real terrain */ } ), - new ParameterDefn("TerrainRestitution", "Bouncyness" , + (s,v) => { TerrainHitFraction = v; /* TODO: set on real terrain */ } ), + new ParameterDefn("TerrainRestitution", "Bouncyness" , 0f, - (s,cf,p,v) => { TerrainRestitution = cf.GetFloat(p, v); }, (s) => { return TerrainRestitution; }, - (s,p,l,v) => { TerrainRestitution = v; /* TODO: set on real terrain */ } ), - new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" , + (s,v) => { TerrainRestitution = v; /* TODO: set on real terrain */ } ), + new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" , 0.08f, - (s,cf,p,v) => { TerrainCollisionMargin = cf.GetFloat(p, v); }, (s) => { return TerrainCollisionMargin; }, - (s,p,l,v) => { TerrainCollisionMargin = v; /* TODO: set on real terrain */ } ), + (s,v) => { TerrainCollisionMargin = v; /* TODO: set on real terrain */ } ), - new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", + new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", 0.2f, - (s,cf,p,v) => { AvatarFriction = cf.GetFloat(p, v); }, (s) => { return AvatarFriction; }, - (s,p,l,v) => { AvatarFriction = v; } ), - new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.", + (s,v) => { AvatarFriction = v; } ), + new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.", 0.95f, - (s,cf,p,v) => { AvatarStandingFriction = cf.GetFloat(p, v); }, (s) => { return AvatarStandingFriction; }, - (s,p,l,v) => { AvatarStandingFriction = v; } ), - new ParameterDefn("AvatarAlwaysRunFactor", "Speed multiplier if avatar is set to always run", + (s,v) => { AvatarStandingFriction = v; } ), + new ParameterDefn("AvatarAlwaysRunFactor", "Speed multiplier if avatar is set to always run", 1.3f, - (s,cf,p,v) => { AvatarAlwaysRunFactor = cf.GetFloat(p, v); }, (s) => { return AvatarAlwaysRunFactor; }, - (s,p,l,v) => { AvatarAlwaysRunFactor = v; } ), - new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.", + (s,v) => { AvatarAlwaysRunFactor = v; } ), + new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.", 3.5f, - (s,cf,p,v) => { AvatarDensity = cf.GetFloat(p, v); }, (s) => { return AvatarDensity; }, - (s,p,l,v) => { AvatarDensity = v; } ), - new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.", + (s,v) => { AvatarDensity = v; } ), + new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.", 0f, - (s,cf,p,v) => { AvatarRestitution = cf.GetFloat(p, v); }, (s) => { return AvatarRestitution; }, - (s,p,l,v) => { AvatarRestitution = v; } ), - new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule", + (s,v) => { AvatarRestitution = v; } ), + new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule", 0.6f, - (s,cf,p,v) => { AvatarCapsuleWidth = cf.GetFloat(p, v); }, (s) => { return AvatarCapsuleWidth; }, - (s,p,l,v) => { AvatarCapsuleWidth = v; } ), - new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule", + (s,v) => { AvatarCapsuleWidth = v; } ), + new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule", 0.45f, - (s,cf,p,v) => { AvatarCapsuleDepth = cf.GetFloat(p, v); }, (s) => { return AvatarCapsuleDepth; }, - (s,p,l,v) => { AvatarCapsuleDepth = v; } ), - new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar", + (s,v) => { AvatarCapsuleDepth = v; } ), + new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar", 1.5f, - (s,cf,p,v) => { AvatarCapsuleHeight = cf.GetFloat(p, v); }, (s) => { return AvatarCapsuleHeight; }, - (s,p,l,v) => { AvatarCapsuleHeight = v; } ), - new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions", + (s,v) => { AvatarCapsuleHeight = v; } ), + new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions", 0.1f, - (s,cf,p,v) => { AvatarContactProcessingThreshold = cf.GetFloat(p, v); }, (s) => { return AvatarContactProcessingThreshold; }, - (s,p,l,v) => { AvatarContactProcessingThreshold = v; } ), - new ParameterDefn("AvatarStepHeight", "Height of a step obstacle to consider step correction", + (s,v) => { AvatarContactProcessingThreshold = v; } ), + new ParameterDefn("AvatarStepHeight", "Height of a step obstacle to consider step correction", 0.3f, - (s,cf,p,v) => { AvatarStepHeight = cf.GetFloat(p, v); }, (s) => { return AvatarStepHeight; }, - (s,p,l,v) => { AvatarStepHeight = v; } ), - new ParameterDefn("AvatarStepApproachFactor", "Factor to control angle of approach to step (0=straight on)", + (s,v) => { AvatarStepHeight = v; } ), + new ParameterDefn("AvatarStepApproachFactor", "Factor to control angle of approach to step (0=straight on)", 0.6f, - (s,cf,p,v) => { AvatarStepApproachFactor = cf.GetFloat(p, v); }, (s) => { return AvatarStepApproachFactor; }, - (s,p,l,v) => { AvatarStepApproachFactor = v; } ), - new ParameterDefn("AvatarStepForceFactor", "Controls the amount of force up applied to step up onto a step", + (s,v) => { AvatarStepApproachFactor = v; } ), + new ParameterDefn("AvatarStepForceFactor", "Controls the amount of force up applied to step up onto a step", 2.0f, - (s,cf,p,v) => { AvatarStepForceFactor = cf.GetFloat(p, v); }, (s) => { return AvatarStepForceFactor; }, - (s,p,l,v) => { AvatarStepForceFactor = v; } ), + (s,v) => { AvatarStepForceFactor = v; } ), - new ParameterDefn("VehicleMaxLinearVelocity", "Maximum velocity magnitude that can be assigned to a vehicle", + new ParameterDefn("VehicleMaxLinearVelocity", "Maximum velocity magnitude that can be assigned to a vehicle", 1000.0f, - (s,cf,p,v) => { VehicleMaxLinearVelocity = cf.GetFloat(p, v); }, (s) => { return (float)VehicleMaxLinearVelocity; }, - (s,p,l,v) => { VehicleMaxLinearVelocity = v; VehicleMaxLinearVelocitySq = v * v; } ), - new ParameterDefn("VehicleMaxAngularVelocity", "Maximum rotational velocity magnitude that can be assigned to a vehicle", + (s,v) => { VehicleMaxLinearVelocity = v; VehicleMaxLinearVelocitySq = v * v; } ), + new ParameterDefn("VehicleMaxAngularVelocity", "Maximum rotational velocity magnitude that can be assigned to a vehicle", 12.0f, - (s,cf,p,v) => { VehicleMaxAngularVelocity = cf.GetFloat(p, v); }, (s) => { return (float)VehicleMaxAngularVelocity; }, - (s,p,l,v) => { VehicleMaxAngularVelocity = v; VehicleMaxAngularVelocitySq = v * v; } ), - new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)", + (s,v) => { VehicleMaxAngularVelocity = v; VehicleMaxAngularVelocitySq = v * v; } ), + new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)", 0.0f, - (s,cf,p,v) => { VehicleAngularDamping = cf.GetFloat(p, v); }, (s) => { return VehicleAngularDamping; }, - (s,p,l,v) => { VehicleAngularDamping = v; } ), - new ParameterDefn("VehicleLinearFactor", "Fraction of physical linear changes applied to vehicle (0.0 - 1.0)", - 1.0f, - (s,cf,p,v) => { VehicleLinearFactor = cf.GetFloat(p, v); }, + (s,v) => { VehicleAngularDamping = v; } ), + new ParameterDefn("VehicleLinearFactor", "Fraction of physical linear changes applied to vehicle (<0,0,0> to <1,1,1>)", + new Vector3(1f, 1f, 1f), (s) => { return VehicleLinearFactor; }, - (s,p,l,v) => { VehicleLinearFactor = v; VehicleLinearFactorV = new Vector3(v, v, v); } ), - new ParameterDefn("VehicleAngularFactor", "Fraction of physical angular changes applied to vehicle (0.0 - 1.0)", - 1.0f, - (s,cf,p,v) => { VehicleAngularFactor = cf.GetFloat(p, v); }, + (s,v) => { VehicleLinearFactor = v; } ), + new ParameterDefn("VehicleAngularFactor", "Fraction of physical angular changes applied to vehicle (<0,0,0> to <1,1,1>)", + new Vector3(1f, 1f, 1f), (s) => { return VehicleAngularFactor; }, - (s,p,l,v) => { VehicleAngularFactor = v; VehicleAngularFactorV = new Vector3(v, v, v); } ), - new ParameterDefn("VehicleFriction", "Friction of vehicle on the ground (0.0 - 1.0)", + (s,v) => { VehicleAngularFactor = v; } ), + new ParameterDefn("VehicleFriction", "Friction of vehicle on the ground (0.0 - 1.0)", 0.0f, - (s,cf,p,v) => { VehicleFriction = cf.GetFloat(p, v); }, (s) => { return VehicleFriction; }, - (s,p,l,v) => { VehicleFriction = v; } ), - new ParameterDefn("VehicleRestitution", "Bouncyness factor for vehicles (0.0 - 1.0)", + (s,v) => { VehicleFriction = v; } ), + new ParameterDefn("VehicleRestitution", "Bouncyness factor for vehicles (0.0 - 1.0)", 0.0f, - (s,cf,p,v) => { VehicleRestitution = cf.GetFloat(p, v); }, (s) => { return VehicleRestitution; }, - (s,p,l,v) => { VehicleRestitution = v; } ), - new ParameterDefn("VehicleGroundGravityFudge", "Factor to multiple gravity if a ground vehicle is probably on the ground (0.0 - 1.0)", + (s,v) => { VehicleRestitution = v; } ), + new ParameterDefn("VehicleGroundGravityFudge", "Factor to multiple gravity if a ground vehicle is probably on the ground (0.0 - 1.0)", 0.2f, - (s,cf,p,v) => { VehicleGroundGravityFudge = cf.GetFloat(p, v); }, (s) => { return VehicleGroundGravityFudge; }, - (s,p,l,v) => { VehicleGroundGravityFudge = v; } ), - new ParameterDefn("VehicleDebuggingEnable", "Turn on/off vehicle debugging", - ConfigurationParameters.numericFalse, - (s,cf,p,v) => { VehicleDebuggingEnabled = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s,v) => { VehicleGroundGravityFudge = v; } ), + new ParameterDefn("VehicleDebuggingEnable", "Turn on/off vehicle debugging", + false, (s) => { return VehicleDebuggingEnabled; }, - (s,p,l,v) => { VehicleDebuggingEnabled = v; } ), + (s,v) => { VehicleDebuggingEnabled = v; } ), - new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", + new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", 0f, - (s,cf,p,v) => { MaxPersistantManifoldPoolSize = cf.GetFloat(p, v); }, (s) => { return MaxPersistantManifoldPoolSize; }, - (s,p,l,v) => { MaxPersistantManifoldPoolSize = v; s.UnmanagedParams[0].maxPersistantManifoldPoolSize = v; } ), - new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)", + (s,v) => { MaxPersistantManifoldPoolSize = v; s.UnmanagedParams[0].maxPersistantManifoldPoolSize = v; } ), + new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)", 0f, - (s,cf,p,v) => { MaxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); }, (s) => { return MaxCollisionAlgorithmPoolSize; }, - (s,p,l,v) => { MaxCollisionAlgorithmPoolSize = v; s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = v; } ), - new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count", - ConfigurationParameters.numericFalse, - (s,cf,p,v) => { ShouldDisableContactPoolDynamicAllocation = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s,v) => { MaxCollisionAlgorithmPoolSize = v; s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = v; } ), + new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count", + false, (s) => { return ShouldDisableContactPoolDynamicAllocation; }, - (s,p,l,v) => { ShouldDisableContactPoolDynamicAllocation = v; s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = v; } ), - new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step", - ConfigurationParameters.numericFalse, - (s,cf,p,v) => { ShouldForceUpdateAllAabbs = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s,v) => { ShouldDisableContactPoolDynamicAllocation = v; + s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = NumericBool(v); } ), + new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step", + false, (s) => { return ShouldForceUpdateAllAabbs; }, - (s,p,l,v) => { ShouldForceUpdateAllAabbs = v; s.UnmanagedParams[0].shouldForceUpdateAllAabbs = v; } ), - new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction", - ConfigurationParameters.numericTrue, - (s,cf,p,v) => { ShouldRandomizeSolverOrder = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s,v) => { ShouldForceUpdateAllAabbs = v; s.UnmanagedParams[0].shouldForceUpdateAllAabbs = NumericBool(v); } ), + new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction", + true, (s) => { return ShouldRandomizeSolverOrder; }, - (s,p,l,v) => { ShouldRandomizeSolverOrder = v; s.UnmanagedParams[0].shouldRandomizeSolverOrder = v; } ), - new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands", - ConfigurationParameters.numericTrue, - (s,cf,p,v) => { ShouldSplitSimulationIslands = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s,v) => { ShouldRandomizeSolverOrder = v; s.UnmanagedParams[0].shouldRandomizeSolverOrder = NumericBool(v); } ), + new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands", + true, (s) => { return ShouldSplitSimulationIslands; }, - (s,p,l,v) => { ShouldSplitSimulationIslands = v; s.UnmanagedParams[0].shouldSplitSimulationIslands = v; } ), - new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching", - ConfigurationParameters.numericTrue, - (s,cf,p,v) => { ShouldEnableFrictionCaching = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s,v) => { ShouldSplitSimulationIslands = v; s.UnmanagedParams[0].shouldSplitSimulationIslands = NumericBool(v); } ), + new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching", + true, (s) => { return ShouldEnableFrictionCaching; }, - (s,p,l,v) => { ShouldEnableFrictionCaching = v; s.UnmanagedParams[0].shouldEnableFrictionCaching = v; } ), - new ParameterDefn("NumberOfSolverIterations", "Number of internal iterations (0 means default)", + (s,v) => { ShouldEnableFrictionCaching = v; s.UnmanagedParams[0].shouldEnableFrictionCaching = NumericBool(v); } ), + new ParameterDefn("NumberOfSolverIterations", "Number of internal iterations (0 means default)", 0f, // zero says use Bullet default - (s,cf,p,v) => { NumberOfSolverIterations = cf.GetFloat(p, v); }, (s) => { return NumberOfSolverIterations; }, - (s,p,l,v) => { NumberOfSolverIterations = v; s.UnmanagedParams[0].numberOfSolverIterations = v; } ), - new ParameterDefn("UseSingleSidedMeshes", "Whether to compute collisions based on single sided meshes.", - ConfigurationParameters.numericTrue, - (s,cf,p,v) => { UseSingleSidedMeshesF = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, - (s) => { return UseSingleSidedMeshesF; }, - (s,p,l,v) => { UseSingleSidedMeshesF = v; s.UnmanagedParams[0].useSingleSidedMeshes = v; } ), - new ParameterDefn("GlobalContactBreakingThreshold", "Amount of shape radius before breaking a collision contact (0 says Bullet default (0.2))", + (s,v) => { NumberOfSolverIterations = v; s.UnmanagedParams[0].numberOfSolverIterations = v; } ), + new ParameterDefn("UseSingleSidedMeshes", "Whether to compute collisions based on single sided meshes.", + true, + (s) => { return UseSingleSidedMeshes; }, + (s,v) => { UseSingleSidedMeshes = v; s.UnmanagedParams[0].useSingleSidedMeshes = NumericBool(v); } ), + new ParameterDefn("GlobalContactBreakingThreshold", "Amount of shape radius before breaking a collision contact (0 says Bullet default (0.2))", 0f, - (s,cf,p,v) => { GlobalContactBreakingThreshold = cf.GetFloat(p, v); }, (s) => { return GlobalContactBreakingThreshold; }, - (s,p,l,v) => { GlobalContactBreakingThreshold = v; s.UnmanagedParams[0].globalContactBreakingThreshold = v; } ), + (s,v) => { GlobalContactBreakingThreshold = v; s.UnmanagedParams[0].globalContactBreakingThreshold = v; } ), - new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)", + new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)", (float)BSLinkset.LinksetImplementation.Compound, - (s,cf,p,v) => { LinksetImplementation = cf.GetFloat(p,v); }, (s) => { return LinksetImplementation; }, - (s,p,l,v) => { LinksetImplementation = v; } ), - new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.", - ConfigurationParameters.numericFalse, - (s,cf,p,v) => { LinkConstraintUseFrameOffset = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s,v) => { LinksetImplementation = v; } ), + new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.", + false, (s) => { return LinkConstraintUseFrameOffset; }, - (s,p,l,v) => { LinkConstraintUseFrameOffset = v; } ), - new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints", - ConfigurationParameters.numericTrue, - (s,cf,p,v) => { LinkConstraintEnableTransMotor = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s,v) => { LinkConstraintUseFrameOffset = v; } ), + new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints", + true, (s) => { return LinkConstraintEnableTransMotor; }, - (s,p,l,v) => { LinkConstraintEnableTransMotor = v; } ), - new ParameterDefn("LinkConstraintTransMotorMaxVel", "Maximum velocity to be applied by translational motor in linkset constraints", + (s,v) => { LinkConstraintEnableTransMotor = v; } ), + new ParameterDefn("LinkConstraintTransMotorMaxVel", "Maximum velocity to be applied by translational motor in linkset constraints", 5.0f, - (s,cf,p,v) => { LinkConstraintTransMotorMaxVel = cf.GetFloat(p, v); }, (s) => { return LinkConstraintTransMotorMaxVel; }, - (s,p,l,v) => { LinkConstraintTransMotorMaxVel = v; } ), - new ParameterDefn("LinkConstraintTransMotorMaxForce", "Maximum force to be applied by translational motor in linkset constraints", + (s,v) => { LinkConstraintTransMotorMaxVel = v; } ), + new ParameterDefn("LinkConstraintTransMotorMaxForce", "Maximum force to be applied by translational motor in linkset constraints", 0.1f, - (s,cf,p,v) => { LinkConstraintTransMotorMaxForce = cf.GetFloat(p, v); }, (s) => { return LinkConstraintTransMotorMaxForce; }, - (s,p,l,v) => { LinkConstraintTransMotorMaxForce = v; } ), - new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1", + (s,v) => { LinkConstraintTransMotorMaxForce = v; } ), + new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1", 0.1f, - (s,cf,p,v) => { LinkConstraintCFM = cf.GetFloat(p, v); }, (s) => { return LinkConstraintCFM; }, - (s,p,l,v) => { LinkConstraintCFM = v; } ), - new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2", + (s,v) => { LinkConstraintCFM = v; } ), + new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2", 0.1f, - (s,cf,p,v) => { LinkConstraintERP = cf.GetFloat(p, v); }, (s) => { return LinkConstraintERP; }, - (s,p,l,v) => { LinkConstraintERP = v; } ), - new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)", + (s,v) => { LinkConstraintERP = v; } ), + new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)", 40, - (s,cf,p,v) => { LinkConstraintSolverIterations = cf.GetFloat(p, v); }, (s) => { return LinkConstraintSolverIterations; }, - (s,p,l,v) => { LinkConstraintSolverIterations = v; } ), + (s,v) => { LinkConstraintSolverIterations = v; } ), - new ParameterDefn("PhysicsMetricFrames", "Frames between outputting detailed phys metrics. (0 is off)", + new ParameterDefn("PhysicsMetricFrames", "Frames between outputting detailed phys metrics. (0 is off)", + 0, + (s) => { return s.PhysicsMetricDumpFrames; }, + (s,v) => { s.PhysicsMetricDumpFrames = v; } ), + new ParameterDefn("ResetBroadphasePool", "Setting this is any value resets the broadphase collision pool", 0f, - (s,cf,p,v) => { s.PhysicsMetricDumpFrames = cf.GetFloat(p, (int)v); }, - (s) => { return (float)s.PhysicsMetricDumpFrames; }, - (s,p,l,v) => { s.PhysicsMetricDumpFrames = (int)v; } ), - new ParameterDefn("ResetBroadphasePool", "Setting this is any value resets the broadphase collision pool", - 0f, - (s,cf,p,v) => { ; }, (s) => { return 0f; }, - (s,p,l,v) => { BSParam.ResetBroadphasePoolTainted(s, v); } ), - new ParameterDefn("ResetConstraintSolver", "Setting this is any value resets the constraint solver", + (s,v) => { BSParam.ResetBroadphasePoolTainted(s, v); } ), + new ParameterDefn("ResetConstraintSolver", "Setting this is any value resets the constraint solver", 0f, - (s,cf,p,v) => { ; }, (s) => { return 0f; }, - (s,p,l,v) => { BSParam.ResetConstraintSolverTainted(s, v); } ), + (s,v) => { BSParam.ResetConstraintSolverTainted(s, v); } ), }; // Convert a boolean to our numeric true and false values @@ -658,13 +644,13 @@ public static class BSParam // ParameterDefn structure. // Case does not matter as names are compared after converting to lower case. // Returns 'false' if the parameter is not found. - internal static bool TryGetParameter(string paramName, out ParameterDefn defn) + internal static bool TryGetParameter(string paramName, out ParameterDefnBase defn) { bool ret = false; - ParameterDefn foundDefn = new ParameterDefn(); + ParameterDefnBase foundDefn = null; string pName = paramName.ToLower(); - foreach (ParameterDefn parm in ParameterDefinitions) + foreach (ParameterDefnBase parm in ParameterDefinitions) { if (pName == parm.name.ToLower()) { @@ -680,18 +666,18 @@ public static class BSParam // Pass through the settable parameters and set the default values internal static void SetParameterDefaultValues(BSScene physicsScene) { - foreach (ParameterDefn parm in ParameterDefinitions) + foreach (ParameterDefnBase parm in ParameterDefinitions) { - parm.setter(physicsScene, parm.name, PhysParameterEntry.APPLY_TO_NONE, parm.defaultValue); + parm.AssignDefault(physicsScene); } } // Get user set values out of the ini file. internal static void SetParameterConfigurationValues(BSScene physicsScene, IConfig cfg) { - foreach (ParameterDefn parm in ParameterDefinitions) + foreach (ParameterDefnBase parm in ParameterDefinitions) { - parm.userParam(physicsScene, cfg, parm.name, parm.defaultValue); + parm.SetValue(physicsScene, cfg.GetString(parm.name, parm.GetValue(physicsScene))); } } @@ -706,17 +692,21 @@ public static class BSParam List entries = new List(); for (int ii = 0; ii < ParameterDefinitions.Length; ii++) { - ParameterDefn pd = ParameterDefinitions[ii]; + ParameterDefnBase pd = ParameterDefinitions[ii]; entries.Add(new PhysParameterEntry(pd.name, pd.desc)); } - // make the list alphabetical for estetic reasons + // make the list alphabetical for ease of finding anything entries.Sort((ppe1, ppe2) => { return ppe1.name.CompareTo(ppe2.name); }); SettableParameters = entries.ToArray(); } } + // ===================================================================== + // ===================================================================== + // There are parameters that, when set, cause things to happen in the physics engine. + // This causes the broadphase collision cache to be cleared. private static void ResetBroadphasePoolTainted(BSScene pPhysScene, float v) { BSScene physScene = pPhysScene; @@ -726,6 +716,7 @@ public static class BSParam }); } + // This causes the constraint solver cache to be cleared and reset. private static void ResetConstraintSolverTainted(BSScene pPhysScene, float v) { BSScene physScene = pPhysScene; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 0323b0d39f..4dff9278f1 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -70,6 +70,8 @@ public class BSPrim : BSPhysObject private bool _kinematic; private float _buoyancy; + private int CrossingFailures { get; set; } + public BSDynamics VehicleController { get; private set; } private BSVMotor _targetMotor; @@ -197,7 +199,20 @@ public class BSPrim : BSPhysObject { get { return _isSelected; } } - public override void CrossingFailure() { return; } + + public override void CrossingFailure() + { + CrossingFailures++; + if (CrossingFailures > BSParam.CrossingFailuresBeforeOutOfBounds) + { + base.RaiseOutOfBounds(RawPosition); + } + else if (CrossingFailures == BSParam.CrossingFailuresBeforeOutOfBounds) + { + m_log.WarnFormat("{0} Too many crossing failures for {1}", LogHeader, Name); + } + return; + } // link me to the specified parent public override void link(PhysicsActor obj) { @@ -239,50 +254,98 @@ public class BSPrim : BSPhysObject }); } + bool TryExperimentalLockAxisCode = false; + BSConstraint LockAxisConstraint = null; public override void LockAngularMotion(OMV.Vector3 axis) { DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis); + // "1" means free, "0" means locked OMV.Vector3 locking = new OMV.Vector3(1f, 1f, 1f); if (axis.X != 1) locking.X = 0f; if (axis.Y != 1) locking.Y = 0f; if (axis.Z != 1) locking.Z = 0f; LockedAxis = locking; - /* Not implemented yet - if (LockedAxis != LockedAxisFree) + if (TryExperimentalLockAxisCode && LockedAxis != LockedAxisFree) { - // Something is locked so start the thingy that keeps that axis from changing - RegisterPreUpdatePropertyAction("BSPrim.LockAngularMotion", delegate(ref EntityProperties entprop) + // Lock that axis by creating a 6DOF constraint that has one end in the world and + // the other in the object. + // http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?p=20817 + // http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?p=26380 + + PhysicsScene.TaintedObject("BSPrim.LockAngularMotion", delegate() { - if (LockedAxis != LockedAxisFree) - { - if (IsPhysicallyActive) - { - // Bullet can lock axis but it only works for global axis. - // Check if this prim is aligned on global axis and use Bullet's - // system if so. + CleanUpLockAxisPhysicals(true /* inTaintTime */); - ForceOrientation = entprop.Rotation; - ForceRotationalVelocity = entprop.RotationalVelocity; - } - } - else - { - UnRegisterPreUpdatePropertyAction("BSPrim.LockAngularMotion"); - } + BSConstraint6Dof axisConstrainer = new BSConstraint6Dof(PhysicsScene.World, PhysBody, + OMV.Vector3.Zero, OMV.Quaternion.Inverse(RawOrientation), + true /* useLinearReferenceFrameB */, true /* disableCollisionsBetweenLinkedBodies */); + LockAxisConstraint = axisConstrainer; + PhysicsScene.Constraints.AddConstraint(LockAxisConstraint); + // The constraint is tied to the world and oriented to the prim. + + // Free to move linearly + OMV.Vector3 linearLow = OMV.Vector3.Zero; + OMV.Vector3 linearHigh = PhysicsScene.TerrainManager.DefaultRegionSize; + axisConstrainer.SetLinearLimits(linearLow, linearHigh); + + // Angular with some axis locked + float f2PI = (float)Math.PI * 2f; + OMV.Vector3 angularLow = new OMV.Vector3(-f2PI, -f2PI, -f2PI); + OMV.Vector3 angularHigh = new OMV.Vector3(f2PI, f2PI, f2PI); + if (LockedAxis.X != 1f) + { + angularLow.X = 0f; + angularHigh.X = 0f; + } + if (LockedAxis.Y != 1f) + { + angularLow.Y = 0f; + angularHigh.Y = 0f; + } + if (LockedAxis.Z != 1f) + { + angularLow.Z = 0f; + angularHigh.Z = 0f; + } + axisConstrainer.SetAngularLimits(angularLow, angularHigh); + + DetailLog("{0},BSPrim.LockAngularMotion,create,linLow={1},linHi={2},angLow={3},angHi={4}", + LocalID, linearLow, linearHigh, angularLow, angularHigh); + + // Constants from one of the posts mentioned above and used in Bullet's ConstraintDemo. + axisConstrainer.TranslationalLimitMotor(true /* enable */, 5.0f, 0.1f); + + axisConstrainer.RecomputeConstraintVariables(RawMass); }); } else { // Everything seems unlocked - UnRegisterPreUpdatePropertyAction("BSPrim.LockAngularMotion"); + CleanUpLockAxisPhysicals(false /* inTaintTime */); } - */ return; } + // Get rid of any constraint built for LockAxis + // Most often the constraint is removed when the constraint collection is cleaned for this prim. + private void CleanUpLockAxisPhysicals(bool inTaintTime) + { + if (LockAxisConstraint != null) + { + PhysicsScene.TaintedObject(inTaintTime, "BSPrim.CleanUpLockAxisPhysicals", delegate() + { + if (LockAxisConstraint != null) + { + PhysicsScene.Constraints.RemoveAndDestroyConstraint(LockAxisConstraint); + LockAxisConstraint = null; + DetailLog("{0},BSPrim.CleanUpLockAxisPhysicals,destroyingConstraint", LocalID); + } + }); + } + } public override OMV.Vector3 RawPosition { @@ -762,6 +825,7 @@ public class BSPrim : BSPhysObject SetObjectDynamic(true); // whether phys-to-static or static-to-phys, the object is not moving. ZeroMotion(true); + }); } } @@ -885,6 +949,8 @@ public class BSPrim : BSPhysObject // For good measure, make sure the transform is set through to the motion state ForcePosition = _position; + ForceVelocity = _velocity; + ForceRotationalVelocity = _rotationalVelocity; // A dynamic object has mass UpdatePhysicalMassProperties(RawMass, false); @@ -1064,15 +1130,19 @@ public class BSPrim : BSPhysObject _buoyancy = value; // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); // Force the recalculation of the various inertia,etc variables in the object - DetailLog("{0},BSPrim.ForceBuoyancy,buoy={1},mass={2}", LocalID, _buoyancy, _mass); - UpdatePhysicalMassProperties(_mass, true); + UpdatePhysicalMassProperties(RawMass, true); + DetailLog("{0},BSPrim.ForceBuoyancy,buoy={1},mass={2},grav={3}", LocalID, _buoyancy, RawMass, Gravity); ActivateIfPhysical(false); } } // Used for MoveTo public override OMV.Vector3 PIDTarget { - set { _PIDTarget = value; } + set + { + // TODO: add a sanity check -- don't move more than a region or something like that. + _PIDTarget = value; + } } public override float PIDTau { set { _PIDTau = value; } @@ -1126,7 +1196,9 @@ public class BSPrim : BSPhysObject } else { - ForcePosition = movePosition; + _position = movePosition; + PositionSanityCheck(true /* intaintTime */); + ForcePosition = _position; } DetailLog("{0},BSPrim.PIDTarget,move,fromPos={1},movePos={2}", LocalID, origPosition, movePosition); }); @@ -1303,6 +1375,7 @@ public class BSPrim : BSPhysObject { if (PhysBody.HasPhysicalBody) { + DetailLog("{0},BSPrim.AddAngularForce,taint,angForce={1}", LocalID, angForce); PhysicsScene.PE.ApplyTorque(PhysBody, angForce); ActivateIfPhysical(false); } @@ -1667,7 +1740,7 @@ public class BSPrim : BSPhysObject // _velocity = entprop.Velocity; // DEBUG DEBUG DEBUG -- smooth velocity changes a bit. The simulator seems to be // very sensitive to velocity changes. - if (!entprop.Velocity.ApproxEquals(_velocity, 0.1f)) + if (entprop.Velocity == OMV.Vector3.Zero || !entprop.Velocity.ApproxEquals(_velocity, 0.1f)) _velocity = entprop.Velocity; _acceleration = entprop.Acceleration; _rotationalVelocity = entprop.RotationalVelocity; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 05722b8c34..e6aefd52ec 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -161,7 +161,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters private int m_physicsLoggingFileMinutes; private bool m_physicsLoggingDoFlush; private bool m_physicsPhysicalDumpEnabled; - public float PhysicsMetricDumpFrames { get; set; } + public int PhysicsMetricDumpFrames { get; set; } // 'true' of the vehicle code is to log lots of details public bool VehicleLoggingEnabled { get; private set; } public bool VehiclePhysicalLoggingEnabled { get; private set; } @@ -542,7 +542,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters collidersCount = 0; } - if ((m_simulationStep % PhysicsMetricDumpFrames) == 0) + if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0)) PE.DumpPhysicsStatistics(World); // Get a value for 'now' so all the collision and update routines don't have to get their own. @@ -880,38 +880,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters { bool ret = false; - float valf = 0f; - if (val.ToLower() == "true") - { - valf = PhysParameterEntry.NUMERIC_TRUE; - } - else - { - if (val.ToLower() == "false") - { - valf = PhysParameterEntry.NUMERIC_FALSE; - } - else - { - try - { - valf = float.Parse(val); - } - catch - { - valf = 0f; - } - } - } - - BSParam.ParameterDefn theParam; + BSParam.ParameterDefnBase theParam; if (BSParam.TryGetParameter(parm, out theParam)) { // Set the value in the C# code - theParam.setter(this, parm, localID, valf); + theParam.SetValue(this, val); // Optionally set the parameter in the unmanaged code - if (theParam.onObject != null) + if (theParam.HasSetOnObject) { // update all the localIDs specified // If the local ID is APPLY_TO_NONE, just change the default value @@ -923,16 +899,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters case PhysParameterEntry.APPLY_TO_NONE: // This will cause a call into the physical world if some operation is specified (SetOnObject). objectIDs.Add(TERRAIN_ID); - TaintedUpdateParameter(parm, objectIDs, valf); + TaintedUpdateParameter(parm, objectIDs, val); break; case PhysParameterEntry.APPLY_TO_ALL: lock (PhysObjects) objectIDs = new List(PhysObjects.Keys); - TaintedUpdateParameter(parm, objectIDs, valf); + TaintedUpdateParameter(parm, objectIDs, val); break; default: // setting only one localID objectIDs.Add(localID); - TaintedUpdateParameter(parm, objectIDs, valf); + TaintedUpdateParameter(parm, objectIDs, val); break; } } @@ -943,22 +919,22 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters } // schedule the actual updating of the paramter to when the phys engine is not busy - private void TaintedUpdateParameter(string parm, List lIDs, float val) + private void TaintedUpdateParameter(string parm, List lIDs, string val) { - float xval = val; + string xval = val; List xlIDs = lIDs; string xparm = parm; TaintedObject("BSScene.UpdateParameterSet", delegate() { - BSParam.ParameterDefn thisParam; + BSParam.ParameterDefnBase thisParam; if (BSParam.TryGetParameter(xparm, out thisParam)) { - if (thisParam.onObject != null) + if (thisParam.HasSetOnObject) { foreach (uint lID in xlIDs) { BSPhysObject theObject = null; if (PhysObjects.TryGetValue(lID, out theObject)) - thisParam.onObject(this, theObject, xval); + thisParam.SetOnObject(this, theObject); } } } @@ -971,10 +947,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters { string val = String.Empty; bool ret = false; - BSParam.ParameterDefn theParam; + BSParam.ParameterDefnBase theParam; if (BSParam.TryGetParameter(parm, out theParam)) { - val = theParam.getter(this).ToString(); + val = theParam.GetValue(this); ret = true; } value = val; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index 096b300a90..2e54a93e7c 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -568,7 +568,7 @@ public sealed class BSShapeCollection : IDisposable { newShape = PhysicsScene.PE.BuildCapsuleShape(PhysicsScene.World, 1f, 1f, prim.Scale); - if (DDetail) DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale); + if (DDetail) DetailLog("{0},BSShapeCollection.BuildPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale); } else { diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt index bda7c471b1..49718c4e0f 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt @@ -1,17 +1,16 @@ CURRENT PRIORITIES ================================================= -One sided meshes? Should terrain be built into a closed shape? - When meshes get partially wedged into the terrain, they cannot push themselves out. - It is possible that Bullet processes collisions whether entering or leaving a mesh. - Ref: http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4869 Deleting a linkset while standing on the root will leave the physical shape of the root behind. Not sure if it is because standing on it. Done with large prim linksets. -Terrain detail: double terrain mesh detail Vehicle angular vertical attraction vehicle angular banking Center-of-gravity Vehicle angular deflection Preferred orientation angular correction fix +Enable vehicle border crossings (at least as poorly as ODE) + Terrain skirts + Avatar created in previous region and not new region when crossing border + Vehicle recreated in new sim at small Z value (offset from root value?) (DONE) when should angular and linear motor targets be zeroed? when selected? Need a vehicle.clear()? Or an 'else' in prestep if not physical. Teravus llMoveToTarget script debug @@ -26,14 +25,16 @@ Avatar movement flying into a wall doesn't stop avatar who keeps appearing to move through the obstacle (DONE) walking up stairs is not calibrated correctly (stairs out of Kepler cabin) avatar capsule rotation completed (NOT DONE - Bullet's capsule shape is not the solution) -Enable vehicle border crossings (at least as poorly as ODE) - Terrain skirts - Avatar created in previous region and not new region when crossing border - Vehicle recreated in new sim at small Z value (offset from root value?) (DONE) Vehicle script tuning/debugging Avanti speed script Weapon shooter script -Add material densities to the material types +Move material definitions (friction, ...) into simulator. +Add material densities to the material types. +Terrain detail: double terrain mesh detail +One sided meshes? Should terrain be built into a closed shape? + When meshes get partially wedged into the terrain, they cannot push themselves out. + It is possible that Bullet processes collisions whether entering or leaving a mesh. + Ref: http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4869 VEHICLES TODO LIST: ================================================= @@ -65,6 +66,7 @@ Vehicle attributes are not restored when a vehicle is rezzed on region creation GENERAL TODO LIST: ================================================= +Add a sanity check for PIDTarget location. Level-of-detail for mesh creation. Prims with circular interiors require lod of 32. Is much saved with lower LODs? At the moment, all set to 32. Collisions are inconsistant: arrows are supposed to hit and report collision. Often don't. diff --git a/OpenSim/Region/Physics/BulletSPlugin/Properties/AssemblyInfo.cs b/OpenSim/Region/Physics/BulletSPlugin/Properties/AssemblyInfo.cs index d240c71814..02b03a8cfe 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Region/Physics/ConvexDecompositionDotNet/Properties/AssemblyInfo.cs b/OpenSim/Region/Physics/ConvexDecompositionDotNet/Properties/AssemblyInfo.cs index cafd7f41f5..f611b9ac26 100644 --- a/OpenSim/Region/Physics/ConvexDecompositionDotNet/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/Physics/ConvexDecompositionDotNet/Properties/AssemblyInfo.cs @@ -33,4 +33,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Region/Physics/Meshing/Properties/AssemblyInfo.cs b/OpenSim/Region/Physics/Meshing/Properties/AssemblyInfo.cs index bd70296cb6..3de061a83c 100644 --- a/OpenSim/Region/Physics/Meshing/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/Physics/Meshing/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Region/RegionCombinerModule/Properties/AssemblyInfo.cs b/OpenSim/Region/RegionCombinerModule/Properties/AssemblyInfo.cs index ca945b579f..86a3101061 100644 --- a/OpenSim/Region/RegionCombinerModule/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/RegionCombinerModule/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 6ff6b00bd1..d64e6d7f4e 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -2172,9 +2172,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api CheckThreatLevel(ThreatLevel.Moderate, "osGetGridHomeURI"); m_host.AddScriptLPS(1); - string HomeURI = String.Empty; IConfigSource config = m_ScriptEngine.ConfigSource; + string HomeURI = Util.GetConfigVarWithDefaultSection(config, "HomeURI", string.Empty); + if (!string.IsNullOrEmpty(HomeURI)) + return HomeURI; + + // Legacy. Remove soon! if (config.Configs["LoginService"] != null) HomeURI = config.Configs["LoginService"].GetString("SRV_HomeURI", HomeURI); @@ -2189,9 +2193,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api CheckThreatLevel(ThreatLevel.Moderate, "osGetGridGatekeeperURI"); m_host.AddScriptLPS(1); - string gatekeeperURI = String.Empty; IConfigSource config = m_ScriptEngine.ConfigSource; + string gatekeeperURI = Util.GetConfigVarWithDefaultSection(config, "GatekeeperURI", string.Empty); + if (!string.IsNullOrEmpty(gatekeeperURI)) + return gatekeeperURI; + + // Legacy. Remove soon! if (config.Configs["GridService"] != null) gatekeeperURI = config.Configs["GridService"].GetString("Gatekeeper", gatekeeperURI); diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Properties/AssemblyInfo.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Properties/AssemblyInfo.cs index 3c01eecc89..6d218a6778 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Region/ScriptEngine/Shared/CodeTools/Properties/AssemblyInfo.cs b/OpenSim/Region/ScriptEngine/Shared/CodeTools/Properties/AssemblyInfo.cs index fd37753eb0..5b5c4fd954 100644 --- a/OpenSim/Region/ScriptEngine/Shared/CodeTools/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/ScriptEngine/Shared/CodeTools/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/Properties/AssemblyInfo.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/Properties/AssemblyInfo.cs index 74747a2435..48964b6028 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Instance/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Instance/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Region/ScriptEngine/XEngine/Properties/AssemblyInfo.cs b/OpenSim/Region/ScriptEngine/XEngine/Properties/AssemblyInfo.cs index a8871715a1..f0640da83e 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Region/UserStatistics/Properties/AssemblyInfo.cs b/OpenSim/Region/UserStatistics/Properties/AssemblyInfo.cs index caa6d4e2a0..1fff12a60c 100644 --- a/OpenSim/Region/UserStatistics/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/UserStatistics/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Server/Base/Properties/AssemblyInfo.cs b/OpenSim/Server/Base/Properties/AssemblyInfo.cs index 8b4556418e..b4732b883a 100644 --- a/OpenSim/Server/Base/Properties/AssemblyInfo.cs +++ b/OpenSim/Server/Base/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Server/Handlers/Grid/GridInfoHandlers.cs b/OpenSim/Server/Handlers/Grid/GridInfoHandlers.cs index 965a54e48e..35f86c5acf 100644 --- a/OpenSim/Server/Handlers/Grid/GridInfoHandlers.cs +++ b/OpenSim/Server/Handlers/Grid/GridInfoHandlers.cs @@ -170,14 +170,6 @@ namespace OpenSim.Server.Handlers.Grid public string JsonGetGridInfoMethod(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { - string HomeURI = String.Empty; - IConfig cfg = m_Config.Configs["LoginService"]; - - if (null != cfg) - { - HomeURI = cfg.GetString("SRV_HomeURI", HomeURI); - } - OSDMap map = new OSDMap(); foreach (string k in _info.Keys) @@ -185,9 +177,19 @@ namespace OpenSim.Server.Handlers.Grid map[k] = OSD.FromString(_info[k].ToString()); } + string HomeURI = Util.GetConfigVarWithDefaultSection(m_Config, "HomeURI", string.Empty); + if (!String.IsNullOrEmpty(HomeURI)) + map["home"] = OSD.FromString(HomeURI); + else // Legacy. Remove soon! { - map["home"] = OSD.FromString(HomeURI); + IConfig cfg = m_Config.Configs["LoginService"]; + + if (null != cfg) + HomeURI = cfg.GetString("SRV_HomeURI", HomeURI); + + if (!String.IsNullOrEmpty(HomeURI)) + map["home"] = OSD.FromString(HomeURI); } return OSDParser.SerializeJsonString(map).ToString(); diff --git a/OpenSim/Server/Handlers/Properties/AssemblyInfo.cs b/OpenSim/Server/Handlers/Properties/AssemblyInfo.cs index d72d36a091..3295ffd7b8 100644 --- a/OpenSim/Server/Handlers/Properties/AssemblyInfo.cs +++ b/OpenSim/Server/Handlers/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/AssetService/Properties/AssemblyInfo.cs b/OpenSim/Services/AssetService/Properties/AssemblyInfo.cs index b57052c4d7..50ee0335a3 100644 --- a/OpenSim/Services/AssetService/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/AssetService/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/AuthenticationService/Properties/AssemblyInfo.cs b/OpenSim/Services/AuthenticationService/Properties/AssemblyInfo.cs index 99c46ecb33..435852da35 100644 --- a/OpenSim/Services/AuthenticationService/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/AuthenticationService/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/AuthorizationService/Properties/AssemblyInfo.cs b/OpenSim/Services/AuthorizationService/Properties/AssemblyInfo.cs index 33e48d3c3c..8db16710ba 100644 --- a/OpenSim/Services/AuthorizationService/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/AuthorizationService/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/AvatarService/Properties/AssemblyInfo.cs b/OpenSim/Services/AvatarService/Properties/AssemblyInfo.cs index 8b0214a536..138d4cd4c1 100644 --- a/OpenSim/Services/AvatarService/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/AvatarService/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/Base/Properties/AssemblyInfo.cs b/OpenSim/Services/Base/Properties/AssemblyInfo.cs index 2825a8865e..84a40f0f7e 100644 --- a/OpenSim/Services/Base/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/Base/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/Connectors/Properties/AssemblyInfo.cs b/OpenSim/Services/Connectors/Properties/AssemblyInfo.cs index 73fc72c5a6..8b18afb515 100644 --- a/OpenSim/Services/Connectors/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/Connectors/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/FreeswitchService/Properties/AssemblyInfo.cs b/OpenSim/Services/FreeswitchService/Properties/AssemblyInfo.cs index fdd4b694f5..b488b361e3 100644 --- a/OpenSim/Services/FreeswitchService/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/FreeswitchService/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/Friends/Properties/AssemblyInfo.cs b/OpenSim/Services/Friends/Properties/AssemblyInfo.cs index cb624f03db..b11d07d926 100644 --- a/OpenSim/Services/Friends/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/Friends/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/GridService/HypergridLinker.cs b/OpenSim/Services/GridService/HypergridLinker.cs index 073197f8db..3e7c556f46 100644 --- a/OpenSim/Services/GridService/HypergridLinker.cs +++ b/OpenSim/Services/GridService/HypergridLinker.cs @@ -128,7 +128,9 @@ namespace OpenSim.Services.GridService m_MapTileDirectory = gridConfig.GetString("MapTileDirectory", "maptiles"); - m_ThisGatekeeper = gridConfig.GetString("Gatekeeper", string.Empty); + m_ThisGatekeeper = Util.GetConfigVarWithDefaultSection(config, "GatekeeperURI", "GridService"); + // Legacy. Remove soon! + m_ThisGatekeeper = gridConfig.GetString("Gatekeeper", m_ThisGatekeeper); try { m_ThisGatekeeperURI = new Uri(m_ThisGatekeeper); diff --git a/OpenSim/Services/GridService/Properties/AssemblyInfo.cs b/OpenSim/Services/GridService/Properties/AssemblyInfo.cs index 09084d328d..b1e5e12cf7 100644 --- a/OpenSim/Services/GridService/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/GridService/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/HypergridService/GatekeeperService.cs b/OpenSim/Services/HypergridService/GatekeeperService.cs index 7b84d55ded..21f363c3a8 100644 --- a/OpenSim/Services/HypergridService/GatekeeperService.cs +++ b/OpenSim/Services/HypergridService/GatekeeperService.cs @@ -96,7 +96,8 @@ namespace OpenSim.Services.HypergridService UUID.TryParse(scope, out m_ScopeID); //m_WelcomeMessage = serverConfig.GetString("WelcomeMessage", "Welcome to OpenSim!"); m_AllowTeleportsToAnyRegion = serverConfig.GetBoolean("AllowTeleportsToAnyRegion", true); - m_ExternalName = serverConfig.GetString("ExternalName", string.Empty); + m_ExternalName = Util.GetConfigVarWithDefaultSection(config, "GatekeeperURI", "GatekeeperService"); + m_ExternalName = serverConfig.GetString("ExternalName", m_ExternalName); if (m_ExternalName != string.Empty && !m_ExternalName.EndsWith("/")) m_ExternalName = m_ExternalName + "/"; diff --git a/OpenSim/Services/HypergridService/HGInventoryService.cs b/OpenSim/Services/HypergridService/HGInventoryService.cs index 2e9bd40cd2..a9661f41a8 100644 --- a/OpenSim/Services/HypergridService/HGInventoryService.cs +++ b/OpenSim/Services/HypergridService/HGInventoryService.cs @@ -81,10 +81,7 @@ namespace OpenSim.Services.HypergridService if (m_UserAccountService == null) throw new Exception(String.Format("Unable to create UserAccountService from {0}", userAccountsDll)); - // legacy configuration [obsolete] - m_HomeURL = invConfig.GetString("ProfileServerURI", string.Empty); - // Preferred - m_HomeURL = invConfig.GetString("HomeURI", m_HomeURL); + m_HomeURL = Util.GetConfigVarWithDefaultSection(config, "HomeURI", m_ConfigName); m_Cache = UserAccountCache.CreateUserAccountCache(m_UserAccountService); } diff --git a/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs b/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs index 784f136bb0..dd546b8e06 100644 --- a/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs +++ b/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs @@ -96,8 +96,7 @@ namespace OpenSim.Services.HypergridService if (m_AvatarService == null) throw new Exception(String.Format("Unable to create m_AvatarService from {0}", avatarDll)); - // Preferred - m_HomeURL = invConfig.GetString("HomeURI", m_HomeURL); + m_HomeURL = Util.GetConfigVarWithDefaultSection(config, "HomeURI", m_ConfigName); // m_Cache = UserAccountCache.CreateUserAccountCache(m_UserAccountService); } diff --git a/OpenSim/Services/HypergridService/Properties/AssemblyInfo.cs b/OpenSim/Services/HypergridService/Properties/AssemblyInfo.cs index fe1889d06f..8d66f1b778 100644 --- a/OpenSim/Services/HypergridService/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/HypergridService/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/HypergridService/UserAgentService.cs b/OpenSim/Services/HypergridService/UserAgentService.cs index 416ad16241..c810b3594b 100644 --- a/OpenSim/Services/HypergridService/UserAgentService.cs +++ b/OpenSim/Services/HypergridService/UserAgentService.cs @@ -131,12 +131,17 @@ namespace OpenSim.Services.HypergridService LoadDomainExceptionsFromConfig(serverConfig, "AllowExcept", m_TripsAllowedExceptions); LoadDomainExceptionsFromConfig(serverConfig, "DisallowExcept", m_TripsDisallowedExceptions); - m_GridName = serverConfig.GetString("ExternalName", string.Empty); - if (m_GridName == string.Empty) + m_GridName = Util.GetConfigVarWithDefaultSection(config, "GatekeeperURI", "UserAgentService"); + if (string.IsNullOrEmpty(m_GridName)) // Legacy. Remove soon. { - serverConfig = config.Configs["GatekeeperService"]; m_GridName = serverConfig.GetString("ExternalName", string.Empty); + if (m_GridName == string.Empty) + { + serverConfig = config.Configs["GatekeeperService"]; + m_GridName = serverConfig.GetString("ExternalName", string.Empty); + } } + if (!m_GridName.EndsWith("/")) m_GridName = m_GridName + "/"; diff --git a/OpenSim/Services/Interfaces/Properties/AssemblyInfo.cs b/OpenSim/Services/Interfaces/Properties/AssemblyInfo.cs index 669e0b889c..47ece7513c 100644 --- a/OpenSim/Services/Interfaces/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/Interfaces/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/InventoryService/Properties/AssemblyInfo.cs b/OpenSim/Services/InventoryService/Properties/AssemblyInfo.cs index 0870065ff3..bfae81f33c 100644 --- a/OpenSim/Services/InventoryService/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/InventoryService/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index cbb6e6c4a9..88e9eefe70 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -110,7 +110,7 @@ namespace OpenSim.Services.LLLoginService m_RequireInventory = m_LoginServerConfig.GetBoolean("RequireInventory", true); m_AllowRemoteSetLoginLevel = m_LoginServerConfig.GetBoolean("AllowRemoteSetLoginLevel", false); m_MinLoginLevel = m_LoginServerConfig.GetInt("MinLoginLevel", 0); - m_GatekeeperURL = m_LoginServerConfig.GetString("GatekeeperURI", string.Empty); + m_GatekeeperURL = Util.GetConfigVarWithDefaultSection(config, "GatekeeperURI", "LoginService"); m_MapTileURL = m_LoginServerConfig.GetString("MapTileURL", string.Empty); m_ProfileURL = m_LoginServerConfig.GetString("ProfileServerURL", string.Empty); m_OpenIDURL = m_LoginServerConfig.GetString("OpenIDServerURL", String.Empty); @@ -969,14 +969,25 @@ namespace OpenSim.Services.LLLoginService // or fixing critical issues // if (cmd.Length > 2) - Int32.TryParse(cmd[2], out m_MinLoginLevel); + { + if (Int32.TryParse(cmd[2], out m_MinLoginLevel)) + MainConsole.Instance.OutputFormat("Set minimum login level to {0}", m_MinLoginLevel); + else + MainConsole.Instance.OutputFormat("ERROR: {0} is not a valid login level", cmd[2]); + } break; - case "reset": + + case "reset": m_MinLoginLevel = 0; + MainConsole.Instance.OutputFormat("Reset min login level to {0}", m_MinLoginLevel); break; + case "text": if (cmd.Length > 2) + { m_WelcomeMessage = cmd[2]; + MainConsole.Instance.OutputFormat("Login welcome message set to '{0}'", m_WelcomeMessage); + } break; } } diff --git a/OpenSim/Services/LLLoginService/Properties/AssemblyInfo.cs b/OpenSim/Services/LLLoginService/Properties/AssemblyInfo.cs index 3ac8af7a1f..0a6daee62d 100644 --- a/OpenSim/Services/LLLoginService/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/LLLoginService/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/MapImageService/Properties/AssemblyInfo.cs b/OpenSim/Services/MapImageService/Properties/AssemblyInfo.cs index 69adf73a37..19936e5628 100644 --- a/OpenSim/Services/MapImageService/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/MapImageService/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/PresenceService/Properties/AssemblyInfo.cs b/OpenSim/Services/PresenceService/Properties/AssemblyInfo.cs index 040bbe0640..5d433df753 100644 --- a/OpenSim/Services/PresenceService/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/PresenceService/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Services/UserAccountService/Properties/AssemblyInfo.cs b/OpenSim/Services/UserAccountService/Properties/AssemblyInfo.cs index 576ccce768..e7d2d6fe97 100644 --- a/OpenSim/Services/UserAccountService/Properties/AssemblyInfo.cs +++ b/OpenSim/Services/UserAccountService/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Tools/Compiler/Properties/AssemblyInfo.cs b/OpenSim/Tools/Compiler/Properties/AssemblyInfo.cs index b98e2d2712..088be45ac9 100644 --- a/OpenSim/Tools/Compiler/Properties/AssemblyInfo.cs +++ b/OpenSim/Tools/Compiler/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Tools/Configger/Properties/AssemblyInfo.cs b/OpenSim/Tools/Configger/Properties/AssemblyInfo.cs index 89aafa3b57..0348628ecb 100644 --- a/OpenSim/Tools/Configger/Properties/AssemblyInfo.cs +++ b/OpenSim/Tools/Configger/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/OpenSim/Tools/pCampBot/Properties/AssemblyInfo.cs b/OpenSim/Tools/pCampBot/Properties/AssemblyInfo.cs index c4d278a2e7..78f3603b2e 100644 --- a/OpenSim/Tools/pCampBot/Properties/AssemblyInfo.cs +++ b/OpenSim/Tools/pCampBot/Properties/AssemblyInfo.cs @@ -30,4 +30,4 @@ using System.Runtime.InteropServices; // Revision // [assembly: AssemblyVersion("0.7.6.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 7e7b23179c..c8c5ed3859 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -342,6 +342,21 @@ ;; - "Imprudence 1.3.1" has access ; BannedViewerList = + ;# {HomeURI} {Hypergrid} {The Home URL of this world} {} + ;; If this is a standalone world, this is the address of this instance. + ;; If this is a grided simulator, this is the address of the external robust server that + ;; runs the UserAgentsService. + ;; For example http://myworld.com:9000 or http://myworld.com:8002 + ;; This is a default that can be overwritten in some sections. + ; HomeURI = "http://127.0.0.1:9000" + + ;# {GatekeeperURI} {Hypergrid} {The URL of the gatekeeper of this world} {} + ;; If this is a standalone world, this is the address of this instance. + ;; If this is a grided simulator, this is the address of the external robust server + ;; that runs the Gatekeeper service. + ;; For example http://myworld.com:9000 or http://myworld.com:8002 + ;; This is a default that can be overwritten in some sections. + ; GatekeeperURI = "http://127.0.0.1:9000" [Estates] ; If these values are commented out then the user will be asked for estate details when required (this is the normal case). @@ -527,14 +542,20 @@ [Messaging] - ;# {OfflineMessageModule} {} {Module to use for offline message storage} {OfflineMessageModule *} + ;# {OfflineMessageModule} {} {Module to use for offline message storage} {OfflineMessageModule "Offline Message Module V2" *} ;; Module to handle offline messaging. The core module requires an external ;; web service to do this. See OpenSim wiki. ; OfflineMessageModule = OfflineMessageModule + ;; Or, alternatively, use this one, which works for both standalones and grids + ; OfflineMessageModule = "Offline Message Module V2" - ;# {OfflineMessageURL} {OfflineMessageModule:OfflineMessageModule} {URL of offline messaging service} {} - ;; URL of web service for offline message storage - ; OfflineMessageURL = http://yourserver/Offline.php + ;# {OfflineMessageURL} {OfflineMessageModule:OfflineMessageModule Offline Message Module V2:Offline Message Module V2} {URL of offline messaging service} {} + ;; URL of web service for offline message storage. Leave it commented if your service is local to the sim. + ; OfflineMessageURL = http://yourserver/Offline.php or http://yourrobustserver:8003 + + ;# {StorageProvider} {Offline Message Module V2:Offline Message Module V2} {DLL that provides the storage interface} {OpenSim.Data.MySQL.dll} + ;; For standalones, this is the storage dll. + ; StorageProvider = OpenSim.Data.MySQL.dll ;# {MuteListModule} {OfflineMessageModule:OfflineMessageModule} {} {} MuteListModule ;; Mute list handler (not yet implemented). MUST BE SET to allow offline @@ -921,33 +942,48 @@ ;; Enables the groups module ; Enabled = false - ;# {Module} {Enabled:true} {Groups module to use? (Use GroupsModule to use Flotsam/Simian)} {} Default - ;; This is the current groups stub in Region.CoreModules.Avatar.Groups. - ;; All the other settings below only really apply to the Flotsam/SimianGrid - ;; GroupsModule. - ;; This module can use a PHP XmlRpc server from the Flotsam project at + ;# {Module} {Enabled:true} {Groups module to use? (Use GroupsModule to use Flotsam/Simian)} {Default "Groups Module V2"} Default + ;; The default module can use a PHP XmlRpc server from the Flotsam project at ;; http://code.google.com/p/flotsam/ ;; or from the SimianGrid project at http://code.google.com/p/openmetaverse ; Module = Default + ;; or... use Groups Module V2, which works for standalones and robust grids + ; Module = "Groups Module V2" - ;# {MessagingEnabled} {Module:GroupsModule} {Is groups messaging enabled?} {true false} true - ; MessagingEnabled = true + ;# {StorageProvider} {Module:Groups Module V2} {The DLL that provides the storage for V2} {OpenSim.Data.MySQL.dll} + ; StorageProvider = OpenSim.Data.MySQL.dll - ;# {MessagingModule} {MessagingEnabled:true} {Module to use for groups messaging} {GroupsMessagingModule} GroupsMessagingModule - ; MessagingModule = GroupsMessagingModule - - ;# {ServicesConnectorModule} {Module:GroupsModule} {Service connector to use for groups} {XmlRpcGroupsServicesConnector SimianGroupsServicesConnector} XmlRpcGroupsServicesConnector + ;# {ServicesConnectorModule} {Module:GroupsModule Module:Groups Module V2} {Service connector to use for groups} {XmlRpcGroupsServicesConnector SimianGroupsServicesConnector "Groups Local Service Connector" "Groups Remote Service Connector" "Groups HG Service Connector"} XmlRpcGroupsServicesConnector ;; Service connectors to the Groups Service as used in the GroupsModule. Select one depending on - ;; whether you're using a Flotsam XmlRpc backend or a SimianGrid backend + ;; whether you're using a Flotsam XmlRpc backend or a SimianGrid backend or several flavours of V2, Hypergrided or not, standalone or grided. ; ServicesConnectorModule = XmlRpcGroupsServicesConnector - ;# {GroupsServerURI} {Module:GroupsModule} {Groups Server URI} {} - ;; URI for the groups services + ;# {LocalService} {ServicesConnectorModule:Groups HG Service Connector} {Is the group service in this process or elsewhere?} {local remote} local + ;; Used for V2 in HG only. If standalone, set this to local; if grided sim, set this to remote + ; LocalService = local + + ;# {GroupsServerURI} {Module:GroupsModule (ServicesConnectorModule:Groups Remote Service Connector or (ServicesConnectorModule:Groups HG Service Connector and LocalService:remote))} {Groups Server URI} {} + ;; URI for the groups services of this grid ;; e.g. http://yourxmlrpcserver.com/xmlrpc.php for Flotsam XmlRpc ;; or http://mygridserver.com:82/Grid/ for SimianGrid + ;; or http:://mygridserver.com:8003 for robust, V2 ; GroupsServerURI = "" - ;# {NoticesEnabled} {Module:GroupsModule} {Enable group notices?} {true false} true + ;# {HomeURI} {ServicesConnectorModule:Groups HG Service Connector} {What's the home address of this world?} {} + ;; Used for V2 in HG only. For example + ;; http://mygridserver.com:9000 or http://mygridserver.com:8002 + ;; If you have this set under [Startup], no need to set it here, leave it commented + ; HomeURI = "" + + ;# {MessagingEnabled} {Module:GroupsModule Module:Groups Module V2} {Is groups messaging enabled?} {true false} true + ; MessagingEnabled = true + + ;# {MessagingModule} {MessagingEnabled:true} {Module to use for groups messaging} {GroupsMessagingModule "Groups Messaging Module V2"} GroupsMessagingModule + ; MessagingModule = GroupsMessagingModule + ;; or use V2 for Groups V2 + ; MessagingModule = "Groups Messaging Module V2" + + ;# {NoticesEnabled} {Module:GroupsModule Module:Groups Module V2} {Enable group notices?} {true false} true ;; Enable Group Notices ; NoticesEnabled = true diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example index c7d4b7f0f5..274132e4ff 100644 --- a/bin/Robust.HG.ini.example +++ b/bin/Robust.HG.ini.example @@ -22,17 +22,31 @@ ; * [Startup] -; Plugin Registry Location -; Set path to directory for plugin registry. Information -; about the registered repositories and installed plugins -; will be stored here -; The Robust.exe process must hvae R/W access to the location -RegistryLocation = "." + ; Plugin Registry Location + ; Set path to directory for plugin registry. Information + ; about the registered repositories and installed plugins + ; will be stored here + ; The Robust.exe process must hvae R/W access to the location + RegistryLocation = "." -; Modular configurations -; Set path to directory for modular ini files... -; The Robust.exe process must hvae R/W access to the location -ConfigDirectory = "/home/opensim/etc/Configs" + ; Modular configurations + ; Set path to directory for modular ini files... + ; The Robust.exe process must hvae R/W access to the location + ConfigDirectory = "/home/opensim/etc/Configs" + + ;# {HomeURI} {Hypergrid} {The Home URL of this world} {} + ;; This is the address of the external robust server that + ;; runs the UserAgentsService, possibly this server. + ;; For example http://myworld.com:8002 + ;; This is a default that can be overwritten in some sections. + ; HomeURI = "http://127.0.0.1:8002" + + ;# {GatekeeperURI} {Hypergrid} {The URL of the gatekeeper of this world} {} + ;; This is the address of the external robust server + ;; that runs the Gatekeeper service, possibly this server. + ;; For example http://myworld.com:8002 + ;; This is a default that can be overwritten in some sections. + ; GatekeeperURI = "http://127.0.0.1:8002" [ServiceList] @@ -51,6 +65,10 @@ GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConn FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector" MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector" MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector" +;; Uncomment this if you want offline IM to work +; OfflineIMServiceConnector = "8003/OpenSim.Addons.OfflineIM.dll:OfflineIMServiceRobustConnector" +;; Uncomment this if you want Groups V2 to work +; GroupsServiceConnector = "8003/OpenSim.Addons.Groups.dll:GroupsServiceRobustConnector" ;; Additions for Hypergrid @@ -61,6 +79,8 @@ HGFriendsServerConnector = "8002/OpenSim.Server.Handlers.dll:HGFriendsServerConn InstantMessageServerConnector = "8002/OpenSim.Server.Handlers.dll:InstantMessageServerConnector" HGInventoryServiceConnector = "HGInventoryService@8002/OpenSim.Server.Handlers.dll:XInventoryInConnector" HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:AssetServiceConnector" +;; Uncomment this if you want Groups V2, HG to work +; HGGroupsServiceConnector = "8002/Diva.Groups.dll:HGGroupsServiceRobustConnector" ;; Additions for other add-on modules. For example: ;; WifiServerConnector = "8002/Diva.Wifi.dll:WifiServerConnector" @@ -149,7 +169,8 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset ;; Allow Hyperlinks to be created at the console HypergridLinker = true - Gatekeeper = "http://127.0.0.1:8002" + ;; If you have this set under [Startup], no need to set it here, leave it commented + ; GatekeeperURI = "http://127.0.0.1:8002" ; * This is the configuration for the freeswitch server in grid mode @@ -303,8 +324,8 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset ; HasProxy = false ; Defaults for the users, if none is specified in the useraccounts table entry (ServiceURLs) - ; CHANGE THIS - GatekeeperURI = "http://127.0.0.1:8002" + ;; If you have Gatekeeper set under [Startup], no need to set it here, leave it commented + ; GatekeeperURI = "http://127.0.0.1:8002" SRV_HomeURI = "http://127.0.0.1:8002" SRV_InventoryServerURI = "http://127.0.0.1:8002" @@ -411,8 +432,8 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset AuthenticationService = "OpenSim.Services.Connectors.dll:AuthenticationServicesConnector" SimulationService ="OpenSim.Services.Connectors.dll:SimulationServiceConnector" ; how does the outside world reach me? This acts as public key too. - ; CHANGE THIS - ExternalName = "http://127.0.0.1:8002" + ;; If you have GatekeeperURI set under [Startup], no need to set it here, leave it commented + ; ExternalName = "http://127.0.0.1:8002" ; Does this grid allow incoming links to any region in it? ; If false, HG TPs happen only to the Default regions specified in [GridService] section @@ -505,7 +526,9 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService" AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" - HomeURI = "http://127.0.0.1:8002" + + ;; Can overwrite the default in [Startup], but probably shouldn't + ; HomeURI = "http://127.0.0.1:8002" ; * The interface that local users get when they are in other grids. ; * This restricts the access that the rest of the world has to @@ -514,7 +537,9 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset [HGAssetService] LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGAssetService" UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService" - HomeURI = "http://127.0.0.1:8002" + + ;; Can overwrite the default in [Startup], but probably shouldn't + ; HomeURI = "http://127.0.0.1:8002" ;; The asset types that this grid can export to / import from other grids. ;; Comma separated. @@ -545,15 +570,16 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset InGatekeeper = True [Messaging] - ; If you have an Offline IM server, set the vars in this section, so that - ; incomming IMs to local users from foreign grids can be saved - ; - ;# {OfflineMessageURL} {OfflineMessageModule:OfflineMessageModule} {URL of offline messaging service} {} - ;; URL of web service for offline message storage - ; OfflineMessageURL = http://yourserver/Offline.php + ; OfflineIM + OfflineIMService = "OpenSim.Addons.OfflineIM.dll:OfflineIMService" + +[Groups] + ;; for the HG Groups service + OfflineIMService = "OpenSim.Addons.OfflineIM.dll:OfflineIMService" + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + + ;; What is the HomeURI of users associated with this grid? + ;; Can overwrite the default in [Startup], but probably shouldn't + ; HomeURI = "http://127.0.0.1:8002" - ;; Control whether group messages are forwarded to offline users. - ;; Default is true. - ;; This applies to the core groups module (Flotsam) only. - ; ForwardOfflineGroupMessages = true diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index bc5cbccb9d..ecbed1fcba 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -14,18 +14,18 @@ ; * [Startup] -; Plugin Registry Location -; Set path to directory for plugin registry. Information -; about the registered repositories and installed plugins -; will be stored here -; The Robust.exe process must hvae R/W access to the location -RegistryLocation = "." + ; Plugin Registry Location + ; Set path to directory for plugin registry. Information + ; about the registered repositories and installed plugins + ; will be stored here + ; The Robust.exe process must hvae R/W access to the location + RegistryLocation = "." -; Modular configurations -; Set path to directory for modular ini files... -; The Robust.exe process must hvae R/W access to the location -ConfigDirectory = "/home/opensim/etc/Configs" + ; Modular configurations + ; Set path to directory for modular ini files... + ; The Robust.exe process must hvae R/W access to the location + ConfigDirectory = "/home/opensim/etc/Configs" [ServiceList] AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector" @@ -43,6 +43,11 @@ GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConn FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector" MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector" MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector" +;; Uncomment this if you want offline IM to work +;OfflineIMServiceConnector = "8003/OpenSim.Addons.OfflineIM.dll:OfflineIMServiceRobustConnector" +;; Uncomment this if you want Groups V2 to work +;GroupsServiceConnector = "8003/OpenSim.Addons.Groups.dll:GroupsServiceRobustConnector" + ; * This is common for all services, it's the network setup for the entire ; * server instance, if none is specified above @@ -256,7 +261,12 @@ MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnecto LibraryService = "OpenSim.Services.InventoryService.dll:LibraryService" FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" - ;; Ask co-operative viewers to use a different currency name + ; The minimum user level required for a user to be able to login. 0 by default + ; If you disable a particular user's account then you can set their login level below this number. + ; You can also change this level from the console though these changes will not be persisted. + ; MinLoginLevel = 0 + + ; Ask co-operative viewers to use a different currency name ;Currency = "" WelcomeMessage = "Welcome, Avatar!" @@ -329,6 +339,9 @@ MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnecto ; HasProxy = false +[Messaging] + ; OfflineIM + OfflineIMService = "OpenSim.Addons.OfflineIM.dll:OfflineIMService" [GridInfoService] ; These settings are used to return information on a get_grid_info call. diff --git a/bin/config-include/StandaloneCommon.ini.example b/bin/config-include/StandaloneCommon.ini.example index f28de437cd..3129078861 100644 --- a/bin/config-include/StandaloneCommon.ini.example +++ b/bin/config-include/StandaloneCommon.ini.example @@ -64,8 +64,8 @@ Region_Welcome_Area = "DefaultRegion, FallbackRegion" ; === HG ONLY === - ;; change this to the address of your simulator - Gatekeeper="http://127.0.0.1:9000" + ;; If you have this set under [Startup], no need to set it here, leave it commented + ; GatekeeperURI="http://127.0.0.1:9000" [LibraryModule] ; Set this if you want to change the name of the OpenSim Library @@ -73,7 +73,8 @@ [LoginService] WelcomeMessage = "Welcome, Avatar!" - GatekeeperURI = "http://127.0.0.1:9000" + ;; If you have Gatekeeper set under [Startup], no need to set it here, leave it commented + ; GatekeeperURI = "http://127.0.0.1:9000" SRV_HomeURI = "http://127.0.0.1:9000" SRV_InventoryServerURI = "http://127.0.0.1:9000" @@ -85,6 +86,11 @@ ;; For Viewer 2 MapTileURL = "http://127.0.0.1:9000/" + ; The minimum user level required for a user to be able to login. 0 by default + ; If you disable a particular user's account then you can set their login level below this number. + ; You can also change this level from the console though these changes will not be persisted. + ; MinLoginLevel = 0 + ;; Ask co-operative viewers to use a different currency name ;Currency = "" @@ -213,7 +219,8 @@ ;; HG configurations ;; [GatekeeperService] - ExternalName = "http://127.0.0.1:9000" + ;; If you have GatekeeperURI set under [Startup], no need to set it here, leave it commented + ; ExternalName = "http://127.0.0.1:9000" ; Does this grid allow incoming links to any region in it? ; If false, HG TPs happen only to the Default regions specified in [GridService] section @@ -269,10 +276,12 @@ ; AllowExcept_Level_200 = "http://griefer.com:8002, http://enemy.com:8002" [HGInventoryService] - HomeURI = "http://127.0.0.1:9000" + ;; If you have this set under [Startup], no need to set it here, leave it commented + ; HomeURI = "http://127.0.0.1:9000" [HGAssetService] - HomeURI = "http://127.0.0.1:9000" + ;; If you have this set under [Startup], no need to set it here, leave it commented + ; HomeURI = "http://127.0.0.1:9000" ;; The asset types that this grid can export to / import from other grids. ;; Comma separated. @@ -288,8 +297,9 @@ [HGInventoryAccessModule] - HomeURI = "http://127.0.0.1:9000" - Gatekeeper = "http://127.0.0.1:9000" + ;; If you have these set under [Startup], no need to set it here, leave it commented + ; HomeURI = "http://127.0.0.1:9000" + ; GatekeeperURI = "http://127.0.0.1:9000" ;; If you want to protect your assets from being copied by foreign visitors ;; uncomment the next line. You may want to do this on sims that have licensed content. @@ -306,8 +316,8 @@ [Messaging] ; === HG ONLY === - ;; change this to the address of your simulator - Gatekeeper = "http://127.0.0.1:9000" + ;; If you have this set under [Startup], no need to set it here, leave it commented + ; GatekeeperURI = "http://127.0.0.1:9000" [EntityTransfer] diff --git a/bin/lib32/BulletSim.dll b/bin/lib32/BulletSim.dll index e7a8a41ec0..689b34f99d 100755 Binary files a/bin/lib32/BulletSim.dll and b/bin/lib32/BulletSim.dll differ diff --git a/bin/lib32/libBulletSim.so b/bin/lib32/libBulletSim.so index 4bf23a6496..b8c813e345 100755 Binary files a/bin/lib32/libBulletSim.so and b/bin/lib32/libBulletSim.so differ diff --git a/bin/lib64/BulletSim.dll b/bin/lib64/BulletSim.dll index 63ac1a91aa..33c4d08bf4 100755 Binary files a/bin/lib64/BulletSim.dll and b/bin/lib64/BulletSim.dll differ diff --git a/bin/lib64/libBulletSim.so b/bin/lib64/libBulletSim.so index 422681e423..79207fedc9 100755 Binary files a/bin/lib64/libBulletSim.so and b/bin/lib64/libBulletSim.so differ diff --git a/prebuild.xml b/prebuild.xml index 2bee760502..8296daa37a 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2647,6 +2647,104 @@ + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +