/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* 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.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using OpenSim.Framework;
using OpenMetaverse;
using log4net;
namespace OpenSim.Services.Interfaces
{
public interface IGridService
{
///
/// Register a region with the grid service.
///
///
///
/// Thrown if region registration failed
string RegisterRegion(UUID scopeID, GridRegion regionInfos);
///
/// Deregister a region with the grid service.
///
///
///
/// Thrown if region deregistration failed
bool DeregisterRegion(UUID regionID);
///
/// Get information about the regions neighbouring the given co-ordinates (in meters).
///
///
///
///
List GetNeighbours(UUID scopeID, UUID regionID);
GridRegion GetRegionByUUID(UUID scopeID, UUID regionID);
///
/// Get the region at the given position (in meters)
///
///
///
///
///
GridRegion GetRegionByPosition(UUID scopeID, int x, int y);
///
/// Get information about a region which exactly matches the name given.
///
///
///
/// Returns the region information if the name matched. Null otherwise.
GridRegion GetRegionByName(UUID scopeID, string regionName);
///
/// Get information about regions starting with the provided name.
///
///
/// The name to match against.
///
///
/// The maximum number of results to return.
///
///
/// A list of s of regions with matching name. If the
/// grid-server couldn't be contacted or returned an error, return null.
///
List GetRegionsByName(UUID scopeID, string name, int maxNumber);
List GetRegionRange(UUID scopeID, int xmin, int xmax, int ymin, int ymax);
List GetDefaultRegions(UUID scopeID);
List GetDefaultHypergridRegions(UUID scopeID);
List GetFallbackRegions(UUID scopeID, int x, int y);
List GetHyperlinks(UUID scopeID);
///
/// Get internal OpenSimulator region flags.
///
///
/// See OpenSimulator.Framework.RegionFlags. These are not returned in the GridRegion structure -
/// they currently need to be requested separately. Possibly this should change to avoid multiple service calls
/// in some situations.
///
///
/// The region flags.
///
///
///
int GetRegionFlags(UUID scopeID, UUID regionID);
Dictionary GetExtraFeatures();
}
public interface IHypergridLinker
{
GridRegion TryLinkRegionToCoords(UUID scopeID, string mapName, int xloc, int yloc, UUID ownerID, out string reason);
bool TryUnlinkRegion(string mapName);
}
public class GridRegion
{
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
#pragma warning disable 414
private static readonly string LogHeader = "[GRID REGION]";
#pragma warning restore 414
///
/// The port by which http communication occurs with the region
///
public uint HttpPort { get; set; }
///
/// A well-formed URI for the host region server (namely "http://" + ExternalHostName)
///
public string ServerURI
{
get {
if (!String.IsNullOrEmpty(m_serverURI)) {
return m_serverURI;
} else {
if (HttpPort == 0)
return "http://" + m_externalHostName + "/";
else
return "http://" + m_externalHostName + ":" + HttpPort + "/";
}
}
set {
if ( value == null)
{
m_serverURI = String.Empty;
return;
}
if ( value.EndsWith("/") )
{
m_serverURI = value;
}
else
{
m_serverURI = value + '/';
}
}
}
protected string m_serverURI;
///
/// Provides direct access to the 'm_serverURI' field, without returning a generated URL if m_serverURI is missing.
///
public string RawServerURI
{
get { return m_serverURI; }
set { m_serverURI = value; }
}
public string RegionName
{
get { return m_regionName; }
set { m_regionName = value; }
}
protected string m_regionName = String.Empty;
///
/// Region flags.
///
///
/// If not set (chiefly if a robust service is running code pre OpenSim 0.8.1) then this will be null and
/// should be ignored. If you require flags information please use the separate IGridService.GetRegionFlags() call
/// XXX: This field is currently ignored when used in RegisterRegion, but could potentially be
/// used to set flags at this point.
///
public OpenSim.Framework.RegionFlags? RegionFlags { get; set; }
protected string m_externalHostName;
protected IPEndPoint m_internalEndPoint;
///
/// The co-ordinate of this region in region units.
///
public int RegionCoordX { get { return (int)Util.WorldToRegionLoc((uint)RegionLocX); } }
///
/// The co-ordinate of this region in region units
///
public int RegionCoordY { get { return (int)Util.WorldToRegionLoc((uint)RegionLocY); } }
///
/// The location of this region in meters.
/// DANGER DANGER! Note that this name means something different in RegionInfo.
///
public int RegionLocX
{
get { return m_regionLocX; }
set { m_regionLocX = value; }
}
protected int m_regionLocX;
public int RegionSizeX { get; set; }
public int RegionSizeY { get; set; }
///
/// The location of this region in meters.
/// DANGER DANGER! Note that this name means something different in RegionInfo.
///
public int RegionLocY
{
get { return m_regionLocY; }
set { m_regionLocY = value; }
}
protected int m_regionLocY;
protected UUID m_estateOwner;
public UUID EstateOwner
{
get { return m_estateOwner; }
set { m_estateOwner = value; }
}
public UUID RegionID = UUID.Zero;
public UUID ScopeID = UUID.Zero;
public UUID TerrainImage = UUID.Zero;
public UUID ParcelImage = UUID.Zero;
public byte Access;
public int Maturity;
public string RegionSecret = string.Empty;
public string Token = string.Empty;
public GridRegion()
{
RegionSizeX = (int)Constants.RegionSize;
RegionSizeY = (int)Constants.RegionSize;
m_serverURI = string.Empty;
}
public GridRegion(uint xcell, uint ycell)
{
m_regionLocX = (int)Util.RegionToWorldLoc(xcell);
m_regionLocY = (int)Util.RegionToWorldLoc(ycell);
RegionSizeX = (int)Constants.RegionSize;
RegionSizeY = (int)Constants.RegionSize;
}
public GridRegion(RegionInfo ConvertFrom)
{
m_regionName = ConvertFrom.RegionName;
m_regionLocX = (int)(ConvertFrom.WorldLocX);
m_regionLocY = (int)(ConvertFrom.WorldLocY);
RegionSizeX = (int)ConvertFrom.RegionSizeX;
RegionSizeY = (int)ConvertFrom.RegionSizeY;
m_internalEndPoint = ConvertFrom.InternalEndPoint;
m_externalHostName = ConvertFrom.ExternalHostName;
HttpPort = ConvertFrom.HttpPort;
RegionID = ConvertFrom.RegionID;
ServerURI = ConvertFrom.ServerURI;
TerrainImage = ConvertFrom.RegionSettings.TerrainImageID;
ParcelImage = ConvertFrom.RegionSettings.ParcelImageID;
Access = ConvertFrom.AccessLevel;
Maturity = ConvertFrom.RegionSettings.Maturity;
RegionSecret = ConvertFrom.regionSecret;
EstateOwner = ConvertFrom.EstateSettings.EstateOwner;
}
public GridRegion(GridRegion ConvertFrom)
{
m_regionName = ConvertFrom.RegionName;
RegionFlags = ConvertFrom.RegionFlags;
m_regionLocX = ConvertFrom.RegionLocX;
m_regionLocY = ConvertFrom.RegionLocY;
RegionSizeX = ConvertFrom.RegionSizeX;
RegionSizeY = ConvertFrom.RegionSizeY;
m_internalEndPoint = ConvertFrom.InternalEndPoint;
m_externalHostName = ConvertFrom.ExternalHostName;
HttpPort = ConvertFrom.HttpPort;
RegionID = ConvertFrom.RegionID;
ServerURI = ConvertFrom.ServerURI;
TerrainImage = ConvertFrom.TerrainImage;
ParcelImage = ConvertFrom.ParcelImage;
Access = ConvertFrom.Access;
Maturity = ConvertFrom.Maturity;
RegionSecret = ConvertFrom.RegionSecret;
EstateOwner = ConvertFrom.EstateOwner;
}
public GridRegion(Dictionary kvp)
{
if (kvp.ContainsKey("uuid"))
RegionID = new UUID((string)kvp["uuid"]);
if (kvp.ContainsKey("locX"))
RegionLocX = Convert.ToInt32((string)kvp["locX"]);
if (kvp.ContainsKey("locY"))
RegionLocY = Convert.ToInt32((string)kvp["locY"]);
if (kvp.ContainsKey("sizeX"))
RegionSizeX = Convert.ToInt32((string)kvp["sizeX"]);
else
RegionSizeX = (int)Constants.RegionSize;
if (kvp.ContainsKey("sizeY"))
RegionSizeY = Convert.ToInt32((string)kvp["sizeY"]);
else
RegionSizeX = (int)Constants.RegionSize;
if (kvp.ContainsKey("regionName"))
RegionName = (string)kvp["regionName"];
if (kvp.ContainsKey("access"))
{
byte access = Convert.ToByte((string)kvp["access"]);
Access = access;
Maturity = (int)Util.ConvertAccessLevelToMaturity(access);
}
if (kvp.ContainsKey("flags") && kvp["flags"] != null)
RegionFlags = (OpenSim.Framework.RegionFlags?)Convert.ToInt32((string)kvp["flags"]);
if (kvp.ContainsKey("serverIP"))
{
//int port = 0;
//Int32.TryParse((string)kvp["serverPort"], out port);
//IPEndPoint ep = new IPEndPoint(IPAddress.Parse((string)kvp["serverIP"]), port);
ExternalHostName = (string)kvp["serverIP"];
}
else
ExternalHostName = "127.0.0.1";
if (kvp.ContainsKey("serverPort"))
{
Int32 port = 0;
Int32.TryParse((string)kvp["serverPort"], out port);
InternalEndPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), port);
}
if (kvp.ContainsKey("serverHttpPort"))
{
UInt32 port = 0;
UInt32.TryParse((string)kvp["serverHttpPort"], out port);
HttpPort = port;
}
if (kvp.ContainsKey("serverURI"))
ServerURI = (string)kvp["serverURI"];
if (kvp.ContainsKey("regionMapTexture"))
UUID.TryParse((string)kvp["regionMapTexture"], out TerrainImage);
if (kvp.ContainsKey("parcelMapTexture"))
UUID.TryParse((string)kvp["parcelMapTexture"], out ParcelImage);
if (kvp.ContainsKey("regionSecret"))
RegionSecret =(string)kvp["regionSecret"];
if (kvp.ContainsKey("owner_uuid"))
EstateOwner = new UUID(kvp["owner_uuid"].ToString());
if (kvp.ContainsKey("Token"))
Token = kvp["Token"].ToString();
// m_log.DebugFormat("{0} New GridRegion. id={1}, loc=<{2},{3}>, size=<{4},{5}>",
// LogHeader, RegionID, RegionLocX, RegionLocY, RegionSizeX, RegionSizeY);
}
public Dictionary ToKeyValuePairs()
{
Dictionary kvp = new Dictionary();
kvp["uuid"] = RegionID.ToString();
kvp["locX"] = RegionLocX.ToString();
kvp["locY"] = RegionLocY.ToString();
kvp["sizeX"] = RegionSizeX.ToString();
kvp["sizeY"] = RegionSizeY.ToString();
kvp["regionName"] = RegionName;
if (RegionFlags != null)
kvp["flags"] = ((int)RegionFlags).ToString();
kvp["serverIP"] = ExternalHostName; //ExternalEndPoint.Address.ToString();
kvp["serverHttpPort"] = HttpPort.ToString();
kvp["serverURI"] = ServerURI;
kvp["serverPort"] = InternalEndPoint.Port.ToString();
kvp["regionMapTexture"] = TerrainImage.ToString();
kvp["parcelMapTexture"] = ParcelImage.ToString();
kvp["access"] = Access.ToString();
kvp["regionSecret"] = RegionSecret;
kvp["owner_uuid"] = EstateOwner.ToString();
kvp["Token"] = Token.ToString();
// Maturity doesn't seem to exist in the DB
return kvp;
}
#region Definition of equality
///
/// Define equality as two regions having the same, non-zero UUID.
///
public bool Equals(GridRegion region)
{
if ((object)region == null)
return false;
// Return true if the non-zero UUIDs are equal:
return (RegionID != UUID.Zero) && RegionID.Equals(region.RegionID);
}
public override bool Equals(Object obj)
{
if (obj == null)
return false;
return Equals(obj as GridRegion);
}
public override int GetHashCode()
{
return RegionID.GetHashCode() ^ TerrainImage.GetHashCode() ^ ParcelImage.GetHashCode();
}
#endregion
///
/// This accessor can throw all the exceptions that Dns.GetHostAddresses can throw.
///
/// XXX Isn't this really doing too much to be a simple getter, rather than an explict method?
///
public IPEndPoint ExternalEndPoint
{
get
{
IPAddress ia = null;
// If it is already an IP, don't resolve it - just return directly
// we should not need this
if (IPAddress.TryParse(m_externalHostName, out ia))
{
if (ia.Equals(IPAddress.Any) || ia.Equals(IPAddress.IPv6Any))
return null;
return new IPEndPoint(ia, m_internalEndPoint.Port);
}
// Reset for next check
ia = null;
try
{
foreach (IPAddress Adr in Dns.GetHostAddresses(m_externalHostName))
{
if (ia == null)
ia = Adr;
if (Adr.AddressFamily == AddressFamily.InterNetwork)
{
ia = Adr;
break;
}
}
}
catch // (SocketException e)
{
/*throw new Exception(
"Unable to resolve local hostname " + m_externalHostName + " innerException of type '" +
e + "' attached to this exception", e);*/
// Don't throw a fatal exception here, instead, return Null and handle it in the caller.
// Reason is, on systems such as OSgrid it has occured that known hostnames stop
// resolving and thus make surrounding regions crash out with this exception.
return null;
}
if(ia == null)
return null;
return new IPEndPoint(ia, m_internalEndPoint.Port);
}
}
public string ExternalHostName
{
get { return m_externalHostName; }
set { m_externalHostName = value; }
}
public IPEndPoint InternalEndPoint
{
get { return m_internalEndPoint; }
set { m_internalEndPoint = value; }
}
public ulong RegionHandle
{
get { return Util.UIntsToLong((uint)RegionLocX, (uint)RegionLocY); }
}
}
}