/* * Copyright (c) 2006-2007, 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.Timers; using System.Collections.Generic; using libsecondlife.Packets; namespace libsecondlife { #region Structs /// /// Some information about a parcel of land /// public struct ParcelInfo { /// public LLUUID ID; /// public LLUUID OwnerID; /// public string Name; /// public string Description; /// public int ActualArea; /// public int BillableArea; /// public bool Mature; /// public float GlobalX; /// public float GlobalY; /// public float GlobalZ; /// public string SimName; /// public LLUUID SnapshotID; /// public float Dwell; /// public int SalePrice; /// public int AuctionID; } #endregion Structs #region Parcel Class /// /// Parcel of land, a portion of virtual real estate in a simulator /// public class 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, /// Unknown AllowDeedToGroup = 1 << 13, /// Unknown 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 DenyIdentified = 1 << 23, /// Ban all transacted avatars 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, } /// /// Parcel ownership status /// public enum ParcelStatus : sbyte { /// None = -1, /// Leased = 0, /// LeasePending = 1, /// Abandoned = 2 } /// /// /// public enum ParcelCategory : sbyte { /// No assigned category None = 0, /// Linden, /// Adult, /// Arts, /// Business, /// Educational, /// Gaming, /// Hangout, /// Newcomer, /// Park, /// Residential, /// Shopping, /// Stage, /// Other, /// Not an actual category, only used for queries Any = -1 } #endregion Enums /// public int RequestResult; /// public int SequenceID; /// public bool SnapSelection; /// public int SelfCount; /// public int OtherCount; /// public int PublicCount; /// Simulator-local ID of this parcel public int LocalID { get { return localid; } } /// UUID of the owner of this parcel public LLUUID 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; /// public int RentPrice; /// Minimum corner of the axis-aligned bounding box for this /// parcel public LLVector3 AABBMin; /// Maximum corner of the axis-aligned bounding box for this /// parcel public LLVector3 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; /// URL For other Media public string MediaURL; /// Key to Picture for Media Placeholder public LLUUID MediaID; /// public byte MediaAutoScale; /// public LLUUID 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 LLUUID AuthBuyerID; /// Key of parcel snapshot public LLUUID SnapshotID; /// public LLVector3 UserLocation; /// public LLVector3 UserLookAt; /// public byte LandingType; /// public float Dwell; /// public bool RegionDenyAnonymous; /// public bool RegionDenyIdentified; /// public bool RegionDenyTransacted; /// public bool RegionPushOverride; /// public Simulator Simulator; /// Access list of who is whitelisted or blacklisted on this /// parcel public List AccessList; private int localid; /// /// Defalt constructor /// /// Simulator this parcel resides in /// Local ID of this parcel public Parcel(Simulator simulator, int localID) { Simulator = simulator; localid = localID; } /// /// 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.Network.AgentID; request.AgentData.SessionID = Simulator.Client.Network.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.MediaAutoScale; request.ParcelData.MediaID = this.MediaID; request.ParcelData.MediaURL = Helpers.StringToField(this.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(); } public void UpdateOtherCleanTime() { ParcelSetOtherCleanTimePacket request = new ParcelSetOtherCleanTimePacket(); request.AgentData.AgentID = Simulator.Client.Network.AgentID; request.AgentData.SessionID = Simulator.Client.Network.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, /// Owner = 1 << 1, /// Group = 1 << 2, /// Other = 1 << 3, /// List = 1 << 4, /// Sell = 1 << 5 } /// /// /// public enum ParcelAccessFlags : uint { /// NoAccess = 0, /// 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 } #endregion Enums #region Structs /// /// /// public struct ParcelAccessEntry { /// public LLUUID AgentID; /// public DateTime Time; /// public AccessList Flags; } #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(LLUUID 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); /// /// /// /// /// /// /// public delegate void ParcelAccessListReplyCallback(Simulator simulator, int sequenceID, int localID, uint flags, List accessEntries); #endregion Delegates #region Events /// public event ParcelDwellCallback OnParcelDwell; /// public event ParcelInfoCallback OnParcelInfo; /// public event ParcelPropertiesCallback OnParcelProperties; /// public event ParcelAccessListReplyCallback OnAccessListReply; #endregion Events private SecondLife Client; #region Public Methods /// /// Default constructor /// /// A reference to the SecondLife client public ParcelManager(SecondLife client) { Client = client; // Setup the callbacks Client.Network.RegisterCallback(PacketType.ParcelInfoReply, new NetworkManager.PacketCallback(ParcelInfoReplyHandler)); Client.Network.RegisterCallback(PacketType.ParcelProperties, new NetworkManager.PacketCallback(ParcelPropertiesHandler)); Client.Network.RegisterCallback(PacketType.ParcelDwellReply, new NetworkManager.PacketCallback(ParcelDwellReplyHandler)); Client.Network.RegisterCallback(PacketType.ParcelAccessListReply, new NetworkManager.PacketCallback(ParcelAccessListReplyHandler)); } /// /// Request basic information for a single parcel /// /// Simulator-local ID of the parcel public void InfoRequest(LLUUID parcelID) { ParcelInfoRequestPacket request = new ParcelInfoRequestPacket(); request.AgentData.AgentID = Client.Network.AgentID; request.AgentData.SessionID = Client.Network.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.Network.AgentID; request.AgentData.SessionID = Client.Network.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.Network.AgentID; request.AgentData.SessionID = Client.Network.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.Network.AgentID; request.AgentData.SessionID = Client.Network.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 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.Network.AgentID; request.AgentData.SessionID = Client.Network.SessionID; request.Data.LocalID = localID; request.Data.ParcelID = LLUUID.Zero; // Not used by clients Client.Network.SendPacket(request, simulator); } /// /// /// /// /// /// /// /// /// public void Buy(Simulator simulator, int localID, bool forGroup, LLUUID groupID, bool removeContribution) { ParcelBuyPacket request = new ParcelBuyPacket(); request.AgentData.AgentID = Client.Network.AgentID; request.AgentData.SessionID = Client.Network.SessionID; request.Data.Final = true; request.Data.GroupID = groupID; request.Data.LocalID = localID; request.Data.IsGroupOwned = forGroup; request.Data.RemoveContribution = removeContribution; Client.Network.SendPacket(request, simulator); } /// /// /// /// /// public void Reclaim(Simulator simulator, int localID) { ParcelReclaimPacket request = new ParcelReclaimPacket(); request.AgentData.AgentID = Client.Network.AgentID; request.AgentData.SessionID = Client.Network.SessionID; request.Data.LocalID = localID; Client.Network.SendPacket(request, simulator); } /// /// /// /// /// /// public void DeedToGroup(Simulator simulator, int localID, LLUUID groupID) { ParcelDeedToGroupPacket request = new ParcelDeedToGroupPacket(); request.AgentData.AgentID = Client.Network.AgentID; request.AgentData.SessionID = Client.Network.SessionID; request.Data.LocalID = localID; request.Data.GroupID = groupID; Client.Network.SendPacket(request, simulator); } /// /// /// /// /// /// /// public void ReturnObjects(Simulator simulator, int localID, ObjectReturnType type, List ownerIDs) { ParcelReturnObjectsPacket request = new ParcelReturnObjectsPacket(); request.AgentData.AgentID = Client.Network.AgentID; request.AgentData.SessionID = Client.Network.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 = LLUUID.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); } /// /// /// /// /// /// /// /// public void ParcelSubdivide(Simulator simulator, float west, float south, float east, float north) { ParcelDividePacket divide = new ParcelDividePacket(); divide.AgentData.AgentID = Client.Network.AgentID; divide.AgentData.SessionID = Client.Network.SessionID; divide.ParcelData.East = east; divide.ParcelData.North = north; divide.ParcelData.South = south; divide.ParcelData.West = west; Client.Network.SendPacket(divide, simulator); } /// /// /// /// /// /// /// /// public void ParcelJoin(Simulator simulator, float west, float south, float east, float north) { ParcelJoinPacket join = new ParcelJoinPacket(); join.AgentData.AgentID = Client.Network.AgentID; join.AgentData.SessionID = Client.Network.SessionID; join.ParcelData.East = east; join.ParcelData.North = north; join.ParcelData.South = south; join.ParcelData.West = west; Client.Network.SendPacket(join, simulator); } #endregion Public Methods #region Packet Handlers private void ParcelDwellReplyHandler(Packet packet, Simulator simulator) { if (OnParcelDwell != null) { ParcelDwellReplyPacket dwell = (ParcelDwellReplyPacket)packet; try { OnParcelDwell(dwell.Data.ParcelID, dwell.Data.LocalID, dwell.Data.Dwell); } catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); } } } 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) { Client.Log(e.ToString(), Helpers.LogLevel.Error); } } } private void ParcelPropertiesHandler(Packet packet, Simulator simulator) { if (OnParcelProperties != null) { 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.MediaAutoScale = properties.ParcelData.MediaAutoScale; parcel.MediaID = properties.ParcelData.MediaID; parcel.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.RegionDenyIdentified = properties.ParcelData.RegionDenyIdentified; parcel.RegionDenyTransacted = properties.ParcelData.RegionDenyTransacted; 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; // Fire the callback try { OnParcelProperties(parcel, (ParcelResult)properties.ParcelData.RequestResult, properties.ParcelData.SequenceID, properties.ParcelData.SnapSelection); } catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); } } } protected void ParcelAccessListReplyHandler(Packet packet, Simulator simulator) { if (OnAccessListReply != null) { 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); } try { OnAccessListReply(simulator, reply.Data.SequenceID, reply.Data.LocalID, reply.Data.Flags, accessList); } catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); } } } #endregion Packet Handlers } }