/* * 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.Timers; using System.Collections.Generic; using libsecondlife.Packets; namespace libsecondlife { /// /// Parcel information retrieved from a simulator /// public class Parcel { /// /// /// [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, /// AllowTerraform = 1 << 4, /// Avatars have health and can take damage on this parcel 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, } /// public int RequestResult; /// public int SequenceID; /// public bool SnapSelection; /// public int SelfCount; /// public int OtherCount; /// public int PublicCount; /// public int LocalID; /// Key of land owner public LLUUID OwnerID; /// Is the land group owned public bool IsGroupOwned; /// public uint AuctionID; /// Presumably for first land public bool ReservedNewbie; /// Date land was claimed public int ClaimDate; /// public int ClaimPrice; /// public int RentPrice; /// public LLVector3 AABBMin; /// public LLVector3 AABBMax; /// Bitmap describing land layout in 4x4m squares across the entire region public byte[] Bitmap; /// Total land area public int Area; /// public byte Status; /// Max objects across region public int SimWideMaxObjects; /// Total objects across region public int SimWideTotalObjects; /// Max objects for parcel public int MaxObjects; /// Total objects in parcel public int TotalObjects; /// Total objects for owner public int OwnerObjects; /// Total objects for group public int GroupObjects; /// Total for other objects public int OtherObjects; /// public float ParcelObjectBonus; /// public int OtherCleanTime; /// public ParcelFlags Flags; /// 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 byte 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 Simulator Simulator; private void init() { OwnerID = LLUUID.Zero; AABBMin = LLVector3.Zero; AABBMax = LLVector3.Zero; Bitmap = new byte[512]; MediaID = LLUUID.Zero; GroupID = LLUUID.Zero; AuthBuyerID = LLUUID.Zero; SnapshotID = LLUUID.Zero; UserLocation = LLVector3.Zero; UserLookAt = LLVector3.Zero; } /// /// /// public Parcel() { init(); } /// /// /// /// public Parcel(Simulator simulator) { Simulator = simulator; init(); } /// /// /// /// public void GetDwell(SecondLife client) { 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; client.Network.SendPacket((Packet)request, Simulator); } /// /// /// /// /// /// /// /// public bool Buy(SecondLife client, 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 = this.LocalID; request.Data.IsGroupOwned = forGroup; request.Data.RemoveContribution = removeContribution; client.Network.SendPacket((Packet)request, Simulator); return true; } /// /// /// /// /// public bool Reclaim(SecondLife client) { ParcelReclaimPacket request = new ParcelReclaimPacket(); request.AgentData.AgentID = client.Network.AgentID; request.AgentData.SessionID = client.Network.SessionID; request.Data.LocalID = this.LocalID; client.Network.SendPacket((Packet)request, Simulator); return true; } /// /// /// /// /// /// public bool Deed(SecondLife client, LLUUID groupID) { ParcelDeedToGroupPacket request = new ParcelDeedToGroupPacket(); request.AgentData.AgentID = client.Network.AgentID; request.AgentData.SessionID = client.Network.SessionID; request.Data.LocalID = this.LocalID; request.Data.GroupID = groupID; client.Network.SendPacket((Packet)request, Simulator); return true; } /// /// /// /// public void Update(SecondLife client) { ParcelPropertiesUpdatePacket request = new ParcelPropertiesUpdatePacket(); request.AgentData.AgentID = client.Network.AgentID; request.AgentData.SessionID = client.Network.SessionID; request.ParcelData.Flags = 0xFFFFFFFF; // TODO: Probably very important request.ParcelData.LocalID = this.LocalID; request.ParcelData.AuthBuyerID = this.AuthBuyerID; request.ParcelData.Category = 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); request.ParcelData.Flags = 0; // TODO: Unused? 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; client.Network.SendPacket(request, Simulator); //Packet updatePacket = Packets.Parcel.ParcelPropertiesUpdate(client.Protocol, client.Avatar.ID, client.Network.SessionID, this); //Sim.SendPacket(updatePacket, true); } /// /// /// /// /// public void ReturnObjects(SecondLife client, uint returnType) { // TODO: ENUM for returnType ParcelReturnObjectsPacket request = new ParcelReturnObjectsPacket(); request.AgentData.AgentID = client.Network.AgentID; request.AgentData.SessionID = client.Network.SessionID; request.ParcelData.LocalID = this.LocalID; request.ParcelData.ReturnType = returnType; // TODO: Handling of TaskIDs and OwnerIDs request.OwnerIDs = new ParcelReturnObjectsPacket.OwnerIDsBlock[0]; request.TaskIDs = new ParcelReturnObjectsPacket.TaskIDsBlock[1]; client.Network.SendPacket((Packet)request, Simulator); } } 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; } /// /// Parcel (subdivided simulator lots) subsystem /// public class ParcelManager { /// /// /// /// /// /// public delegate void ParcelDwellCallback(LLUUID parcelID, int localID, float dwell); public delegate void ParcelInfoCallback(ParcelInfo parcel); public delegate void ParcelPropertiesCallback(Parcel parcel); /// /// /// public event ParcelDwellCallback OnParcelDwell; public event ParcelInfoCallback OnParcelInfo; public event ParcelPropertiesCallback OnParcelProperties; private SecondLife Client; /// /// 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)); } /// /// /// /// public void ParcelInfoRequest(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((Packet)request); } /// /// /// /// /// /// /// /// /// /// public void ParcelPropertiesRequest(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); } 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.FieldToString(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.FieldToString(info.Data.Name); parcelInfo.OwnerID = info.Data.OwnerID; parcelInfo.SalePrice = info.Data.SalePrice; parcelInfo.SimName = Helpers.FieldToString(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); // TODO: Lots of values are not being stored, Parcel needs to be expanded to take all the data. // August2006: God help me should I have to type this out again... argh. // October2006: I really shouldnt have typed that. parcel.RequestResult = properties.ParcelData.RequestResult; parcel.SequenceID = properties.ParcelData.SequenceID; parcel.SnapSelection = properties.ParcelData.SnapSelection; parcel.SelfCount = properties.ParcelData.SelfCount; parcel.OtherCount = properties.ParcelData.OtherCount; parcel.PublicCount = properties.ParcelData.PublicCount; parcel.LocalID = properties.ParcelData.LocalID; parcel.OwnerID = properties.ParcelData.OwnerID; parcel.IsGroupOwned = properties.ParcelData.IsGroupOwned; parcel.AuctionID = properties.ParcelData.AuctionID; parcel.ReservedNewbie = properties.ParcelData.ReservedNewbie; parcel.ClaimDate = properties.ParcelData.ClaimDate; parcel.ClaimPrice = properties.ParcelData.ClaimPrice; parcel.RentPrice = properties.ParcelData.RentPrice; parcel.AABBMin = properties.ParcelData.AABBMin; parcel.AABBMax = properties.ParcelData.AABBMax; parcel.Bitmap = properties.ParcelData.Bitmap; parcel.Area = properties.ParcelData.Area; parcel.Status = properties.ParcelData.Status; parcel.SimWideMaxObjects = properties.ParcelData.SimWideMaxPrims; parcel.SimWideTotalObjects = properties.ParcelData.SimWideTotalPrims; parcel.MaxObjects = properties.ParcelData.MaxPrims; parcel.TotalObjects = properties.ParcelData.TotalPrims; parcel.OwnerObjects = properties.ParcelData.OwnerPrims; parcel.GroupObjects = properties.ParcelData.GroupPrims; parcel.OtherObjects = properties.ParcelData.OtherPrims; parcel.ParcelObjectBonus = properties.ParcelData.ParcelPrimBonus; parcel.OtherCleanTime = properties.ParcelData.OtherCleanTime; parcel.Flags = (Parcel.ParcelFlags)properties.ParcelData.ParcelFlags; parcel.SalePrice = properties.ParcelData.SalePrice; parcel.Name = Helpers.FieldToString(properties.ParcelData.Name); parcel.Desc = Helpers.FieldToString(properties.ParcelData.Desc); parcel.MusicURL = Helpers.FieldToString(properties.ParcelData.MusicURL); parcel.MediaURL = Helpers.FieldToString(properties.ParcelData.MediaURL); parcel.MediaID = properties.ParcelData.MediaID; parcel.MediaAutoScale = properties.ParcelData.MediaAutoScale; parcel.GroupID = properties.ParcelData.GroupID; parcel.PassPrice = properties.ParcelData.PassPrice; parcel.PassHours = properties.ParcelData.PassHours; parcel.Category = properties.ParcelData.Category; parcel.AuthBuyerID = properties.ParcelData.AuthBuyerID; parcel.SnapshotID = properties.ParcelData.SnapshotID; parcel.UserLocation = properties.ParcelData.UserLocation; parcel.UserLookAt = properties.ParcelData.UserLookAt; parcel.LandingType = properties.ParcelData.LandingType; // Fire the callback try { OnParcelProperties(parcel); } catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); } } } } }