/*
* 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.Text;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using libsecondlife.Packets;
namespace libsecondlife
{
///
///
///
public enum GridLayerType : uint
{
/// Objects and terrain are shown
Objects = 0,
/// Only the terrain is shown, no objects
Terrain = 1,
/// Overlay showing land for sale and for auction
LandForSale = 2
}
///
///
///
public enum GridItemType : uint
{
Telehub = 1,
PgEvent = 2,
MatureEvent = 3,
Popular = 4,
AgentLocations = 6,
LandForSale = 7,
Classified = 8
}
///
/// Information about a region on the grid map
///
public struct GridRegion
{
/// Sim X position on World Map
public int X;
/// Sim Y position on World Map
public int Y;
/// Sim Name (NOTE: In lowercase!)
public string Name;
///
public Simulator.SimAccess Access;
/// Appears to always be zero (None)
public Simulator.RegionFlags RegionFlags;
/// Sim's defined Water Height
public byte WaterHeight;
///
public byte Agents;
/// UUID of the World Map image
public LLUUID MapImageID;
/// Unique identifier for this region, a combination of the X
/// and Y position
public ulong RegionHandle;
///
///
///
///
public override string ToString()
{
StringBuilder output = new StringBuilder("GridRegion: ");
output.Append(Name); output.Append(Helpers.NewLine);
output.Append("RegionHandle: " + RegionHandle); output.Append(Helpers.NewLine);
output.Append(String.Format("X: {0} Y: {1}", X, Y)); output.Append(Helpers.NewLine);
output.Append("MapImageID: " + MapImageID.ToString()); output.Append(Helpers.NewLine);
output.Append("Access: " + Access); output.Append(Helpers.NewLine);
output.Append("RegionFlags: " + RegionFlags); output.Append(Helpers.NewLine);
output.Append("WaterHeight: " + WaterHeight); output.Append(Helpers.NewLine);
output.Append("Agents: " + Agents);
return output.ToString();
}
///
///
///
///
public override int GetHashCode()
{
return X.GetHashCode() ^ Y.GetHashCode();
}
///
///
///
///
///
public override bool Equals(object obj)
{
if (obj is GridRegion)
return Equals((GridRegion)obj);
else
return false;
}
private bool Equals(GridRegion region)
{
return (this.X == region.X && this.Y == region.Y);
}
}
///
/// Visual chunk of the grid map
///
public struct GridLayer
{
public int Bottom;
public int Left;
public int Top;
public int Right;
public LLUUID ImageID;
public bool ContainsRegion(int x, int y)
{
return (x >= Left && x <= Right && y >= Bottom && y <= Top);
}
}
public abstract class GridItem
{
}
public class GridAgentLocation : GridItem
{
public uint GlobalX;
public uint GlobalY;
public int AvatarCount;
public string Identifier;
public uint LocalX { get { return GlobalX % 256; } }
public uint LocalY { get { return GlobalY % 256; } }
public ulong RegionHandle
{
get { return Helpers.UIntsToLong((uint)(GlobalX - (GlobalX % 256)), (uint)(GlobalY - (GlobalY % 256))); }
}
}
///
/// Manages grid-wide tasks such as the world map
///
public class GridManager
{
///
///
///
///
public delegate void GridRegionCallback(GridRegion region);
///
///
///
///
public delegate void GridLayerCallback(GridLayer layer);
///
///
///
///
///
public delegate void GridItemsCallback(GridItemType type, List items);
/// Triggered when a new region is discovered through GridManager
public event GridRegionCallback OnGridRegion;
///
public event GridLayerCallback OnGridLayer;
///
public event GridItemsCallback OnGridItems;
/// Unknown
public float SunPhase { get { return sunPhase; } }
/// Current direction of the sun
public LLVector3 SunDirection { get { return sunDirection; } }
/// Current angular velocity of the sun
public LLVector3 SunAngVelocity { get { return sunAngVelocity; } }
/// A dictionary of all the regions, indexed by region name
internal Dictionary Regions = new Dictionary();
/// A dictionary of all the regions, indexed by region handle
internal Dictionary RegionsByHandle = new Dictionary();
private SecondLife Client;
private float sunPhase;
private LLVector3 sunDirection;
private LLVector3 sunAngVelocity;
///
/// Constructor
///
/// Instance of type SecondLife to associate with this GridManager instance
public GridManager(SecondLife client)
{
Client = client;
Client.Network.RegisterCallback(PacketType.MapBlockReply, new NetworkManager.PacketCallback(MapBlockReplyHandler));
Client.Network.RegisterCallback(PacketType.MapItemReply, new NetworkManager.PacketCallback(MapItemReplyHandler));
Client.Network.RegisterCallback(PacketType.SimulatorViewerTimeMessage, new NetworkManager.PacketCallback(TimeMessageHandler));
Client.Network.RegisterCallback(PacketType.CoarseLocationUpdate, new NetworkManager.PacketCallback(CoarseLocationHandler));
}
///
///
///
///
public void RequestMapLayer(GridLayerType layer)
{
string url = Client.Network.CurrentSim.Caps.CapabilityURI("MapLayer");
if (!String.IsNullOrEmpty(url))
{
Hashtable body = new Hashtable();
body["Flags"] = (int)layer;
Client.Network.SendCapsRequest(url, body,
new CapsRequest.CapsResponseCallback(MapLayerResponseHandler));
}
}
///
///
///
///
///
public void RequestMapRegion(string regionName, GridLayerType layer)
{
MapNameRequestPacket request = new MapNameRequestPacket();
request.AgentData.AgentID = Client.Self.AgentID;
request.AgentData.SessionID = Client.Self.SessionID;
request.AgentData.Flags = (uint)layer;
request.AgentData.EstateID = 0; // Filled in on the sim
request.AgentData.Godlike = false; // Filled in on the sim
request.NameData.Name = Helpers.StringToField(regionName.ToLower());
Client.Network.SendPacket(request);
}
///
///
///
///
///
///
///
///
///
public void RequestMapBlocks(GridLayerType layer, ushort minX, ushort minY, ushort maxX, ushort maxY,
bool returnNonExistent)
{
MapBlockRequestPacket request = new MapBlockRequestPacket();
request.AgentData.AgentID = Client.Self.AgentID;
request.AgentData.SessionID = Client.Self.SessionID;
request.AgentData.Flags = (uint)layer;
request.AgentData.Flags |= (uint)(returnNonExistent ? 0x10000 : 0);
request.AgentData.EstateID = 0; // Filled in at the simulator
request.AgentData.Godlike = false; // Filled in at the simulator
request.PositionData.MinX = minX;
request.PositionData.MinY = minY;
request.PositionData.MaxX = maxX;
request.PositionData.MaxY = maxY;
Client.Network.SendPacket(request);
}
///
///
///
///
///
///
///
///
public List MapItems(ulong regionHandle, GridItemType item, GridLayerType layer, int timeoutMS)
{
List itemList = null;
AutoResetEvent itemsEvent = new AutoResetEvent(false);
GridItemsCallback callback =
delegate(GridItemType type, List items)
{
if (type == GridItemType.AgentLocations)
{
itemList = items;
itemsEvent.Set();
}
};
OnGridItems += callback;
RequestMapItems(regionHandle, item, layer);
itemsEvent.WaitOne(timeoutMS, false);
OnGridItems -= callback;
return itemList;
}
///
///
///
///
///
///
public void RequestMapItems(ulong regionHandle, GridItemType item, GridLayerType layer)
{
MapItemRequestPacket request = new MapItemRequestPacket();
request.AgentData.AgentID = Client.Self.AgentID;
request.AgentData.SessionID = Client.Self.SessionID;
request.AgentData.Flags = (uint)layer;
request.AgentData.Godlike = false; // Filled in on the sim
request.AgentData.EstateID = 0; // Filled in on the sim
request.RequestData.ItemType = (uint)item;
request.RequestData.RegionHandle = regionHandle;
Client.Network.SendPacket(request);
}
///
/// Request data for all mainland (Linden managed) simulators
///
public void RequestMainlandSims(GridLayerType layer)
{
RequestMapBlocks(layer, 0, 0, 65535, 65535, false);
}
///
/// Get grid region information using the region name, this function
/// will block until it can find the region or gives up
///
/// Name of sim you're looking for
/// Layer that you are requesting
/// Will contain a GridRegion for the sim you're
/// looking for if successful, otherwise an empty structure
/// True if the GridRegion was successfully fetched, otherwise
/// false
public bool GetGridRegion(string name, GridLayerType layer, out GridRegion region)
{
if (String.IsNullOrEmpty(name))
{
Client.Log("GetGridRegion called with a null or empty region name", Helpers.LogLevel.Error);
region = new GridRegion();
return false;
}
// All lookups are done using lowercase sim names
name = name.ToLower();
if (Regions.ContainsKey(name))
{
// We already have this GridRegion structure
region = Regions[name];
return true;
}
else
{
AutoResetEvent regionEvent = new AutoResetEvent(false);
GridRegionCallback callback =
delegate(GridRegion gridRegion)
{
if (gridRegion.Name == name)
regionEvent.Set();
};
OnGridRegion += callback;
RequestMapRegion(name, layer);
regionEvent.WaitOne(Client.Settings.MAP_REQUEST_TIMEOUT, false);
OnGridRegion -= callback;
if (Regions.ContainsKey(name))
{
// The region was found after our request
region = Regions[name];
return true;
}
else
{
Client.Log("Couldn't find region " + name, Helpers.LogLevel.Warning);
region = new GridRegion();
return false;
}
}
}
private void MapLayerResponseHandler(object response, HttpRequestState state)
{
Dictionary body = (Dictionary)response;
List