/* * Copyright (c) 2006, Second Life Reverse Engineering Team * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the Second Life Reverse Engineering Team nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.Threading; using libsecondlife.Packets; namespace libsecondlife { /// /// /// public class DirectoryManager { /// /// The different categories a classified ad can be placed in /// public enum ClassifiedCategories { /// Any = 0, /// Shopping, /// LandRental, /// PropertyRental, /// SpecialAttraction, /// NewProducts, /// Employment, /// Wanted, /// Service, /// Personal } /// /// /// [Flags] public enum DirFindFlags { /// People = 1 << 0, /// Online = 1 << 1, /// [Obsolete] Places = 1 << 2, /// Events = 1 << 3, /// Groups = 1 << 4, /// DateEvents = 1 << 5, /// AgentOwned = 1 << 6, /// ForSale = 1 << 7, /// GroupOwned = 1 << 8, /// [Obsolete] Auction = 1 << 9, /// DwellSort = 1 << 10, /// PgSimsOnly = 1 << 11, /// PicturesOnly = 1 << 12, /// PgEventsOnly = 1 << 13, /// MatureSimsOnly = 1 << 14, /// SortAsc = 1 << 15, /// PricesSort = 1 << 16, /// PerMeterSort = 1 << 17, /// AreaSort = 1 << 18, /// NameSort = 1 << 19, /// LimitByPrice = 1 << 20, /// LimitByArea = 1 << 21 } /// /// /// [Flags] public enum SearchTypeFlags { /// None = 0, /// Auction = 1 << 1, /// Newbie = 1 << 2, /// Mainland = 1 << 3, /// Estate = 1 << 4 } /// /// A classified ad in Second Life /// public struct Classified { /// UUID for this ad, useful for looking up detailed /// information about it public LLUUID ID; /// The title of this classified ad public string Name; /// Unknown public byte 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; } /// /// A parcel retrieved from the dataserver such as results from the /// "For-Sale" listings /// public struct DirectoryParcel { /// public LLUUID ID; /// public string Name; /// public int ActualArea; /// public int SalePrice; /// public bool Auction; /// public bool ForSale; /// public bool ReservedNewbie; } /*/// public LLUUID OwnerID; /// public LLUUID SnapshotID; /// public ulong RegionHandle; /// public string SimName; /// public string Desc; /// public LLVector3 GlobalPosition; /// public LLVector3 SimPosition; /// public float Dwell;*/ /// /// /// /// public delegate void ClassifiedReplyCallback(List classifieds); /// /// /// /// public delegate void DirLandReplyCallback(List dirParcels); /// /// /// public event ClassifiedReplyCallback OnClassifiedReply; /// /// /// public event DirLandReplyCallback OnDirLandReply; private SecondLife Client; public DirectoryManager(SecondLife client) { Client = client; Client.Network.RegisterCallback(PacketType.DirClassifiedReply, new NetworkManager.PacketCallback(DirClassifiedReplyHandler)); Client.Network.RegisterCallback(PacketType.DirLandReply, new NetworkManager.PacketCallback(DirLandReplyHandler)); } public LLUUID StartClassifiedSearch(string searchText, ClassifiedCategories categories, bool mature) { DirClassifiedQueryPacket query = new DirClassifiedQueryPacket(); LLUUID queryID = LLUUID.Random(); query.AgentData.AgentID = Client.Network.AgentID; query.AgentData.SessionID = Client.Network.SessionID; query.QueryData.Category = (uint)categories; query.QueryData.QueryFlags = (uint)(mature ? 0 : 2); query.QueryData.QueryID = queryID; query.QueryData.QueryText = Helpers.StringToField(searchText); Client.Network.SendPacket(query); return queryID; } /// /// Starts a search for land sales using the directory /// /// What type of land to search for. Auction, /// estate, mainland, "first land", etc /// A unique identifier that can identify packets associated /// with this query from other queries /// The OnDirLandReply event handler must be registered before /// calling this function. There is no way to determine how many /// results will be returned, or how many times the callback will be /// fired other than you won't get more than 100 total parcels from /// each query. public LLUUID StartLandSearch(SearchTypeFlags typeFlags) { return StartLandSearch(DirFindFlags.SortAsc | DirFindFlags.PerMeterSort, typeFlags, 0, 0, 0); } /// /// Starts a search for land sales using the directory /// /// What type of land to search for. Auction, /// estate, mainland, "first land", etc /// Maximum price to search for /// Maximum area to search for /// Each request is limited to 100 parcels /// being returned. To get the first 100 parcels of a request use 0, /// from 100-199 use 1, 200-299 use 2, etc. /// A unique identifier that can identify packets associated /// with this query from other queries /// The OnDirLandReply event handler must be registered before /// calling this function. There is no way to determine how many /// results will be returned, or how many times the callback will be /// fired other than you won't get more than 100 total parcels from /// each query. public LLUUID StartLandSearch(SearchTypeFlags typeFlags, int priceLimit, int areaLimit, int queryStart) { return StartLandSearch(DirFindFlags.SortAsc | DirFindFlags.PerMeterSort | DirFindFlags.LimitByPrice | DirFindFlags.LimitByArea, typeFlags, priceLimit, areaLimit, queryStart); } /// /// Starts a search for land sales using the directory /// /// A flags parameter that can modify the way /// search results are returned, for example changing the ordering of /// results or limiting based on price or area /// What type of land to search for. Auction, /// estate, mainland, "first land", etc /// Maximum price to search for, the /// DirFindFlags.LimitByPrice flag must be set /// Maximum area to search for, the /// DirFindFlags.LimitByArea flag must be set /// Each request is limited to 100 parcels /// being returned. To get the first 100 parcels of a request use 0, /// from 100-199 use 1, 200-299 use 2, etc. /// A unique identifier that can identify packets associated /// with this query from other queries /// The OnDirLandReply event handler must be registered before /// calling this function. There is no way to determine how many /// results will be returned, or how many times the callback will be /// fired other than you won't get more than 100 total parcels from /// each query. public LLUUID StartLandSearch(DirFindFlags findFlags, SearchTypeFlags typeFlags, int priceLimit, int areaLimit, int queryStart) { LLUUID queryID = LLUUID.Random(); DirLandQueryPacket query = new DirLandQueryPacket(); query.AgentData.AgentID = Client.Network.AgentID; query.AgentData.SessionID = Client.Network.SessionID; query.QueryData.Area = areaLimit; query.QueryData.Price = priceLimit; query.QueryData.QueryStart = queryStart; query.QueryData.SearchType = (uint)typeFlags; query.QueryData.QueryFlags = (uint)findFlags; query.QueryData.QueryID = queryID; Client.Network.SendPacket(query); return queryID; } private void DirClassifiedReplyHandler(Packet packet, Simulator simulator) { if (OnClassifiedReply != null) { DirClassifiedReplyPacket reply = (DirClassifiedReplyPacket)packet; List classifieds = new List(); foreach (DirClassifiedReplyPacket.QueryRepliesBlock block in reply.QueryReplies) { Classified classified = new Classified(); classified.CreationDate = Helpers.UnixTimeToDateTime(block.CreationDate); classified.ExpirationDate = Helpers.UnixTimeToDateTime(block.ExpirationDate); classified.Flags = block.ClassifiedFlags; classified.ID = block.ClassifiedID; classified.Name = Helpers.FieldToString(block.Name); classified.Price = block.PriceForListing; classifieds.Add(classified); } try { OnClassifiedReply(classifieds); } catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); } } } private void DirLandReplyHandler(Packet packet, Simulator simulator) { if (OnDirLandReply != null) { List parcelsForSale = new List(); DirLandReplyPacket reply = (DirLandReplyPacket)packet; foreach (DirLandReplyPacket.QueryRepliesBlock block in reply.QueryReplies) { DirectoryParcel dirParcel = new DirectoryParcel(); dirParcel.ActualArea = block.ActualArea; dirParcel.ID = block.ParcelID; dirParcel.Name = Helpers.FieldToString(block.Name); dirParcel.SalePrice = block.SalePrice; dirParcel.Auction = block.Auction; dirParcel.ForSale = block.ForSale; dirParcel.ReservedNewbie = block.ReservedNewbie; parcelsForSale.Add(dirParcel); } try { OnDirLandReply(parcelsForSale); } catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); } } } } }