/* * 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.Collections.Generic; using libsecondlife.Packets; namespace libsecondlife { /// /// /// /// public delegate void GridRegionCallback(GridRegion region); /// /// Region information returned from the spaceserver, used for the world map /// public class 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 byte Access; /// Various flags for the region (presumably things like PG/Mature) public uint 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; /// /// Constructor /// public GridRegion() { } public override string ToString() { string output = "GridRegion"; output += Environment.NewLine + "Name: " + Name; output += Environment.NewLine + "RegionHandle: " + RegionHandle; output += Environment.NewLine + "X: " + X; output += Environment.NewLine + "Y: " + Y; output += Environment.NewLine + "MapImageID: " + MapImageID; output += Environment.NewLine + "Access: " + Access; output += Environment.NewLine + "RegionFlags: " + RegionFlags; output += Environment.NewLine + "WaterHeight: " + WaterHeight; output += Environment.NewLine + "Agents: " + Agents; return output; } } /// /// Manages grid-wide tasks such as the world map /// public class GridManager { public enum MapLayerType : uint { Objects = 0, Terrain = 1 } /// /// Triggered when a new region is discovered through GridManager /// public event GridRegionCallback OnRegionAdd; // FIXME: These publically accessible dictionaries are a recipe for multi-threading disaster /// A dictionary of all the regions, indexed by region ID public Dictionary Regions = new Dictionary(); /// A dictionary of all the regions, indexed by region handle public Dictionary RegionsByHandle = new Dictionary(); /// 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; } } private SecondLife Client; private float sunPhase = 0.0f; private LLVector3 sunDirection = LLVector3.Zero; private LLVector3 sunAngVelocity = LLVector3.Zero; /// /// 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.SimulatorViewerTimeMessage, new NetworkManager.PacketCallback(TimeMessageHandler)); } /// /// Fire off packet for Estate/Island sim data request. /// public void RequestEstateSims(MapLayerType layer) { MapLayerRequestPacket request = new MapLayerRequestPacket(); request.AgentData.AgentID = Client.Network.AgentID; request.AgentData.SessionID = Client.Network.SessionID; request.AgentData.Godlike = true; request.AgentData.Flags = (uint)layer; request.AgentData.EstateID = 0; // TODO get a better value here. Client.Network.SendPacket(request); } /// /// Fire off packet for Linden/Mainland sim data request. /// public void RequestLindenSims(MapLayerType layer) { MapBlockRequestPacket request = new MapBlockRequestPacket(); request.AgentData.AgentID = Client.Network.AgentID; request.AgentData.SessionID = Client.Network.SessionID; request.AgentData.EstateID = 0; // TODO: ? request.AgentData.Flags = (uint)layer; request.PositionData.MaxX = 65535; request.PositionData.MaxY = 65535; request.PositionData.MinX = 0; request.PositionData.MinY = 0; Client.Network.SendPacket(request); } /// /// Send Request Packets for lists of Linden ('mainland') and Estate (Island) sims. /// /// LL's protocol for some reason uses a different request packet for Estate sims. /// /// public void RequestAllSims(MapLayerType layer) { RequestLindenSims(layer); RequestEstateSims(layer); } /// /// Begin process to get information for a Region /// /// Region name you're requesting data for public void BeginGetGridRegion(string name) { MapNameRequestPacket map = new MapNameRequestPacket(); map.AgentData.AgentID = Client.Network.AgentID; map.AgentData.SessionID = Client.Network.SessionID; map.NameData.Name = Helpers.StringToField(name.ToLower()); Client.Network.SendPacket(map); } /// /// 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 /// GridRegion for the sim you're looking for, or null if it's not available /// GridRegion regiondata = GetGridRegion("Ahern"); public GridRegion GetGridRegion(string name) { name = name.ToLower(); if (Regions.ContainsKey(name)) { return Regions[name]; } else { BeginGetGridRegion(name); // FIXME: We shouldn't be sleeping in a library call, hopefully this goes away soon System.Threading.Thread.Sleep(5000); if (Regions.ContainsKey(name)) { return Regions[name]; } else { Client.Log("Couldn't find region " + name, Helpers.LogLevel.Warning); return null; } } } /// /// Populate Grid info based on data from MapBlockReplyPacket /// /// Incoming MapBlockReplyPacket packet /// Unused private void MapBlockReplyHandler(Packet packet, Simulator simulator) { GridRegion region; MapBlockReplyPacket map = (MapBlockReplyPacket)packet; foreach (MapBlockReplyPacket.DataBlock block in map.Data) { if (block.X != 0 && block.Y != 0) { region = new GridRegion(); region.X = block.X; region.Y = block.Y; region.Name = Helpers.FieldToString(block.Name); region.RegionFlags = block.RegionFlags; region.WaterHeight = block.WaterHeight; region.Agents = block.Agents; region.Access = block.Access; region.MapImageID = block.MapImageID; region.RegionHandle = Helpers.UIntsToLong((uint)region.X * (uint)256, (uint)region.Y * (uint)256); lock (Regions) Regions[region.Name.ToLower()] = region; lock (RegionsByHandle) RegionsByHandle[region.RegionHandle] = region; if (OnRegionAdd != null) { try { OnRegionAdd(region); } catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); } } } } } /// /// Get sim time from the appropriate packet /// /// Incoming SimulatorViewerTimeMessagePacket from SL /// Unused private void TimeMessageHandler(Packet packet, Simulator simulator) { SimulatorViewerTimeMessagePacket time = (SimulatorViewerTimeMessagePacket)packet; sunPhase = time.TimeInfo.SunPhase; sunDirection = time.TimeInfo.SunDirection; sunAngVelocity = time.TimeInfo.SunAngVelocity; // TODO: Does anyone have a use for the time stuff? } } }