/* * Copyright (c) 2006-2008, openmetaverse.org * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the openmetaverse.org nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.Threading; using System.Reflection; using OpenMetaverse.Packets; using OpenMetaverse.StructuredData; namespace OpenMetaverse { #region Structs /// /// Some information about a parcel of land returned from a DirectoryManager search /// public struct ParcelInfo { /// Global Key of record public UUID ID; /// Parcel Owners public UUID OwnerID; /// Name field of parcel, limited to 128 characters public string Name; /// Description field of parcel, limited to 256 characters public string Description; /// Total Square meters of parcel public int ActualArea; /// Total area billable as Tier, for group owned land this will be 10% less than ActualArea public int BillableArea; /// True of parcel is in Mature simulator public bool Mature; /// Grid global X position of parcel public float GlobalX; /// Grid global Y position of parcel public float GlobalY; /// Grid global Z position of parcel (not used) public float GlobalZ; /// Name of simulator parcel is located in public string SimName; /// Texture of parcels display picture public UUID SnapshotID; /// Float representing calculated traffic based on time spent on parcel by avatars public float Dwell; /// Sale price of parcel (not used) public int SalePrice; /// Auction ID of parcel public int AuctionID; } /// /// Parcel Media Information /// public struct ParcelMedia { /// A byte, if 0x1 viewer should auto scale media to fit object public byte MediaAutoScale; /// A boolean, if true the viewer should loop the media public bool MediaLoop; /// The Asset UUID of the Texture which when applied to a /// primitive will display the media public UUID MediaID; /// A URL which points to any Quicktime supported media type public string MediaURL; /// A description of the media public string MediaDesc; /// An Integer which represents the height of the media public int MediaHeight; /// An integer which represents the width of the media public int MediaWidth; /// A string which contains the mime type of the media public string MediaType; } #endregion Structs #region Parcel Class /// /// Parcel of land, a portion of virtual real estate in a simulator /// public struct Parcel { #region Enums /// /// Various parcel properties /// [Flags] public enum ParcelFlags : uint { /// No flags set None = 0, /// Allow avatars to fly (a client-side only restriction) AllowFly = 1 << 0, /// Allow foreign scripts to run AllowOtherScripts = 1 << 1, /// This parcel is for sale ForSale = 1 << 2, /// Allow avatars to create a landmark on this parcel AllowLandmark = 1 << 3, /// Allows all avatars to edit the terrain on this parcel AllowTerraform = 1 << 4, /// Avatars have health and can take damage on this parcel. /// If set, avatars can be killed and sent home here AllowDamage = 1 << 5, /// Foreign avatars can create objects here CreateObjects = 1 << 6, /// All objects on this parcel can be purchased ForSaleObjects = 1 << 7, /// Access is restricted to a group UseAccessGroup = 1 << 8, /// Access is restricted to a whitelist UseAccessList = 1 << 9, /// Ban blacklist is enabled UseBanList = 1 << 10, /// Unknown UsePassList = 1 << 11, /// List this parcel in the search directory ShowDirectory = 1 << 12, /// Allow personally owned parcels to be deeded to group AllowDeedToGroup = 1 << 13, /// If Deeded, owner contributes required tier to group parcel is deeded to ContributeWithDeed = 1 << 14, /// Restrict sounds originating on this parcel to the /// parcel boundaries SoundLocal = 1 << 15, /// Objects on this parcel are sold when the land is /// purchsaed SellParcelObjects = 1 << 16, /// Allow this parcel to be published on the web AllowPublish = 1 << 17, /// The information for this parcel is mature content MaturePublish = 1 << 18, /// The media URL is an HTML page UrlWebPage = 1 << 19, /// The media URL is a raw HTML string UrlRawHtml = 1 << 20, /// Restrict foreign object pushes RestrictPushObject = 1 << 21, /// Ban all non identified/transacted avatars DenyAnonymous = 1 << 22, // Ban all identified avatars [OBSOLETE] //[Obsolete] //DenyIdentified = 1 << 23, // Ban all transacted avatars [OBSOLETE] //[Obsolete] //DenyTransacted = 1 << 24, /// Allow group-owned scripts to run AllowGroupScripts = 1 << 25, /// Allow object creation by group members or group /// objects CreateGroupObjects = 1 << 26, /// Allow all objects to enter this parcel AllowAllObjectEntry = 1 << 27, /// Only allow group and owner objects to enter this parcel AllowGroupObjectEntry = 1 << 28, /// Voice Enabled on this parcel AllowVoiceChat = 1 << 29, /// Use Estate Voice channel for Voice on this parcel UseEstateVoiceChan = 1 << 30, /// Deny Age Unverified Users DenyAgeUnverified = 1U << 31 } /// /// Parcel ownership status /// public enum ParcelStatus : sbyte { /// Placeholder None = -1, /// Parcel is leased (owned) by an avatar or group Leased = 0, /// Parcel is in process of being leased (purchased) by an avatar or group LeasePending = 1, /// Parcel has been abandoned back to Governor Linden Abandoned = 2 } /// /// Category parcel is listed in under search /// public enum ParcelCategory : sbyte { /// No assigned category None = 0, /// Linden Infohub or public area Linden, /// Adult themed area Adult, /// Arts and Culture Arts, /// Business Business, /// Educational Educational, /// Gaming Gaming, /// Hangout or Club Hangout, /// Newcomer friendly Newcomer, /// Parks and Nature Park, /// Residential Residential, /// Shopping Shopping, /// Not Used? Stage, /// Other Other, /// Not an actual category, only used for queries Any = -1 } #endregion Enums /// public int RequestResult; /// public int SequenceID; /// Used by the viewer in conjunction with the BitMap /// for highlighting the borders of a parcel public bool SnapSelection; /// public int SelfCount; /// public int OtherCount; /// public int PublicCount; /// Simulator-local ID of this parcel public int LocalID; /// UUID of the owner of this parcel public UUID OwnerID; /// Whether the land is deeded to a group or not public bool IsGroupOwned; /// public uint AuctionID; /// Date land was claimed public DateTime ClaimDate; /// Appears to always be zero public int ClaimPrice; /// This field is no longer used public int RentPrice; /// Minimum corner of the axis-aligned bounding box for this /// parcel public Vector3 AABBMin; /// Maximum corner of the axis-aligned bounding box for this /// parcel public Vector3 AABBMax; /// Bitmap describing land layout in 4x4m squares across the /// entire region public byte[] Bitmap; /// Total parcel land area public int Area; /// public ParcelStatus Status; /// Maximum primitives across the entire simulator public int SimWideMaxPrims; /// Total primitives across the entire simulator public int SimWideTotalPrims; /// Maximum number of primitives this parcel supports public int MaxPrims; /// Total number of primitives on this parcel public int TotalPrims; /// Total number of primitives owned by the parcel owner on /// this parcel public int OwnerPrims; /// Total number of primitives owned by the parcel group on /// this parcel public int GroupPrims; /// Total number of other primitives on this parcel public int OtherPrims; /// Total number of primitives you are currently selecting and /// sitting on public int SelectedPrims; /// public float ParcelPrimBonus; /// Autoreturn value in minutes for others' objects public int OtherCleanTime; /// public ParcelFlags Flags; /// Sale price of the parcel, only useful if ForSale is set /// The SalePrice will remain the same after an ownership /// transfer (sale), so it can be used to see the purchase price after /// a sale if the new owner has not changed it public int SalePrice; /// Parcel Name public string Name; /// Parcel Description public string Desc; /// URL For Music Stream public string MusicURL; /// public UUID GroupID; /// Price for a temporary pass public int PassPrice; /// How long is pass valid for public float PassHours; /// public ParcelCategory Category; /// Key of authorized buyer public UUID AuthBuyerID; /// Key of parcel snapshot public UUID SnapshotID; /// public Vector3 UserLocation; /// public Vector3 UserLookAt; /// public byte LandingType; /// public float Dwell; /// public bool RegionDenyAnonymous; /// public bool RegionPushOverride; /// Simulator Object, containing details from Simulator class public Simulator Simulator; /// Access list of who is whitelisted or blacklisted on this /// parcel public List AccessList; /// TRUE of region denies access to age unverified users public bool RegionDenyAgeUnverified; /// true to obscure (hide) media url public bool ObscureMedia; /// true to obscure (hide) music url public bool ObscureMusic; /// A struct containing media details public ParcelMedia Media; /// /// Displays a parcel object in string format /// /// string containing key=value pairs of a parcel object public override string ToString() { string result = ""; Type parcelType = this.GetType(); FieldInfo[] fields = parcelType.GetFields(); foreach (FieldInfo field in fields) { result += (field.Name + " = " + field.GetValue(this) + " "); } return result; } /// /// Defalt constructor /// /// Simulator this parcel resides in /// Local ID of this parcel public Parcel(Simulator simulator, int localID) { Simulator = simulator; LocalID = localID; RequestResult = 0; SequenceID = 0; SnapSelection = false; SelfCount = 0; OtherCount = 0; PublicCount = 0; OwnerID = UUID.Zero; IsGroupOwned = false; AuctionID = 0; ClaimDate = Helpers.Epoch; ClaimPrice = 0; RentPrice = 0; AABBMin = Vector3.Zero; AABBMax = Vector3.Zero; Bitmap = new byte[0]; Area = 0; Status = ParcelStatus.None; SimWideMaxPrims = 0; SimWideTotalPrims = 0; MaxPrims = 0; TotalPrims = 0; OwnerPrims = 0; GroupPrims = 0; OtherPrims = 0; SelectedPrims = 0; ParcelPrimBonus = 0; OtherCleanTime = 0; Flags = ParcelFlags.None; SalePrice = 0; Name = String.Empty; Desc = String.Empty; MusicURL = String.Empty; GroupID = UUID.Zero; PassPrice = 0; PassHours = 0; Category = ParcelCategory.None; AuthBuyerID = UUID.Zero; SnapshotID = UUID.Zero; UserLocation = Vector3.Zero; UserLookAt = Vector3.Zero; LandingType = 0x0; Dwell = 0; RegionDenyAnonymous = false; RegionPushOverride = false; AccessList = new List(0); RegionDenyAgeUnverified = false; Media = new ParcelMedia(); ObscureMedia = false; ObscureMusic = false; } /// /// Update the simulator with any local changes to this Parcel object /// /// Whether we want the simulator to confirm /// the update with a reply packet or not public void Update(bool wantReply) { ParcelPropertiesUpdatePacket request = new ParcelPropertiesUpdatePacket(); request.AgentData.AgentID = Simulator.Client.Self.AgentID; request.AgentData.SessionID = Simulator.Client.Self.SessionID; request.ParcelData.LocalID = this.LocalID; request.ParcelData.AuthBuyerID = this.AuthBuyerID; request.ParcelData.Category = (byte)this.Category; request.ParcelData.Desc = Helpers.StringToField(this.Desc); request.ParcelData.GroupID = this.GroupID; request.ParcelData.LandingType = this.LandingType; request.ParcelData.MediaAutoScale = this.Media.MediaAutoScale; request.ParcelData.MediaID = this.Media.MediaID; request.ParcelData.MediaURL = Helpers.StringToField(this.Media.MediaURL); request.ParcelData.MusicURL = Helpers.StringToField(this.MusicURL); request.ParcelData.Name = Helpers.StringToField(this.Name); if (wantReply) request.ParcelData.Flags = 1; request.ParcelData.ParcelFlags = (uint)this.Flags; request.ParcelData.PassHours = this.PassHours; request.ParcelData.PassPrice = this.PassPrice; request.ParcelData.SalePrice = this.SalePrice; request.ParcelData.SnapshotID = this.SnapshotID; request.ParcelData.UserLocation = this.UserLocation; request.ParcelData.UserLookAt = this.UserLookAt; Simulator.Client.Network.SendPacket(request, Simulator); UpdateOtherCleanTime(); } /// /// Set Autoreturn time /// public void UpdateOtherCleanTime() { ParcelSetOtherCleanTimePacket request = new ParcelSetOtherCleanTimePacket(); request.AgentData.AgentID = Simulator.Client.Self.AgentID; request.AgentData.SessionID = Simulator.Client.Self.SessionID; request.ParcelData.LocalID = this.LocalID; request.ParcelData.OtherCleanTime = this.OtherCleanTime; Simulator.Client.Network.SendPacket(request, Simulator); } } #endregion Parcel Class /// /// Parcel (subdivided simulator lots) subsystem /// public class ParcelManager { #region Enums /// /// Type of return to use when returning objects from a parcel /// public enum ObjectReturnType : uint { /// None = 0, /// Return objects owned by parcel owner Owner = 1 << 1, /// Return objects set to group Group = 1 << 2, /// Return objects not owned by parcel owner or set to group Other = 1 << 3, /// Return a specific list of objects on parcel List = 1 << 4, /// Return objects that are marked for-sale Sell = 1 << 5 } /// /// Blacklist/Whitelist flags used in parcels Access List /// public enum ParcelAccessFlags : uint { /// Agent is denied access NoAccess = 0, /// Agent is granted access Access = 1 } /// /// The result of a request for parcel properties /// public enum ParcelResult : int { /// No matches were found for the request NoData = -1, /// Request matched a single parcel Single = 0, /// Request matched multiple parcels Multiple = 1 } /// /// Flags used in the ParcelAccessListRequest packet to specify whether /// we want the access list (whitelist), ban list (blacklist), or both /// [Flags] public enum AccessList : uint { /// Request the access list Access = 1 << 0, /// Request the ban list Ban = 1 << 1, /// Request both the access list and ban list Both = Access | Ban } /// /// Simulator sent Sequence IDs for ParcelPropertiesReply packets (sent when avatar tries to cross /// parcel border) /// public enum SequenceStatus : int { /// Parcel currently selected ParcelSelected = -10000, /// Parcel restricted to group avatar not member of Collision_Not_In_Group = -20000, /// Avatar banned from parcel Collision_Banned = -30000, /// Parcel restricted to access list in which avatar is not on. Collision_Not_On_AccessList = -40000, /// response to hovered over parcel Hovered_Over_Parcel = -50000 } /// /// /// public enum TerraformAction : byte { /// Level = 0, /// Raise = 1, /// Lower = 2, /// Smooth = 3, /// Noise = 4, /// Revert = 5 } /// /// /// public enum TerraformBrushSize : byte { /// Small = 1, /// Medium = 2, /// Large = 4 } /// /// Reasons agent is denied access to a parcel on the simulator /// public enum AccessDeniedReason : byte { /// Agent is not denied, access is granted NotDenied = 0, /// Agent is not a member of the group set for the parcel, or which owns the parcel NotInGroup = 1, /// Agent is not on the parcels specific allow list NotOnAllowList = 2, /// Agent is on the parcels ban list BannedFromParcel = 3, /// Unknown NoAccess = 4, /// Agent is not age verified and parcel settings deny access to non age verified avatars NotAgeVerified = 5 } #endregion Enums #region Structs /// /// Parcel Accesslist /// public struct ParcelAccessEntry { /// Agents public UUID AgentID; /// public DateTime Time; /// Flag to Permit access to agent, or ban agent from parcel public AccessList Flags; } /// /// Owners of primitives on parcel /// public struct ParcelPrimOwners { /// Prim Owners public UUID OwnerID; /// True of owner is group public bool IsGroupOwned; /// Total count of prims owned by OwnerID public int Count; /// true of OwnerID is currently online and is not a group public bool OnlineStatus; } #endregion Structs #region Delegates /// /// /// /// UUID of the requested parcel /// Simulator-local ID of the requested parcel /// Dwell value of the requested parcel public delegate void ParcelDwellCallback(UUID parcelID, int localID, float dwell); /// /// /// /// public delegate void ParcelInfoCallback(ParcelInfo parcel); /// /// /// /// Full properties for a single parcel. If result /// is NoData this will be incomplete or incorrect data /// Success of the query /// User-assigned identifier for the query /// User-assigned boolean for the query public delegate void ParcelPropertiesCallback(Parcel parcel, ParcelResult result, int sequenceID, bool snapSelection); /// /// /// /// simulator parcel is in /// /// /// /// public delegate void ParcelAccessListReplyCallback(Simulator simulator, int sequenceID, int localID, uint flags, List accessEntries); /// /// Responses to a request for prim owners on a parcel /// /// simulator parcel is in /// List containing details or prim ownership public delegate void ParcelObjectOwnersListReplyCallback(Simulator simulator, List primOwners); /// /// Fired when all parcels are downloaded from simulator /// /// Simulator the parcel is in /// Read-only dictionary containing parcel details for the simulator /// 64,64 array containing sim position to localID mapping public delegate void SimParcelsDownloaded(Simulator simulator, InternalDictionary simParcels, int[,] parcelMap); /// /// Fired in response to SelectParcelObjects /// /// simulator the objects are in /// Local IDs of the selected objects /// If true, list is start of a new selection public delegate void ForceSelectObjects(Simulator simulator, List objectIDs, bool resetList); /// /// Fired when a ParcelMediaUpdate packet is received, this occurs when the media on the parcel an avatar /// is over changes /// /// A reference to the simulator object /// A struct containing updated media information public delegate void ParcelMediaUpdateReplyCallback(Simulator simulator, ParcelMedia media); #endregion Delegates #region Events /// Fired when a is received, /// in response to a public event ParcelDwellCallback OnParcelDwell; /// Fired when a is received, /// in response to a public event ParcelInfoCallback OnParcelInfo; /// Fired when a ParcelProperties Packet is received over the subsystem, /// in response to a public event ParcelPropertiesCallback OnParcelProperties; /// Fired when a is received, /// in response to a public event ParcelAccessListReplyCallback OnAccessListReply; /// Fired when the Agent receives a , /// in response to public event ParcelObjectOwnersListReplyCallback OnPrimOwnersListReply; /// Fired when the simulator parcel dictionary is populated in response /// to a request public event SimParcelsDownloaded OnSimParcelsDownloaded; /// Fired when the Agent receives a , /// in response to a request public event ForceSelectObjects OnParcelSelectedObjects; /// Fired when the Agent receives a which /// occurs when the parcel media information is changed for the current parcel the Agent is over public event ParcelMediaUpdateReplyCallback OnParcelMediaUpdate; #endregion Events private GridClient Client; #region Public Methods /// /// Default constructor /// /// A reference to the GridClient object public ParcelManager(GridClient client) { Client = client; // Setup the callbacks Client.Network.RegisterCallback(PacketType.ParcelInfoReply, new NetworkManager.PacketCallback(ParcelInfoReplyHandler)); // UDP packet handler Client.Network.RegisterCallback(PacketType.ParcelProperties, new NetworkManager.PacketCallback(ParcelPropertiesHandler)); // CAPS packet handler, to allow for Media Data not contained in the message template Client.Network.RegisterEventCallback("ParcelProperties", new Caps.EventQueueCallback(ParcelPropertiesReplyHandler)); Client.Network.RegisterCallback(PacketType.ParcelDwellReply, new NetworkManager.PacketCallback(ParcelDwellReplyHandler)); Client.Network.RegisterCallback(PacketType.ParcelAccessListReply, new NetworkManager.PacketCallback(ParcelAccessListReplyHandler)); Client.Network.RegisterCallback(PacketType.ParcelObjectOwnersReply, new NetworkManager.PacketCallback(ParcelObjectOwnersReplyHandler)); Client.Network.RegisterCallback(PacketType.ForceObjectSelect, new NetworkManager.PacketCallback(SelectParcelObjectsReplyHandler)); Client.Network.RegisterCallback(PacketType.ParcelMediaUpdate, new NetworkManager.PacketCallback(ParcelMediaUpdateHandler)); } /// /// Request basic information for a single parcel /// /// Simulator-local ID of the parcel public void InfoRequest(UUID parcelID) { ParcelInfoRequestPacket request = new ParcelInfoRequestPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.Data.ParcelID = parcelID; Client.Network.SendPacket(request); } /// /// Request properties of a single parcel /// /// Simulator containing the parcel /// Simulator-local ID of the parcel /// An arbitrary integer that will be returned /// with the ParcelProperties reply, useful for distinguishing between /// multiple simultaneous requests public void PropertiesRequest(Simulator simulator, int localID, int sequenceID) { ParcelPropertiesRequestByIDPacket request = new ParcelPropertiesRequestByIDPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.ParcelData.LocalID = localID; request.ParcelData.SequenceID = sequenceID; Client.Network.SendPacket(request, simulator); } /// /// Request the access list for a single parcel /// /// Simulator containing the parcel /// Simulator-local ID of the parcel /// An arbitrary integer that will be returned /// with the ParcelAccessList reply, useful for distinguishing between /// multiple simultaneous requests /// public void AccessListRequest(Simulator simulator, int localID, AccessList flags, int sequenceID) { ParcelAccessListRequestPacket request = new ParcelAccessListRequestPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.Data.LocalID = localID; request.Data.Flags = (uint)flags; request.Data.SequenceID = sequenceID; Client.Network.SendPacket(request, simulator); } /// /// Request properties of parcels using a bounding box selection /// /// Simulator containing the parcel /// Northern boundary of the parcel selection /// Eastern boundary of the parcel selection /// Southern boundary of the parcel selection /// Western boundary of the parcel selection /// An arbitrary integer that will be returned /// with the ParcelProperties reply, useful for distinguishing between /// different types of parcel property requests /// A boolean that is returned with the /// ParcelProperties reply, useful for snapping focus to a single /// parcel public void PropertiesRequest(Simulator simulator, float north, float east, float south, float west, int sequenceID, bool snapSelection) { ParcelPropertiesRequestPacket request = new ParcelPropertiesRequestPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.ParcelData.North = north; request.ParcelData.East = east; request.ParcelData.South = south; request.ParcelData.West = west; request.ParcelData.SequenceID = sequenceID; request.ParcelData.SnapSelection = snapSelection; Client.Network.SendPacket(request, simulator); } /// /// Request all simulator parcel properties (used for populating the Simulator.Parcels /// dictionary) /// /// Simulator to request parcels from (must be connected) public void RequestAllSimParcels(Simulator simulator) { RequestAllSimParcels(simulator, false, 200); } /// /// Request all simulator parcel properties (used for populating the Simulator.Parcels /// dictionary) /// /// Simulator to request parcels from (must be connected) /// If TRUE, will force a full refresh /// Number of milliseconds to pause in between each request public void RequestAllSimParcels(Simulator simulator, bool refresh, int msDelay) { if (refresh) { //lock (simulator.ParcelMap) for (int y = 0; y < 64; y++) for (int x = 0; x < 64; x++) simulator.ParcelMap[y, x] = 0; } Thread th = new Thread(delegate() { int y, x; for (y = 0; y < 64; y++) { for (x = 0; x < 64; x++) { if (!Client.Network.Connected) return; if (simulator.ParcelMap[y, x] == 0) { Client.Parcels.PropertiesRequest(simulator, (y + 1) * 4.0f, (x + 1) * 4.0f, y * 4.0f, x * 4.0f, 0, false); // Pause after every request to avoid flooding the sim System.Threading.Thread.Sleep(msDelay); } } } }); th.Start(); } /// /// Request the dwell value for a parcel /// /// Simulator containing the parcel /// Simulator-local ID of the parcel public void DwellRequest(Simulator simulator, int localID) { ParcelDwellRequestPacket request = new ParcelDwellRequestPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.Data.LocalID = localID; request.Data.ParcelID = UUID.Zero; // Not used by clients Client.Network.SendPacket(request, simulator); } /// /// Send a request to Purchase a parcel of land /// /// The Simulator the parcel is located in /// The parcels region specific local ID /// true if this parcel is being purchased by a group /// The groups /// true to remove tier contribution if purchase is successful /// The parcels size /// The purchase price of the parcel /// public void Buy(Simulator simulator, int localID, bool forGroup, UUID groupID, bool removeContribution, int parcelArea, int parcelPrice) { ParcelBuyPacket request = new ParcelBuyPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.Data.Final = true; request.Data.GroupID = groupID; request.Data.LocalID = localID; request.Data.IsGroupOwned = forGroup; request.Data.RemoveContribution = removeContribution; request.ParcelData.Area = parcelArea; request.ParcelData.Price = parcelPrice; Client.Network.SendPacket(request, simulator); } /// /// Reclaim a parcel of land /// /// The simulator the parcel is in /// The parcels region specific local ID public void Reclaim(Simulator simulator, int localID) { ParcelReclaimPacket request = new ParcelReclaimPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.Data.LocalID = localID; Client.Network.SendPacket(request, simulator); } /// /// Deed a parcel to a group /// /// The simulator the parcel is in /// The parcels region specific local ID /// The groups public void DeedToGroup(Simulator simulator, int localID, UUID groupID) { ParcelDeedToGroupPacket request = new ParcelDeedToGroupPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.Data.LocalID = localID; request.Data.GroupID = groupID; Client.Network.SendPacket(request, simulator); } /// /// Request prim owners of a parcel of land. /// /// Simulator parcel is in /// The parcels region specific local ID public void ObjectOwnersRequest(Simulator simulator, int localID) { ParcelObjectOwnersRequestPacket request = new ParcelObjectOwnersRequestPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.ParcelData.LocalID = localID; Client.Network.SendPacket(request, simulator); } /// /// Return objects from a parcel /// /// Simulator parcel is in /// The parcels region specific local ID /// the type of objects to return, /// A list containing object owners s to return public void ReturnObjects(Simulator simulator, int localID, ObjectReturnType type, List ownerIDs) { ParcelReturnObjectsPacket request = new ParcelReturnObjectsPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.ParcelData.LocalID = localID; request.ParcelData.ReturnType = (uint)type; // A single null TaskID is (not) used for parcel object returns request.TaskIDs = new ParcelReturnObjectsPacket.TaskIDsBlock[1]; request.TaskIDs[0] = new ParcelReturnObjectsPacket.TaskIDsBlock(); request.TaskIDs[0].TaskID = UUID.Zero; // Convert the list of owner UUIDs to packet blocks if a list is given if (ownerIDs != null) { request.OwnerIDs = new ParcelReturnObjectsPacket.OwnerIDsBlock[ownerIDs.Count]; for (int i = 0; i < ownerIDs.Count; i++) { request.OwnerIDs[i] = new ParcelReturnObjectsPacket.OwnerIDsBlock(); request.OwnerIDs[i].OwnerID = ownerIDs[i]; } } else { request.OwnerIDs = new ParcelReturnObjectsPacket.OwnerIDsBlock[0]; } Client.Network.SendPacket(request, simulator); } /// /// Subdivide (split) a parcel /// /// /// /// /// /// public void ParcelSubdivide(Simulator simulator, float west, float south, float east, float north) { ParcelDividePacket divide = new ParcelDividePacket(); divide.AgentData.AgentID = Client.Self.AgentID; divide.AgentData.SessionID = Client.Self.SessionID; divide.ParcelData.East = east; divide.ParcelData.North = north; divide.ParcelData.South = south; divide.ParcelData.West = west; Client.Network.SendPacket(divide, simulator); } /// /// Join two parcels of land creating a single parcel /// /// /// /// /// /// public void ParcelJoin(Simulator simulator, float west, float south, float east, float north) { ParcelJoinPacket join = new ParcelJoinPacket(); join.AgentData.AgentID = Client.Self.AgentID; join.AgentData.SessionID = Client.Self.SessionID; join.ParcelData.East = east; join.ParcelData.North = north; join.ParcelData.South = south; join.ParcelData.West = west; Client.Network.SendPacket(join, simulator); } /// /// Get a parcels LocalID /// /// Simulator parcel is in /// llVector3 position in simulator (Z not used) /// 0 on failure, or parcel LocalID on success. /// A call to Parcels.RequestAllSimParcels is required to populate map and /// dictionary. public int GetParcelLocalID(Simulator simulator, Vector3 position) { return simulator.ParcelMap[(byte)position.Y / 4, (byte)position.X / 4]; } /// /// Terraform (raise, lower, etc) an area or whole parcel of land /// /// Simulator land area is in. /// LocalID of parcel, or -1 if using bounding box /// From Enum, Raise, Lower, Level, Smooth, Etc. /// Size of area to modify /// true on successful request sent. /// Settings.STORE_LAND_PATCHES must be true, /// Parcel information must be downloaded using RequestAllSimParcels() public bool Terraform(Simulator simulator, int localID, TerraformAction action, TerraformBrushSize brushSize) { return Terraform(simulator, localID, 0f, 0f, 0f, 0f, action, brushSize, 1); } /// /// Terraform (raise, lower, etc) an area or whole parcel of land /// /// Simulator land area is in. /// west border of area to modify /// south border of area to modify /// east border of area to modify /// north border of area to modify /// From Enum, Raise, Lower, Level, Smooth, Etc. /// Size of area to modify /// true on successful request sent. /// Settings.STORE_LAND_PATCHES must be true, /// Parcel information must be downloaded using RequestAllSimParcels() public bool Terraform(Simulator simulator, float west, float south, float east, float north, TerraformAction action, TerraformBrushSize brushSize) { return Terraform(simulator, -1, west, south, east, north, action, brushSize, 1); } /// /// Terraform (raise, lower, etc) an area or whole parcel of land /// /// Simulator land area is in. /// LocalID of parcel, or -1 if using bounding box /// west border of area to modify /// south border of area to modify /// east border of area to modify /// north border of area to modify /// From Enum, Raise, Lower, Level, Smooth, Etc. /// Size of area to modify /// How many meters + or - to lower, 1 = 1 meter /// true on successful request sent. /// Settings.STORE_LAND_PATCHES must be true, /// Parcel information must be downloaded using RequestAllSimParcels() public bool Terraform(Simulator simulator, int localID, float west, float south, float east, float north, TerraformAction action, TerraformBrushSize brushSize, int seconds) { float height = 0f; int x, y; if (localID == -1) { x = (int)east - (int)west / 2; y = (int)north - (int)south / 2; } else { Parcel p; if (!simulator.Parcels.TryGetValue(localID, out p)) { Logger.Log(String.Format("Can't find parcel {0} in simulator {1}", localID, simulator), Helpers.LogLevel.Warning, Client); return false; } x = (int)p.AABBMax.X - (int)p.AABBMin.X / 2; y = (int)p.AABBMax.Y - (int)p.AABBMin.Y / 2; } if (!Client.Terrain.TerrainHeightAtPoint(simulator.Handle, x, y, out height)) { Logger.Log("Land Patch not stored for location", Helpers.LogLevel.Warning, Client); return false; } Terraform(simulator, localID, west, south, east, north, action, brushSize, seconds, height); return true; } /// /// Terraform (raise, lower, etc) an area or whole parcel of land /// /// Simulator land area is in. /// LocalID of parcel, or -1 if using bounding box /// west border of area to modify /// south border of area to modify /// east border of area to modify /// north border of area to modify /// From Enum, Raise, Lower, Level, Smooth, Etc. /// Size of area to modify /// How many meters + or - to lower, 1 = 1 meter /// Height at which the terraform operation is acting at public void Terraform(Simulator simulator, int localID, float west, float south, float east, float north, TerraformAction action, TerraformBrushSize brushSize, int seconds, float height) { ModifyLandPacket land = new ModifyLandPacket(); land.AgentData.AgentID = Client.Self.AgentID; land.AgentData.SessionID = Client.Self.SessionID; land.ModifyBlock.Action = (byte)action; land.ModifyBlock.BrushSize = (byte)brushSize; land.ModifyBlock.Seconds = seconds; land.ModifyBlock.Height = height; land.ParcelData[0] = new ModifyLandPacket.ParcelDataBlock(); land.ParcelData[0].LocalID = localID; land.ParcelData[0].West = west; land.ParcelData[0].South = south; land.ParcelData[0].East = east; land.ParcelData[0].North = north; Client.Network.SendPacket(land, simulator); } /// /// Sends a request to the simulator to return a list of objects owned by specific owners /// /// Simulator local ID of parcel /// Owners, Others, Etc /// List containing keys of avatars objects to select; /// if List is null will return Objects of type selectType /// Response data is returned in the event public void SelectObjects(int localID, ObjectReturnType selectType, UUID ownerID) { if (OnParcelSelectedObjects != null) { ParcelSelectObjectsPacket select = new ParcelSelectObjectsPacket(); select.AgentData.AgentID = Client.Self.AgentID; select.AgentData.SessionID = Client.Self.SessionID; select.ParcelData.LocalID = localID; select.ParcelData.ReturnType = (uint)selectType; select.ReturnIDs = new ParcelSelectObjectsPacket.ReturnIDsBlock[1]; select.ReturnIDs[0] = new ParcelSelectObjectsPacket.ReturnIDsBlock(); select.ReturnIDs[0].ReturnID = ownerID; Client.Network.SendPacket(select); } } /// /// Eject and optionally ban a user from a parcel /// /// target key of avatar to eject /// true to also ban target public void EjectUser(UUID targetID, bool ban) { EjectUserPacket eject = new EjectUserPacket(); eject.AgentData.AgentID = Client.Self.AgentID; eject.AgentData.SessionID = Client.Self.SessionID; eject.Data.TargetID = targetID; if (ban) eject.Data.Flags = 1; else eject.Data.Flags = 0; Client.Network.SendPacket(eject); } /// /// Freeze or unfreeze an avatar over your land /// /// target key to freeze /// true to freeze, false to unfreeze public void FreezeUser(UUID targetID, bool freeze) { FreezeUserPacket frz = new FreezeUserPacket(); frz.AgentData.AgentID = Client.Self.AgentID; frz.AgentData.SessionID = Client.Self.SessionID; frz.Data.TargetID = targetID; if (freeze) frz.Data.Flags = 0; else frz.Data.Flags = 1; Client.Network.SendPacket(frz); } /// /// Abandon a parcel of land /// /// Simulator parcel is in /// Simulator local ID of parcel public void ReleaseParcel(Simulator simulator, int localID) { ParcelReleasePacket abandon = new ParcelReleasePacket(); abandon.AgentData.AgentID = Client.Self.AgentID; abandon.AgentData.SessionID = Client.Self.SessionID; abandon.Data.LocalID = localID; Client.Network.SendPacket(abandon, simulator); } #endregion Public Methods #region Packet Handlers private void ParcelDwellReplyHandler(Packet packet, Simulator simulator) { if (OnParcelDwell != null || Client.Settings.ALWAYS_REQUEST_PARCEL_DWELL == true) { ParcelDwellReplyPacket dwell = (ParcelDwellReplyPacket)packet; lock (simulator.Parcels.Dictionary) { if (simulator.Parcels.Dictionary.ContainsKey(dwell.Data.LocalID)) { Parcel parcel = simulator.Parcels.Dictionary[dwell.Data.LocalID]; parcel.Dwell = dwell.Data.Dwell; simulator.Parcels.Dictionary[dwell.Data.LocalID] = parcel; } } if (OnParcelDwell != null) { try { OnParcelDwell(dwell.Data.ParcelID, dwell.Data.LocalID, dwell.Data.Dwell); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } } private void ParcelInfoReplyHandler(Packet packet, Simulator simulator) { if (OnParcelInfo != null) { ParcelInfoReplyPacket info = (ParcelInfoReplyPacket)packet; ParcelInfo parcelInfo = new ParcelInfo(); parcelInfo.ActualArea = info.Data.ActualArea; parcelInfo.AuctionID = info.Data.AuctionID; parcelInfo.BillableArea = info.Data.BillableArea; parcelInfo.Description = Helpers.FieldToUTF8String(info.Data.Desc); parcelInfo.Dwell = info.Data.Dwell; parcelInfo.GlobalX = info.Data.GlobalX; parcelInfo.GlobalY = info.Data.GlobalY; parcelInfo.GlobalZ = info.Data.GlobalZ; parcelInfo.ID = info.Data.ParcelID; parcelInfo.Mature = ((info.Data.Flags & 1) != 0) ? true : false; parcelInfo.Name = Helpers.FieldToUTF8String(info.Data.Name); parcelInfo.OwnerID = info.Data.OwnerID; parcelInfo.SalePrice = info.Data.SalePrice; parcelInfo.SimName = Helpers.FieldToUTF8String(info.Data.SimName); parcelInfo.SnapshotID = info.Data.SnapshotID; try { OnParcelInfo(parcelInfo); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } /// /// ParcelProperties replies sent over CAPS /// /// Not used (will always be ParcelProperties) /// LLSD Structured data /// Object representing simulator private void ParcelPropertiesReplyHandler(string capsKey, LLSD llsd, Simulator simulator) { if (OnParcelProperties != null || Client.Settings.PARCEL_TRACKING == true) { LLSDMap map = (LLSDMap)llsd; LLSDMap parcelDataBlock = (LLSDMap)(((LLSDArray)map["ParcelData"])[0]); LLSDMap ageVerifyBlock = (LLSDMap)(((LLSDArray)map["AgeVerificationBlock"])[0]); LLSDMap mediaDataBlock = (LLSDMap)(((LLSDArray)map["MediaData"])[0]); Parcel parcel = new Parcel(simulator, parcelDataBlock["LocalID"].AsInteger()); parcel.AABBMax.FromLLSD(parcelDataBlock["AABBMax"]); parcel.AABBMin.FromLLSD(parcelDataBlock["AABBMin"]); parcel.Area = parcelDataBlock["Area"].AsInteger(); parcel.AuctionID = (uint)parcelDataBlock["AuctionID"].AsInteger(); parcel.AuthBuyerID = parcelDataBlock["AuthBuyerID"].AsUUID(); parcel.Bitmap = parcelDataBlock["Bitmap"].AsBinary(); parcel.Category = (Parcel.ParcelCategory)parcelDataBlock["Category"].AsInteger(); parcel.ClaimDate = Helpers.UnixTimeToDateTime((uint)parcelDataBlock["ClaimDate"].AsInteger()); parcel.ClaimPrice = parcelDataBlock["ClaimPrice"].AsInteger(); parcel.Desc = parcelDataBlock["Desc"].AsString(); // TODO: this probably needs to happen when the packet is deserialized. byte[] bytes = parcelDataBlock["ParcelFlags"].AsBinary(); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); parcel.Flags = (Parcel.ParcelFlags)BitConverter.ToUInt32(bytes, 0); parcel.GroupID = parcelDataBlock["GroupID"].AsUUID(); parcel.GroupPrims = parcelDataBlock["GroupPrims"].AsInteger(); parcel.IsGroupOwned = parcelDataBlock["IsGroupOwned"].AsBoolean(); parcel.LandingType = (byte)parcelDataBlock["LandingType"].AsInteger(); parcel.LocalID = parcelDataBlock["LocalID"].AsInteger(); parcel.MaxPrims = parcelDataBlock["MaxPrims"].AsInteger(); parcel.Media.MediaAutoScale = (byte)parcelDataBlock["MediaAutoScale"].AsInteger(); parcel.Media.MediaID = parcelDataBlock["MediaID"].AsUUID(); parcel.Media.MediaURL = parcelDataBlock["MediaURL"].AsString(); parcel.MusicURL = parcelDataBlock["MusicURL"].AsString(); parcel.Name = parcelDataBlock["Name"].AsString(); parcel.OtherCleanTime = parcelDataBlock["OtherCleanTime"].AsInteger(); parcel.OtherCount = parcelDataBlock["OtherCount"].AsInteger(); parcel.OtherPrims = parcelDataBlock["OtherPrims"].AsInteger(); parcel.OwnerID = parcelDataBlock["OwnerID"].AsUUID(); parcel.OwnerPrims = parcelDataBlock["OwnerPrims"].AsInteger(); parcel.ParcelPrimBonus = (float)parcelDataBlock["ParcelPrimBonus"].AsReal(); parcel.PassHours = (float)parcelDataBlock["PassHours"].AsReal(); parcel.PassPrice = parcelDataBlock["PassPrice"].AsInteger(); parcel.PublicCount = parcelDataBlock["PublicCount"].AsInteger(); parcel.RegionDenyAgeUnverified = ageVerifyBlock["RegionDenyAgeUnverified"].AsBoolean(); parcel.RegionDenyAnonymous = parcelDataBlock["RegionDenyAnonymous"].AsBoolean(); parcel.RegionPushOverride = parcelDataBlock["RegionPushOverride"].AsBoolean(); parcel.RentPrice = parcelDataBlock["RentPrice"].AsInteger(); parcel.RequestResult = parcelDataBlock["RequestResult"].AsInteger(); parcel.SalePrice = parcelDataBlock["SalePrice"].AsInteger(); parcel.SelectedPrims = parcelDataBlock["SelectedPrims"].AsInteger(); parcel.SelfCount = parcelDataBlock["SelfCount"].AsInteger(); parcel.SequenceID = parcelDataBlock["SequenceID"].AsInteger(); parcel.Simulator = simulator; parcel.SimWideMaxPrims = parcelDataBlock["SimWideMaxPrims"].AsInteger(); parcel.SimWideTotalPrims = parcelDataBlock["SimWideTotalPrims"].AsInteger(); parcel.SnapSelection = parcelDataBlock["SnapSelection"].AsBoolean(); parcel.SnapshotID = parcelDataBlock["SnapshotID"].AsUUID(); parcel.Status = (Parcel.ParcelStatus)parcelDataBlock["Status"].AsInteger(); parcel.TotalPrims = parcelDataBlock["TotalPrims"].AsInteger(); parcel.UserLocation.FromLLSD(parcelDataBlock["UserLocation"]); parcel.UserLookAt.FromLLSD(parcelDataBlock["UserLookAt"]); parcel.Media.MediaDesc = mediaDataBlock["MediaDesc"].AsString(); parcel.Media.MediaHeight = mediaDataBlock["MediaHeight"].AsInteger(); parcel.Media.MediaWidth = mediaDataBlock["MediaWidth"].AsInteger(); parcel.Media.MediaLoop = mediaDataBlock["MediaLoop"].AsBoolean(); parcel.Media.MediaType = mediaDataBlock["MediaType"].AsString(); parcel.ObscureMedia = mediaDataBlock["ObscureMedia"].AsBoolean(); parcel.ObscureMusic = mediaDataBlock["ObscureMusic"].AsBoolean(); if (Client.Settings.PARCEL_TRACKING) { lock (simulator.Parcels.Dictionary) simulator.Parcels.Dictionary[parcel.LocalID] = parcel; int y, x, index, bit; for (y = 0; y < simulator.ParcelMap.GetLength(0); y++) { for (x = 0; x < simulator.ParcelMap.GetLength(1); x++) { if (simulator.ParcelMap[y, x] == 0) { index = (y * 64) + x; bit = index % 8; index >>= 3; if ((parcel.Bitmap[index] & (1 << bit)) != 0) simulator.ParcelMap[y, x] = parcel.LocalID; } } } } // auto request acl, will be stored in parcel tracking dictionary if enabled if (Client.Settings.ALWAYS_REQUEST_PARCEL_ACL) Client.Parcels.AccessListRequest(simulator, parcel.LocalID, AccessList.Both, parcel.SequenceID); // auto request dwell, will be stored in parcel tracking dictionary if enables if (Client.Settings.ALWAYS_REQUEST_PARCEL_DWELL) Client.Parcels.DwellRequest(simulator, parcel.LocalID); // Fire the callback for parcel properties being received if (OnParcelProperties != null) { try { OnParcelProperties(parcel, (ParcelResult)parcel.RequestResult, parcel.SequenceID, parcel.SnapSelection); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } // Check if all of the simulator parcels have been retrieved, if so fire another callback if (OnSimParcelsDownloaded != null && simulator.IsParcelMapFull()) { try { OnSimParcelsDownloaded(simulator, simulator.Parcels, simulator.ParcelMap); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } } /// /// Parcel Properties reply handler for data that comes in over udp (deprecated) /// /// /// private void ParcelPropertiesHandler(Packet packet, Simulator simulator) { if (OnParcelProperties != null || Client.Settings.PARCEL_TRACKING == true) { ParcelPropertiesPacket properties = (ParcelPropertiesPacket)packet; Parcel parcel = new Parcel(simulator, properties.ParcelData.LocalID); parcel.AABBMax = properties.ParcelData.AABBMax; parcel.AABBMin = properties.ParcelData.AABBMin; parcel.Area = properties.ParcelData.Area; parcel.AuctionID = properties.ParcelData.AuctionID; parcel.AuthBuyerID = properties.ParcelData.AuthBuyerID; parcel.Bitmap = properties.ParcelData.Bitmap; parcel.Category = (Parcel.ParcelCategory)(sbyte)properties.ParcelData.Category; parcel.ClaimDate = Helpers.UnixTimeToDateTime((uint)properties.ParcelData.ClaimDate); // ClaimPrice seems to always be zero? parcel.ClaimPrice = properties.ParcelData.ClaimPrice; parcel.Desc = Helpers.FieldToUTF8String(properties.ParcelData.Desc); parcel.GroupID = properties.ParcelData.GroupID; parcel.GroupPrims = properties.ParcelData.GroupPrims; parcel.IsGroupOwned = properties.ParcelData.IsGroupOwned; parcel.LandingType = properties.ParcelData.LandingType; parcel.MaxPrims = properties.ParcelData.MaxPrims; parcel.Media.MediaAutoScale = properties.ParcelData.MediaAutoScale; parcel.Media.MediaID = properties.ParcelData.MediaID; parcel.Media.MediaURL = Helpers.FieldToUTF8String(properties.ParcelData.MediaURL); parcel.MusicURL = Helpers.FieldToUTF8String(properties.ParcelData.MusicURL); parcel.Name = Helpers.FieldToUTF8String(properties.ParcelData.Name); parcel.OtherCleanTime = properties.ParcelData.OtherCleanTime; parcel.OtherCount = properties.ParcelData.OtherCount; parcel.OtherPrims = properties.ParcelData.OtherPrims; parcel.OwnerID = properties.ParcelData.OwnerID; parcel.OwnerPrims = properties.ParcelData.OwnerPrims; parcel.Flags = (Parcel.ParcelFlags)properties.ParcelData.ParcelFlags; parcel.ParcelPrimBonus = properties.ParcelData.ParcelPrimBonus; parcel.PassHours = properties.ParcelData.PassHours; parcel.PassPrice = properties.ParcelData.PassPrice; parcel.PublicCount = properties.ParcelData.PublicCount; parcel.RegionDenyAnonymous = properties.ParcelData.RegionDenyAnonymous; parcel.RegionPushOverride = properties.ParcelData.RegionPushOverride; parcel.RentPrice = properties.ParcelData.RentPrice; parcel.SalePrice = properties.ParcelData.SalePrice; parcel.SelectedPrims = properties.ParcelData.SelectedPrims; parcel.SelfCount = properties.ParcelData.SelfCount; parcel.SimWideMaxPrims = properties.ParcelData.SimWideMaxPrims; parcel.SimWideTotalPrims = properties.ParcelData.SimWideTotalPrims; parcel.SnapshotID = properties.ParcelData.SnapshotID; parcel.Status = (Parcel.ParcelStatus)(sbyte)properties.ParcelData.Status; parcel.TotalPrims = properties.ParcelData.TotalPrims; parcel.UserLocation = properties.ParcelData.UserLocation; parcel.UserLookAt = properties.ParcelData.UserLookAt; parcel.RegionDenyAgeUnverified = properties.AgeVerificationBlock.RegionDenyAgeUnverified; // store parcel in dictionary if (Client.Settings.PARCEL_TRACKING) { lock (simulator.Parcels.Dictionary) simulator.Parcels.Dictionary[parcel.LocalID] = parcel; int y, x, index, bit; for (y = 0; y < simulator.ParcelMap.GetLength(0); y++) { for (x = 0; x < simulator.ParcelMap.GetLength(1); x++) { if (simulator.ParcelMap[y, x] == 0) { index = (y * 64) + x; bit = index % 8; index >>= 3; if ((parcel.Bitmap[index] & (1 << bit)) != 0) simulator.ParcelMap[y, x] = parcel.LocalID; } } } } // auto request acl, will be stored in parcel tracking dictionary if enabled if (Client.Settings.ALWAYS_REQUEST_PARCEL_ACL) Client.Parcels.AccessListRequest(simulator, properties.ParcelData.LocalID, AccessList.Both, properties.ParcelData.SequenceID); // auto request dwell, will be stored in parcel tracking dictionary if enables if (Client.Settings.ALWAYS_REQUEST_PARCEL_DWELL) Client.Parcels.DwellRequest(simulator, properties.ParcelData.LocalID); // Fire the callback for parcel properties being received if (OnParcelProperties != null) { try { OnParcelProperties(parcel, (ParcelResult)properties.ParcelData.RequestResult, properties.ParcelData.SequenceID, properties.ParcelData.SnapSelection); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } // Check if all of the simulator parcels have been retrieved, if so fire another callback if (OnSimParcelsDownloaded != null && simulator.IsParcelMapFull()) { try { OnSimParcelsDownloaded(simulator, simulator.Parcels, simulator.ParcelMap); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } } /// /// /// /// /// protected void ParcelAccessListReplyHandler(Packet packet, Simulator simulator) { if (OnAccessListReply != null || Client.Settings.ALWAYS_REQUEST_PARCEL_ACL == true) { ParcelAccessListReplyPacket reply = (ParcelAccessListReplyPacket)packet; List accessList = new List(reply.List.Length); for (int i = 0; i < reply.List.Length; i++) { ParcelAccessEntry pae = new ParcelAccessEntry(); pae.AgentID = reply.List[i].ID; pae.Flags = (AccessList)reply.List[i].Flags; pae.Time = Helpers.UnixTimeToDateTime((uint)reply.List[i].Time); accessList.Add(pae); } lock (simulator.Parcels.Dictionary) { if (simulator.Parcels.Dictionary.ContainsKey(reply.Data.LocalID)) { Parcel parcel = simulator.Parcels.Dictionary[reply.Data.LocalID]; parcel.AccessList = accessList; simulator.Parcels.Dictionary[reply.Data.LocalID] = parcel; } } if (OnAccessListReply != null) { try { OnAccessListReply(simulator, reply.Data.SequenceID, reply.Data.LocalID, reply.Data.Flags, accessList); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } } private void ParcelObjectOwnersReplyHandler(Packet packet, Simulator simulator) { if (OnPrimOwnersListReply != null) { ParcelObjectOwnersReplyPacket reply = (ParcelObjectOwnersReplyPacket)packet; List primOwners = new List(); for (int i = 0; i < reply.Data.Length; i++) { ParcelPrimOwners poe = new ParcelPrimOwners(); poe.OwnerID = reply.Data[i].OwnerID; poe.IsGroupOwned = reply.Data[i].IsGroupOwned; poe.Count = reply.Data[i].Count; poe.OnlineStatus = reply.Data[i].OnlineStatus; primOwners.Add(poe); } try { OnPrimOwnersListReply(simulator, primOwners); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } private void SelectParcelObjectsReplyHandler(Packet packet, Simulator simulator) { ForceObjectSelectPacket reply = (ForceObjectSelectPacket)packet; List objectIDs = new List(reply.Data.Length); for (int i = 0; i < reply.Data.Length; i++) { objectIDs.Add(reply.Data[i].LocalID); } if (OnParcelSelectedObjects != null) { try { OnParcelSelectedObjects(simulator, objectIDs, reply._Header.ResetList); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } private void ParcelMediaUpdateHandler(Packet packet, Simulator simulator) { ParcelMediaUpdatePacket reply = (ParcelMediaUpdatePacket)packet; ParcelMedia media = new ParcelMedia(); media.MediaAutoScale = reply.DataBlock.MediaAutoScale; media.MediaID = reply.DataBlock.MediaID; media.MediaDesc = Helpers.FieldToUTF8String(reply.DataBlockExtended.MediaDesc); media.MediaHeight = reply.DataBlockExtended.MediaHeight; media.MediaLoop = ((reply.DataBlockExtended.MediaLoop & 1) != 0) ? true : false; media.MediaType = Helpers.FieldToUTF8String(reply.DataBlockExtended.MediaType); media.MediaWidth = reply.DataBlockExtended.MediaWidth; media.MediaURL = Helpers.FieldToUTF8String(reply.DataBlock.MediaURL); if (OnParcelMediaUpdate != null) { try { OnParcelMediaUpdate(simulator, media); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } #endregion Packet Handlers } }