/*
* 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.Collections.Generic;
using System.Threading;
using System.Reflection;
using libsecondlife.Packets;
using libsecondlife.StructuredData;
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 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
{
///
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;
/// 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;
///
[Obsolete]
public bool RegionDenyIdentified; // no longer used as of 1.19
///
[Obsolete]
public bool RegionDenyTransacted; // no longer used as of 1.19
///
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;
/// String describing media
public string MediaDesc;
/// Integer representing the height of the media
public int MediaHeight;
/// true to loop media
public bool MediaLoop;
/// The mime type of the media. e.g.: image/jpeg, text/html, etc
public string MediaType;
/// Integer representing the width of the media
public int MediaWidth;
/// true to obscure (hide) media url
public bool ObscureMedia;
/// true to obscure (hide) music url
public bool ObscureMusic;
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 = LLUUID.Zero;
IsGroupOwned = false;
AuctionID = 0;
ClaimDate = Helpers.Epoch;
ClaimPrice = 0;
RentPrice = 0;
AABBMin = LLVector3.Zero;
AABBMax = LLVector3.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;
MediaURL = String.Empty;
MediaID = LLUUID.Zero;
MediaAutoScale = 0x0;
GroupID = LLUUID.Zero;
PassPrice = 0;
PassHours = 0;
Category = ParcelCategory.None;
AuthBuyerID = LLUUID.Zero;
SnapshotID = LLUUID.Zero;
UserLocation = LLVector3.Zero;
UserLookAt = LLVector3.Zero;
LandingType = 0x0;
Dwell = 0;
RegionDenyAnonymous = false;
RegionDenyIdentified = false; // no longer used as of 1.19
RegionDenyTransacted = false; // no longer used as of 1.19
RegionPushOverride = false;
AccessList = new List(0);
RegionDenyAgeUnverified = false;
MediaDesc = string.Empty;
MediaHeight = 0;
MediaLoop = false;
MediaType = String.Empty;
MediaWidth = 0;
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.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.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
///
///
///
public struct ParcelAccessEntry
{
///
public LLUUID AgentID;
///
public DateTime Time;
///
public AccessList Flags;
}
public struct ParcelPrimOwners
{
public LLUUID OwnerID;
public bool IsGroupOwned;
public int Count;
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(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);
///
/// Responses to a request for prim owners on a parcel
///
/// simulator parcel is in
/// LocalID of parcel
/// 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
public delegate void ForceSelectObjects(Simulator simulator, List objectIDs);
#endregion Delegates
#region Events
///
public event ParcelDwellCallback OnParcelDwell;
///
public event ParcelInfoCallback OnParcelInfo;
///
public event ParcelPropertiesCallback OnParcelProperties;
///
public event ParcelAccessListReplyCallback OnAccessListReply;
///
public event ParcelObjectOwnersListReplyCallback OnPrimOwnersListReply;
///
public event SimParcelsDownloaded OnSimParcelsDownloaded;
///
public event ForceSelectObjects OnParcelSelectedObjects;
#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));
// 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));
}
///
/// 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.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);
}
///
/// 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
public void RequestAllSimParcels(Simulator simulator, bool refresh)
{
if (refresh)
{
lock (simulator.ParcelMap)
simulator.ParcelMap = new int[64, 64];
}
Thread th = new Thread(delegate()
{
int y, x;
for (y = 0; y < 64; y++)
{
for (x = 0; x < 64; x++)
{
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 for 50 ms after every request to avoid flooding the sim
System.Threading.Thread.Sleep(200);
}
}
}
});
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 = LLUUID.Zero; // Not used by clients
Client.Network.SendPacket(request, simulator);
}
///
///
///
///
///
///
///
///
///
public void Buy(Simulator simulator, int localID, bool forGroup, LLUUID 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);
}
///
///
///
///
///
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);
}
///
///
///
///
///
///
public void DeedToGroup(Simulator simulator, int localID, LLUUID 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
/// local ID # of parcel
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);
}
///
///
///
///
///
///
///
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 = 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.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);
}
///
///
///
///
///
///
///
///
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);
}
///
/// Gets a parcel 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, LLVector3 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((int)simulator.ParcelMap.GetValue(localID), out p))
{
Client.Log(String.Format("Can't find parcel {0} in simulator {1}", localID, simulator),
Helpers.LogLevel.Warning);
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))
{
Client.Log("Land Patch not stored for location", Helpers.LogLevel.Warning);
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].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, List ownerIDs)
{
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;
if (ownerIDs != null)
{
select.ReturnIDs = new ParcelSelectObjectsPacket.ReturnIDsBlock[ownerIDs.Count];
for (int i = 0; i < ownerIDs.Count; i++)
{
select.ReturnIDs[i] = new ParcelSelectObjectsPacket.ReturnIDsBlock();
select.ReturnIDs[i].ReturnID = ownerIDs[i];
}
}
else
{
select.ReturnIDs = new ParcelSelectObjectsPacket.ReturnIDsBlock[0];
}
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(LLUUID 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(LLUUID 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);
}
#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) { 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); }
}
}
///
/// 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.MediaAutoScale = (byte)parcelDataBlock["MediaAutoScale"].AsInteger();
parcel.MediaID = parcelDataBlock["MediaID"].AsUUID();
parcel.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.MediaDesc = mediaDataBlock["MediaDesc"].AsString();
parcel.MediaHeight = mediaDataBlock["MediaHeight"].AsInteger();
parcel.MediaWidth = mediaDataBlock["MediaWidth"].AsInteger();
parcel.MediaLoop = mediaDataBlock["MediaLoop"].AsBoolean();
parcel.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) { Client.Log(e.ToString(), Helpers.LogLevel.Error); }
}
// 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) { Client.Log(e.ToString(), Helpers.LogLevel.Error); }
}
}
}
///
/// 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.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;
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) { Client.Log(e.ToString(), Helpers.LogLevel.Error); }
}
// 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) { Client.Log(e.ToString(), Helpers.LogLevel.Error); }
}
}
}
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) { Client.Log(e.ToString(), Helpers.LogLevel.Error); }
}
}
}
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) { Client.Log(e.ToString(), Helpers.LogLevel.Error); }
}
}
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);
}
}
#endregion Packet Handlers
}
}