diff --git a/OpenMetaverse/DirectoryManager.cs b/OpenMetaverse/DirectoryManager.cs index 2c909059..e97d727e 100644 --- a/OpenMetaverse/DirectoryManager.cs +++ b/OpenMetaverse/DirectoryManager.cs @@ -34,7 +34,7 @@ using OpenMetaverse.Messages.Linden; namespace OpenMetaverse { /// - /// Access to the Linden dataserver which allows searching for land, events, people, etc + /// Access to the dataserver which allows searching for land, events, people, etc /// public class DirectoryManager { @@ -43,11 +43,11 @@ namespace OpenMetaverse /// public enum ClassifiedCategories { - /// + /// Classified is listed in the Any category Any = 0, - /// + /// Classified is shopping related Shopping, - /// + /// Classified is LandRental, /// PropertyRental, @@ -65,24 +65,37 @@ namespace OpenMetaverse Personal } + /// public enum EventCategories { + /// All = 0, + /// Discussion = 18, + /// Sports = 19, + /// LiveMusic = 20, + /// Commercial = 22, + /// Nightlife = 23, + /// Games = 24, + /// Pageants = 25, + /// Education = 26, + /// Arts = 27, + /// Charity = 28, + /// Miscellaneous = 29 } /// - /// Modifier flags sent to DirectoryManager to select the behaviour of the + /// Modifier flags sent to DirectoryManager to change the behavior of the /// query /// /// Land Search Flags required in addition to specify land maturity rating: @@ -202,14 +215,56 @@ namespace OpenMetaverse public UUID ID; /// The title of this classified ad public string Name; - /// Unknown - public byte Flags; + /// Flags that show certain options applied to the classified + public ClassifiedFlags Flags; /// Creation date of the ad public DateTime CreationDate; /// Expiration date of the ad public DateTime ExpirationDate; /// Price that was paid for this ad public int Price; + + /// + /// Display a classified ad in a one line human readable format + /// + /// A string representing a single classified ad + public override string ToString() + { + return String.Format("Classified Ad: ID: {0}, Name: {1}, Flags: [{2}] {3}, Created: {4}, Expires: {5}, Paid: {6}", + this.ID, this.Name, this.Flags, this.Flags.ToString(), this.CreationDate, this.ExpirationDate, this.Price); + } + } + + /// + /// Classified Ad Options + /// + /// There appear to be two formats the flags are packed in. + /// This set of flags is for the newer style + [Flags] + public enum ClassifiedFlags : byte + { + None = 1 << 0, + Mature = 1 << 1, + Enabled = 1 << 2, + // HasPrice = 1 << 3, // Deprecated + UpdateTime = 1 << 4, + AutoRenew = 1 << 5 + } + + /// + /// Classified ad query options + /// + [Flags] + public enum ClassifiedQueryFlags + { + /// Include all ads in results + All = PG | Mature | Adult, + /// Include PG ads in results + PG = 1 << 2, + /// Include Mature ads in results + Mature = 1 << 3, + /// Include Adult ads in results + Adult = 1 << 6, } /// @@ -245,9 +300,10 @@ namespace OpenMetaverse public string FirstName; /// Agents last name public string LastName; - /// Agents + /// Agents public UUID AgentID; } + /// /// Response to a "Groups" Search /// @@ -394,7 +450,10 @@ namespace OpenMetaverse private GridClient Client; - + /// + /// Constructs a new instance of the DirectoryManager class + /// + /// An instance of GridClient public DirectoryManager(GridClient client) { Client = client; @@ -410,18 +469,58 @@ namespace OpenMetaverse Client.Network.RegisterCallback(PacketType.DirEventsReply, new NetworkManager.PacketCallback(EventsReplyHandler)); Client.Network.RegisterCallback(PacketType.EventInfoReply, new NetworkManager.PacketCallback(EventInfoReplyHandler)); Client.Network.RegisterCallback(PacketType.DirPlacesReply, new NetworkManager.PacketCallback(DirPlacesReplyHandler)); - } - public UUID StartClassifiedSearch(string searchText, ClassifiedCategories categories, bool mature) + // Obsoleted due to new Adult search option + [Obsolete("Use Overload with ClassifiedQueryFlags option instead")] + public UUID StartClassifiedSearch(string searchText, ClassifiedCategories category, bool mature) { + return UUID.Zero; + } + + /// + /// Query the data server for a list of classified ads containing the specified string. + /// Defaults to searching for classified placed in any category, and includes PG, Adult and Mature + /// results. + /// + /// Responses are sent 16 at a time, there is no way to know how many results a query reply will contain however assuming + /// the reply packets arrived ordered, a response with less than 16 entries would indicate all results have been received + /// + /// A string containing a list of keywords to search for + /// A UUID to correlate the results when the event is raised + public UUID StartClassifiedSearch(string searchText) + { + return StartClassifiedSearch(searchText, ClassifiedCategories.Any, ClassifiedQueryFlags.All); + } + + /// + /// Query the data server for a list of classified ads which contain specified keywords (Overload) + /// + /// A string containing a list of keywords to search for + /// The category to search + /// A set of flags which can be ORed to modify query options + /// such as classified maturity rating. + /// A UUID to correlate the results when the event is raised + /// + /// Search classified ads containing the key words "foo" and "bar" in the "Any" category that are either PG or Mature + /// + /// UUID searchID = StartClassifiedSearch("foo bar", ClassifiedCategories.Any, ClassifiedQueryFlags.PG | ClassifiedQueryFlags.Mature); + /// + /// + /// + /// Responses are sent 16 at a time, there is no way to know how many results a query reply will contain however assuming + /// the reply packets arrived ordered, a response with less than 16 entries would indicate all results have been received + /// + public UUID StartClassifiedSearch(string searchText, ClassifiedCategories category, ClassifiedQueryFlags queryFlags) + { DirClassifiedQueryPacket query = new DirClassifiedQueryPacket(); UUID queryID = UUID.Random(); query.AgentData.AgentID = Client.Self.AgentID; query.AgentData.SessionID = Client.Self.SessionID; - query.QueryData.Category = (uint)categories; - query.QueryData.QueryFlags = (uint)(mature ? 0 : 2); + + query.QueryData.Category = (uint)category; + query.QueryData.QueryFlags = (uint)queryFlags; query.QueryData.QueryID = queryID; query.QueryData.QueryText = Utils.StringToBytes(searchText); @@ -474,7 +573,7 @@ namespace OpenMetaverse /// each query. public UUID StartLandSearch(SearchTypeFlags typeFlags) { - return StartLandSearch(DirFindFlags.SortAsc | DirFindFlags.PerMeterSort, typeFlags, 0, 0, 0); + return StartLandSearch(DirFindFlags.SortAsc | DirFindFlags.PerMeterSort, typeFlags, 0, 0, 0); } /// @@ -541,6 +640,7 @@ namespace OpenMetaverse return queryID; } + /// /// Starts a search for a Group in the directory manager /// @@ -561,6 +661,14 @@ namespace OpenMetaverse return StartGroupSearch(findFlags, searchText, queryStart, UUID.Random()); } + /// + /// Search for groups + /// + /// + /// + /// + /// + /// public UUID StartGroupSearch(DirFindFlags findFlags, string searchText, int queryStart, UUID queryID) { DirFindQueryPacket find = new DirFindQueryPacket(); @@ -574,11 +682,26 @@ namespace OpenMetaverse return queryID; } + /// + /// Search the people directory for other avatars + /// + /// + /// + /// + /// public UUID StartPeopleSearch(DirFindFlags findFlags, string searchText, int queryStart) { return StartPeopleSearch(findFlags, searchText, queryStart, UUID.Random()); } + /// + /// + /// + /// + /// + /// + /// + /// public UUID StartPeopleSearch(DirFindFlags findFlags, string searchText, int queryStart, UUID queryID) { DirFindQueryPacket find = new DirFindQueryPacket(); @@ -595,7 +718,7 @@ namespace OpenMetaverse } /// - /// Search "places" for Land you personally own + /// Search "places" for parcels of land you personally own /// public UUID StartPlacesSearch() { @@ -764,7 +887,7 @@ namespace OpenMetaverse classified.CreationDate = Utils.UnixTimeToDateTime(block.CreationDate); classified.ExpirationDate = Utils.UnixTimeToDateTime(block.ExpirationDate); - classified.Flags = block.ClassifiedFlags; + classified.Flags = (ClassifiedFlags)block.ClassifiedFlags; classified.ID = block.ClassifiedID; classified.Name = Utils.BytesToString(block.Name); classified.Price = block.PriceForListing; diff --git a/OpenMetaverse/PacketDecoder.cs b/OpenMetaverse/PacketDecoder.cs index 8cb0ad30..25416e6b 100644 --- a/OpenMetaverse/PacketDecoder.cs +++ b/OpenMetaverse/PacketDecoder.cs @@ -115,10 +115,13 @@ namespace OpenMetaverse.Packets AddCallback("TransferInfo.TargetType", DecodeTransferTargetType); AddCallback("TransferData.ChannelType", DecodeTransferChannelType); // Directory Manager + AddCallback("DirClassifiedQuery.QueryData.QueryFlags", DecodeDirClassifiedQueryFlags); AddCallback("QueryData.QueryFlags", DecodeDirQueryFlags); AddCallback("Category", DecodeCategory); AddCallback("QueryData.SearchType", SearchTypeFlags); + AddCallback("ClassifiedFlags", DecodeDirClassifiedFlags); + AddCallback("ParcelAccessListRequest.Data.Flags", DecodeParcelACL); AddCallback("ParcelAccessListReply.Data.Flags", DecodeParcelACL); //AddCallback("ParcelAccessListReply.List.Flags", DecodeParcelACLReply); @@ -845,6 +848,22 @@ namespace OpenMetaverse.Packets "(" + (DirectoryManager.DirFindFlags)(uint)fieldData + ")"); } + private static string DecodeDirClassifiedQueryFlags(string fieldName, object fieldData) + { + return String.Format("{0,30}: {1,-10} {2,-29} [ClassifiedQueryFlags]", + fieldName, + fieldData, + "(" + (DirectoryManager.ClassifiedQueryFlags)(uint)fieldData + ")"); + } + + private static string DecodeDirClassifiedFlags(string fieldName, object fieldData) + { + return String.Format("{0,30}: {1,-10} {2,-29} [ClassifiedFlags]", + fieldName, + fieldData, + "(" + (DirectoryManager.ClassifiedFlags)(byte)fieldData + ")"); + } + private static string DecodeParcelACL(string fieldName, object fieldData) { return String.Format("{0,30}: {1,-10} {2,-29} [AccessList]", diff --git a/OpenMetaverse/ParcelManager.cs b/OpenMetaverse/ParcelManager.cs index f8a0c727..9823548a 100644 --- a/OpenMetaverse/ParcelManager.cs +++ b/OpenMetaverse/ParcelManager.cs @@ -117,34 +117,34 @@ namespace OpenMetaverse } /// - /// + /// The tool to use when modifying terrain levels /// public enum TerraformAction : byte { - /// + /// Level the terrain Level = 0, - /// + /// Raise the terrain Raise = 1, - /// + /// Lower the terrain Lower = 2, - /// + /// Smooth the terrain Smooth = 3, - /// + /// Add random noise to the terrain Noise = 4, - /// + /// Revert terrain to simulator default Revert = 5 } /// - /// + /// The tool size to use when changing terrain levels /// public enum TerraformBrushSize : byte { - /// + /// Small Small = 1, - /// + /// Medium Medium = 2, - /// + /// Large Large = 4 } @@ -178,6 +178,7 @@ namespace OpenMetaverse /// the BorderWest and BorderSouth values are bit flags that get attached /// to the value stored in the first three bits. Bits four, five, and six /// are unused + [Flags] public enum ParcelOverlayType : byte { /// Public land diff --git a/Programs/examples/TestClient/Commands/Directory/SearchClassifiedsCommand.cs b/Programs/examples/TestClient/Commands/Directory/SearchClassifiedsCommand.cs new file mode 100644 index 00000000..fefed31c --- /dev/null +++ b/Programs/examples/TestClient/Commands/Directory/SearchClassifiedsCommand.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenMetaverse.TestClient.Commands +{ + class SearchClassifiedsCommand : Command + { + System.Threading.AutoResetEvent waitQuery = new System.Threading.AutoResetEvent(false); + int resultCount; + + public SearchClassifiedsCommand(TestClient testClient) + { + Name = "searchclassifieds"; + Description = "Searches Classified Ads. Usage: searchclassifieds [search text]"; + Category = CommandCategory.Other; + } + + public override string Execute(string[] args, UUID fromAgentID) + { + if (args.Length < 1) + return "Usage: searchclassifieds [search text]"; + + string searchText = string.Empty; + for (int i = 0; i < args.Length; i++) + searchText += args[i] + " "; + searchText = searchText.TrimEnd(); + waitQuery.Reset(); + + StringBuilder result = new StringBuilder(); + DirectoryManager.ClassifiedReplyCallback callback = delegate(List classifieds) + { + result.AppendFormat("Your search string '{0}' returned {1} classified ads" + System.Environment.NewLine, + searchText, classifieds.Count); + foreach (DirectoryManager.Classified ad in classifieds) + { + result.AppendLine(ad.ToString()); + } + + // classifieds are sent 16 ads at a time + if (classifieds.Count < 16) + { + waitQuery.Set(); + } + }; + + Client.Directory.OnClassifiedReply += callback; + + UUID searchID = Client.Directory.StartClassifiedSearch(searchText, DirectoryManager.ClassifiedCategories.Any, DirectoryManager.ClassifiedQueryFlags.Mature | DirectoryManager.ClassifiedQueryFlags.PG); + + if (!waitQuery.WaitOne(20000, false) && Client.Network.Connected) + { + result.AppendLine("Timeout waiting for simulator to respond to query."); + } + + Client.Directory.OnClassifiedReply -= callback; + + return result.ToString(); + } + } +}