/*
* 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.Generic;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Xml;
using System.IO;
using log4net;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
//using OpenSim.Framework.Console;
namespace OpenSim.Framework
{
[Serializable]
public class RegionLightShareData : ICloneable
{
public bool valid = false;
public UUID regionID = UUID.Zero;
public Vector3 waterColor = new Vector3(4.0f,38.0f,64.0f);
public float waterFogDensityExponent = 4.0f;
public float underwaterFogModifier = 0.25f;
public Vector3 reflectionWaveletScale = new Vector3(2.0f,2.0f,2.0f);
public float fresnelScale = 0.40f;
public float fresnelOffset = 0.50f;
public float refractScaleAbove = 0.03f;
public float refractScaleBelow = 0.20f;
public float blurMultiplier = 0.040f;
public Vector2 bigWaveDirection = new Vector2(1.05f,-0.42f);
public Vector2 littleWaveDirection = new Vector2(1.11f,-1.16f);
public UUID normalMapTexture = new UUID("822ded49-9a6c-f61c-cb89-6df54f42cdf4");
public Vector4 horizon = new Vector4(0.25f, 0.25f, 0.32f, 0.32f);
public float hazeHorizon = 0.19f;
public Vector4 blueDensity = new Vector4(0.12f, 0.22f, 0.38f, 0.38f);
public float hazeDensity = 0.70f;
public float densityMultiplier = 0.18f;
public float distanceMultiplier = 0.8f;
public UInt16 maxAltitude = 1605;
public Vector4 sunMoonColor = new Vector4(0.24f, 0.26f, 0.30f, 0.30f);
public float sunMoonPosition = 0.317f;
public Vector4 ambient = new Vector4(0.35f,0.35f,0.35f,0.35f);
public float eastAngle = 0.0f;
public float sunGlowFocus = 0.10f;
public float sunGlowSize = 1.75f;
public float sceneGamma = 1.0f;
public float starBrightness = 0.0f;
public Vector4 cloudColor = new Vector4(0.41f, 0.41f, 0.41f, 0.41f);
public Vector3 cloudXYDensity = new Vector3(1.00f, 0.53f, 1.00f);
public float cloudCoverage = 0.27f;
public float cloudScale = 0.42f;
public Vector3 cloudDetailXYDensity = new Vector3(1.00f, 0.53f, 0.12f);
public float cloudScrollX = 0.20f;
public bool cloudScrollXLock = false;
public float cloudScrollY = 0.01f;
public bool cloudScrollYLock = false;
public bool drawClassicClouds = true;
public delegate void SaveDelegate(RegionLightShareData wl);
public event SaveDelegate OnSave;
public void Save()
{
if (OnSave != null)
OnSave(this);
}
public object Clone()
{
return this.MemberwiseClone(); // call clone method
}
}
public class RegionInfo
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[REGION INFO]";
public bool commFailTF = false;
public ConfigurationMember configMember;
public string DataStore = String.Empty;
public string RegionFile = String.Empty;
public bool isSandbox = false;
public bool Persistent = true;
private EstateSettings m_estateSettings;
private RegionSettings m_regionSettings;
// private IConfigSource m_configSource = null;
public UUID originRegionID = UUID.Zero;
public string proxyUrl = "";
public int ProxyOffset = 0;
public string regionSecret = UUID.Random().ToString();
public string osSecret;
public UUID lastMapUUID = UUID.Zero;
public string lastMapRefresh = "0";
private float m_nonphysPrimMin = 0;
private int m_nonphysPrimMax = 0;
private float m_physPrimMin = 0;
private int m_physPrimMax = 0;
private bool m_clampPrimSize = false;
private int m_objectCapacity = 0;
private int m_maxPrimsPerUser = -1;
private int m_linksetCapacity = 0;
private string m_regionType = String.Empty;
private RegionLightShareData m_windlight = new RegionLightShareData();
protected uint m_httpPort;
protected string m_serverURI;
protected string m_regionName = String.Empty;
protected bool Allow_Alternate_Ports;
public bool m_allow_alternate_ports;
protected string m_externalHostName;
protected IPEndPoint m_internalEndPoint;
protected uint m_remotingPort;
public UUID RegionID = UUID.Zero;
public string RemotingAddress;
public UUID ScopeID = UUID.Zero;
private UUID m_maptileStaticUUID = UUID.Zero;
private bool m_resolveAddress = false;
public uint WorldLocX = 0;
public uint WorldLocY = 0;
public uint WorldLocZ = 0;
///
/// X dimension of the region.
///
///
/// If this is a varregion then the default size set here will be replaced when we load the region config.
///
public uint RegionSizeX = Constants.RegionSize;
///
/// X dimension of the region.
///
///
/// If this is a varregion then the default size set here will be replaced when we load the region config.
///
public uint RegionSizeY = Constants.RegionSize;
///
/// Z dimension of the region.
///
///
/// XXX: Unknown if this accounts for regions with negative Z.
///
public uint RegionSizeZ = Constants.RegionHeight;
// If entering avatar has no specific coords, this is where they land
public Vector3 DefaultLandingPoint = new Vector3(128, 128, 30);
private Dictionary m_extraSettings = new Dictionary();
// Apparently, we're applying the same estatesettings regardless of whether it's local or remote.
// MT: Yes. Estates can't span trust boundaries. Therefore, it can be
// assumed that all instances belonging to one estate are able to
// access the same database server. Since estate settings are lodaed
// from there, that should be sufficient for full remote administration
// File based loading
//
public RegionInfo(string description, string filename, bool skipConsoleConfig, IConfigSource configSource) : this(description, filename, skipConsoleConfig, configSource, String.Empty)
{
}
public RegionInfo(string description, string filename, bool skipConsoleConfig, IConfigSource configSource, string configName)
{
// m_configSource = configSource;
if (filename.ToLower().EndsWith(".ini"))
{
if (!File.Exists(filename)) // New region config request
{
IniConfigSource newFile = new IniConfigSource();
ReadNiniConfig(newFile, configName);
newFile.Save(filename);
RegionFile = filename;
return;
}
IniConfigSource source = new IniConfigSource(filename);
bool saveFile = false;
if (source.Configs[configName] == null)
saveFile = true;
ReadNiniConfig(source, configName);
if (configName != String.Empty && saveFile)
source.Save(filename);
RegionFile = filename;
return;
}
try
{
// This will throw if it's not legal Nini XML format
//
IConfigSource xmlsource = new XmlConfigSource(filename);
ReadNiniConfig(xmlsource, configName);
RegionFile = filename;
return;
}
catch (Exception)
{
}
}
// The web loader uses this
//
public RegionInfo(string description, XmlNode xmlNode, bool skipConsoleConfig, IConfigSource configSource)
{
XmlElement elem = (XmlElement)xmlNode;
string name = elem.GetAttribute("Name");
string xmlstr = "" + xmlNode.OuterXml + "";
XmlConfigSource source = new XmlConfigSource(XmlReader.Create(new StringReader(xmlstr)));
ReadNiniConfig(source, name);
m_serverURI = string.Empty;
}
public RegionInfo(uint legacyRegionLocX, uint legacyRegionLocY, IPEndPoint internalEndPoint, string externalUri)
{
RegionLocX = legacyRegionLocX;
RegionLocY = legacyRegionLocY;
RegionSizeX = Constants.RegionSize;
RegionSizeY = Constants.RegionSize;
m_internalEndPoint = internalEndPoint;
m_externalHostName = externalUri;
m_serverURI = string.Empty;
}
public RegionInfo()
{
m_serverURI = string.Empty;
}
public EstateSettings EstateSettings
{
get
{
if (m_estateSettings == null)
{
m_estateSettings = new EstateSettings();
}
return m_estateSettings;
}
set { m_estateSettings = value; }
}
public RegionSettings RegionSettings
{
get
{
if (m_regionSettings == null)
{
m_regionSettings = new RegionSettings();
}
return m_regionSettings;
}
set { m_regionSettings = value; }
}
public RegionLightShareData WindlightSettings
{
get
{
if (m_windlight == null)
{
m_windlight = new RegionLightShareData();
}
return m_windlight;
}
set { m_windlight = value; }
}
public float NonphysPrimMin
{
get { return m_nonphysPrimMin; }
}
public int NonphysPrimMax
{
get { return m_nonphysPrimMax; }
}
public float PhysPrimMin
{
get { return m_physPrimMin; }
}
public int PhysPrimMax
{
get { return m_physPrimMax; }
}
public bool ClampPrimSize
{
get { return m_clampPrimSize; }
}
public int ObjectCapacity
{
get { return m_objectCapacity; }
}
public int MaxPrimsPerUser
{
get { return m_maxPrimsPerUser; }
}
public int LinksetCapacity
{
get { return m_linksetCapacity; }
}
public int AgentCapacity { get; set; }
public byte AccessLevel
{
get { return (byte)Util.ConvertMaturityToAccessLevel((uint)RegionSettings.Maturity); }
}
public string RegionType
{
get { return m_regionType; }
}
public UUID MaptileStaticUUID
{
get { return m_maptileStaticUUID; }
}
public string MaptileStaticFile { get; private set; }
///
/// The port by which http communication occurs with the region (most noticeably, CAPS communication)
///
public uint HttpPort
{
get { return m_httpPort; }
set { m_httpPort = value; }
}
///
/// A well-formed URI for the host region server (namely "http://" + ExternalHostName)
///
public string ServerURI
{
get {
if ( m_serverURI != string.Empty ) {
return m_serverURI;
} else {
return "http://" + m_externalHostName + ":" + m_httpPort + "/";
}
}
set {
if ( value.EndsWith("/") ) {
m_serverURI = value;
} else {
m_serverURI = value + '/';
}
}
}
public string RegionName
{
get { return m_regionName; }
set { m_regionName = value; }
}
public uint RemotingPort
{
get { return m_remotingPort; }
set { m_remotingPort = value; }
}
///
/// 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
{
// Old one defaults to IPv6
//return new IPEndPoint(Dns.GetHostAddresses(m_externalHostName)[0], m_internalEndPoint.Port);
IPAddress ia = null;
// If it is already an IP, don't resolve it - just return directly
if (IPAddress.TryParse(m_externalHostName, out ia))
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);
}
return new IPEndPoint(ia, m_internalEndPoint.Port);
}
set { m_externalHostName = value.ToString(); }
}
public string ExternalHostName
{
get { return m_externalHostName; }
set { m_externalHostName = value; }
}
public IPEndPoint InternalEndPoint
{
get { return m_internalEndPoint; }
set { m_internalEndPoint = value; }
}
///
/// The x co-ordinate of this region in map tiles (e.g. 1000).
/// Coordinate is scaled as world coordinates divided by the legacy region size
/// and is thus is the number of legacy regions.
///
public uint RegionLocX
{
get { return WorldLocX / Constants.RegionSize; }
set { WorldLocX = value * Constants.RegionSize; }
}
///
/// The y co-ordinate of this region in map tiles (e.g. 1000).
/// Coordinate is scaled as world coordinates divided by the legacy region size
/// and is thus is the number of legacy regions.
///
public uint RegionLocY
{
get { return WorldLocY / Constants.RegionSize; }
set { WorldLocY = value * Constants.RegionSize; }
}
public void SetDefaultRegionSize()
{
WorldLocX = 0;
WorldLocY = 0;
WorldLocZ = 0;
RegionSizeX = Constants.RegionSize;
RegionSizeY = Constants.RegionSize;
RegionSizeZ = Constants.RegionHeight;
}
// A unique region handle is created from the region's world coordinates.
// This cannot be changed because some code expects to receive the region handle and then
// compute the region coordinates from it.
public ulong RegionHandle
{
get { return Util.UIntsToLong(WorldLocX, WorldLocY); }
}
public void SetEndPoint(string ipaddr, int port)
{
IPAddress tmpIP = IPAddress.Parse(ipaddr);
IPEndPoint tmpEPE = new IPEndPoint(tmpIP, port);
m_internalEndPoint = tmpEPE;
}
public string GetSetting(string key)
{
string val;
string keylower = key.ToLower();
if (m_extraSettings.TryGetValue(keylower, out val))
return val;
m_log.DebugFormat("[RegionInfo] Could not locate value for parameter {0}", key);
return null;
}
public void SetExtraSetting(string key, string value)
{
string keylower = key.ToLower();
m_extraSettings[keylower] = value;
}
private void ReadNiniConfig(IConfigSource source, string name)
{
bool creatingNew = false;
if (source.Configs.Count == 0)
{
MainConsole.Instance.Output("=====================================\n");
MainConsole.Instance.Output("We are now going to ask a couple of questions about your region.\n");
MainConsole.Instance.Output("You can press 'enter' without typing anything to use the default\n");
MainConsole.Instance.Output("the default is displayed between [ ] brackets.\n");
MainConsole.Instance.Output("=====================================\n");
if (name == String.Empty)
{
while (name.Trim() == string.Empty)
{
name = MainConsole.Instance.CmdPrompt("New region name", name);
if (name.Trim() == string.Empty)
{
MainConsole.Instance.Output("Cannot interactively create region with no name");
}
}
}
source.AddConfig(name);
creatingNew = true;
}
if (name == String.Empty)
name = source.Configs[0].Name;
if (source.Configs[name] == null)
{
source.AddConfig(name);
}
RegionName = name;
IConfig config = source.Configs[name];
// Track all of the keys in this config and remove as they are processed
// The remaining keys will be added to generic key-value storage for
// whoever might need it
HashSet allKeys = new HashSet();
foreach (string s in config.GetKeys())
{
allKeys.Add(s);
}
// RegionUUID
//
allKeys.Remove("RegionUUID");
string regionUUID = config.GetString("RegionUUID", string.Empty);
if (!UUID.TryParse(regionUUID.Trim(), out RegionID))
{
UUID newID = UUID.Random();
while (RegionID == UUID.Zero)
{
regionUUID = MainConsole.Instance.CmdPrompt("RegionUUID", newID.ToString());
if (!UUID.TryParse(regionUUID.Trim(), out RegionID))
{
MainConsole.Instance.Output("RegionUUID must be a valid UUID");
}
}
config.Set("RegionUUID", regionUUID);
}
originRegionID = RegionID; // What IS this?! (Needed for RegionCombinerModule?)
// Location
//
allKeys.Remove("Location");
string location = config.GetString("Location", String.Empty);
if (location == String.Empty)
{
location = MainConsole.Instance.CmdPrompt("Region Location", "1000,1000");
config.Set("Location", location);
}
string[] locationElements = location.Split(new char[] {','});
RegionLocX = Convert.ToUInt32(locationElements[0]);
RegionLocY = Convert.ToUInt32(locationElements[1]);
// Region size
// Default to legacy region size if not specified.
allKeys.Remove("SizeX");
string configSizeX = config.GetString("SizeX", Constants.RegionSize.ToString());
config.Set("SizeX", configSizeX);
RegionSizeX = Convert.ToUInt32(configSizeX);
allKeys.Remove("SizeY");
string configSizeY = config.GetString("SizeY", Constants.RegionSize.ToString());
config.Set("SizeY", configSizeX);
RegionSizeY = Convert.ToUInt32(configSizeY);
allKeys.Remove("SizeZ");
string configSizeZ = config.GetString("SizeZ", Constants.RegionHeight.ToString());
config.Set("SizeZ", configSizeX);
RegionSizeZ = Convert.ToUInt32(configSizeZ);
DoRegionSizeSanityChecks();
// InternalAddress
//
IPAddress address;
allKeys.Remove("InternalAddress");
if (config.Contains("InternalAddress"))
{
address = IPAddress.Parse(config.GetString("InternalAddress", String.Empty));
}
else
{
address = IPAddress.Parse(MainConsole.Instance.CmdPrompt("Internal IP address", "0.0.0.0"));
config.Set("InternalAddress", address.ToString());
}
// InternalPort
//
int port;
allKeys.Remove("InternalPort");
if (config.Contains("InternalPort"))
{
port = config.GetInt("InternalPort", 9000);
}
else
{
port = Convert.ToInt32(MainConsole.Instance.CmdPrompt("Internal port", "9000"));
config.Set("InternalPort", port);
}
m_internalEndPoint = new IPEndPoint(address, port);
// AllowAlternatePorts
//
allKeys.Remove("AllowAlternatePorts");
if (config.Contains("AllowAlternatePorts"))
{
m_allow_alternate_ports = config.GetBoolean("AllowAlternatePorts", true);
}
else
{
m_allow_alternate_ports = Convert.ToBoolean(MainConsole.Instance.CmdPrompt("Allow alternate ports", "False"));
config.Set("AllowAlternatePorts", m_allow_alternate_ports.ToString());
}
// ResolveAddress
//
allKeys.Remove("ResolveAddress");
if (config.Contains("ResolveAddress"))
{
m_resolveAddress = config.GetBoolean("ResolveAddress", false);
}
else
{
if (creatingNew)
m_resolveAddress = Convert.ToBoolean(MainConsole.Instance.CmdPrompt("Resolve hostname to IP on start (for running inside Docker)", "False"));
config.Set("ResolveAddress", m_resolveAddress.ToString());
}
// ExternalHostName
//
allKeys.Remove("ExternalHostName");
string externalName;
if (config.Contains("ExternalHostName"))
{
externalName = config.GetString("ExternalHostName", "SYSTEMIP");
}
else
{
externalName = MainConsole.Instance.CmdPrompt("External host name", "SYSTEMIP");
config.Set("ExternalHostName", externalName);
}
if (externalName == "SYSTEMIP")
{
m_externalHostName = Util.GetLocalHost().ToString();
m_log.InfoFormat(
"[REGIONINFO]: Resolving SYSTEMIP to {0} for external hostname of region {1}",
m_externalHostName, name);
}
else if (!m_resolveAddress)
{
m_externalHostName = externalName;
}
else
{
IPAddress[] addrs = Dns.GetHostAddresses(externalName);
if (addrs.Length != 1) // If it is ambiguous or not resolveable, use it literally
m_externalHostName = externalName;
else
m_externalHostName = addrs[0].ToString();
}
// RegionType
m_regionType = config.GetString("RegionType", String.Empty);
allKeys.Remove("RegionType");
// Get Default Landing Location (Defaults to 128,128)
string temp_location = config.GetString("DefaultLanding", "<128, 128, 30>");
Vector3 temp_vector;
if (Vector3.TryParse(temp_location, out temp_vector))
DefaultLandingPoint = temp_vector;
else
m_log.ErrorFormat("[RegionInfo]: Unable to parse DefaultLanding for '{0}'. The value given was '{1}'", RegionName, temp_location);
allKeys.Remove("DefaultLanding");
DoDefaultLandingSanityChecks();
#region Prim and map stuff
m_nonphysPrimMin = config.GetFloat("NonPhysicalPrimMin", 0);
allKeys.Remove("NonPhysicalPrimMin");
m_nonphysPrimMax = config.GetInt("NonPhysicalPrimMax", 0);
allKeys.Remove("NonPhysicalPrimMax");
m_physPrimMin = config.GetFloat("PhysicalPrimMin", 0);
allKeys.Remove("PhysicalPrimMin");
m_physPrimMax = config.GetInt("PhysicalPrimMax", 0);
allKeys.Remove("PhysicalPrimMax");
m_clampPrimSize = config.GetBoolean("ClampPrimSize", false);
allKeys.Remove("ClampPrimSize");
m_objectCapacity = config.GetInt("MaxPrims", 15000);
allKeys.Remove("MaxPrims");
m_maxPrimsPerUser = config.GetInt("MaxPrimsPerUser", -1);
allKeys.Remove("MaxPrimsPerUser");
m_linksetCapacity = config.GetInt("LinksetPrims", 0);
allKeys.Remove("LinksetPrims");
allKeys.Remove("MaptileStaticUUID");
string mapTileStaticUUID = config.GetString("MaptileStaticUUID", UUID.Zero.ToString());
if (UUID.TryParse(mapTileStaticUUID.Trim(), out m_maptileStaticUUID))
{
config.Set("MaptileStaticUUID", m_maptileStaticUUID.ToString());
}
MaptileStaticFile = config.GetString("MaptileStaticFile", String.Empty);
allKeys.Remove("MaptileStaticFile");
#endregion
AgentCapacity = config.GetInt("MaxAgents", 100);
allKeys.Remove("MaxAgents");
// Multi-tenancy
//
ScopeID = new UUID(config.GetString("ScopeID", UUID.Zero.ToString()));
allKeys.Remove("ScopeID");
foreach (String s in allKeys)
{
SetExtraSetting(s, config.GetString(s));
}
}
// Make sure DefaultLanding is within region borders with a buffer zone 5 meters from borders
private void DoDefaultLandingSanityChecks()
{
// Sanity Check Default Landing
float buffer_zone = 5f;
bool ValuesCapped = false;
// Minimum Positions
if (DefaultLandingPoint.X < buffer_zone)
{
DefaultLandingPoint.X = buffer_zone;
ValuesCapped = true;
}
if (DefaultLandingPoint.Y < buffer_zone)
{
DefaultLandingPoint.Y = buffer_zone;
ValuesCapped = true;
}
// Maximum Positions
if (DefaultLandingPoint.X > RegionSizeX - buffer_zone)
{
DefaultLandingPoint.X = RegionSizeX - buffer_zone;
ValuesCapped = true;
}
if (DefaultLandingPoint.Y > RegionSizeY - buffer_zone)
{
DefaultLandingPoint.Y = RegionSizeY - buffer_zone;
ValuesCapped = true;
}
// Height
if (DefaultLandingPoint.Z < 0f)
DefaultLandingPoint.Z = 0f;
if (ValuesCapped == true)
m_log.WarnFormat("[RegionInfo]: The default landing location for {0} has been capped to {1}", RegionName, DefaultLandingPoint);
}
// Make sure user specified region sizes are sane.
// Must be multiples of legacy region size (256).
private void DoRegionSizeSanityChecks()
{
if (RegionSizeX != Constants.RegionSize || RegionSizeY != Constants.RegionSize)
{
// Doing non-legacy region sizes.
// Enforce region size to be multiples of the legacy region size (256)
uint partial = RegionSizeX % Constants.RegionSize;
if (partial != 0)
{
RegionSizeX -= partial;
if (RegionSizeX == 0)
RegionSizeX = Constants.RegionSize;
m_log.ErrorFormat("{0} Region size must be multiple of {1}. Enforcing {2}.RegionSizeX={3} instead of specified {4}",
LogHeader, Constants.RegionSize, m_regionName, RegionSizeX, RegionSizeX + partial);
}
partial = RegionSizeY % Constants.RegionSize;
if (partial != 0)
{
RegionSizeY -= partial;
if (RegionSizeY == 0)
RegionSizeY = Constants.RegionSize;
m_log.ErrorFormat("{0} Region size must be multiple of {1}. Enforcing {2}.RegionSizeY={3} instead of specified {4}",
LogHeader, Constants.RegionSize, m_regionName, RegionSizeY, RegionSizeY + partial);
}
// Because of things in the viewer, regions MUST be square.
// Remove this check when viewers have been updated.
if (RegionSizeX != RegionSizeY)
{
uint minSize = Math.Min(RegionSizeX, RegionSizeY);
RegionSizeX = minSize;
RegionSizeY = minSize;
m_log.ErrorFormat("{0} Regions must be square until viewers are updated. Forcing region {1} size to <{2},{3}>",
LogHeader, m_regionName, RegionSizeX, RegionSizeY);
}
// There is a practical limit to region size.
if (RegionSizeX > Constants.MaximumRegionSize || RegionSizeY > Constants.MaximumRegionSize)
{
RegionSizeX = Util.Clamp(RegionSizeX, Constants.RegionSize, Constants.MaximumRegionSize);
RegionSizeY = Util.Clamp(RegionSizeY, Constants.RegionSize, Constants.MaximumRegionSize);
m_log.ErrorFormat("{0} Region dimensions must be less than {1}. Clamping {2}'s size to <{3},{4}>",
LogHeader, Constants.MaximumRegionSize, m_regionName, RegionSizeX, RegionSizeY);
}
m_log.InfoFormat("{0} Region {1} size set to <{2},{3}>", LogHeader, m_regionName, RegionSizeX, RegionSizeY);
}
}
private void WriteNiniConfig(IConfigSource source)
{
IConfig config = source.Configs[RegionName];
if (config != null)
source.Configs.Remove(config);
config = source.AddConfig(RegionName);
config.Set("RegionUUID", RegionID.ToString());
string location = String.Format("{0},{1}", RegionLocX, RegionLocY);
config.Set("Location", location);
if (DataStore != String.Empty)
config.Set("Datastore", DataStore);
if (RegionSizeX != Constants.RegionSize || RegionSizeY != Constants.RegionSize)
{
config.Set("SizeX", RegionSizeX);
config.Set("SizeY", RegionSizeY);
// if (RegionSizeZ > 0)
// config.Set("SizeZ", RegionSizeZ);
}
config.Set("InternalAddress", m_internalEndPoint.Address.ToString());
config.Set("InternalPort", m_internalEndPoint.Port);
config.Set("AllowAlternatePorts", m_allow_alternate_ports.ToString());
config.Set("ExternalHostName", m_externalHostName);
if (m_nonphysPrimMin > 0)
config.Set("NonphysicalPrimMax", m_nonphysPrimMin);
if (m_nonphysPrimMax > 0)
config.Set("NonphysicalPrimMax", m_nonphysPrimMax);
if (m_physPrimMin > 0)
config.Set("PhysicalPrimMax", m_physPrimMin);
if (m_physPrimMax > 0)
config.Set("PhysicalPrimMax", m_physPrimMax);
config.Set("ClampPrimSize", m_clampPrimSize.ToString());
if (m_objectCapacity > 0)
config.Set("MaxPrims", m_objectCapacity);
if (m_maxPrimsPerUser > -1)
config.Set("MaxPrimsPerUser", m_maxPrimsPerUser);
if (m_linksetCapacity > 0)
config.Set("LinksetPrims", m_linksetCapacity);
if (AgentCapacity > 0)
config.Set("MaxAgents", AgentCapacity);
if (ScopeID != UUID.Zero)
config.Set("ScopeID", ScopeID.ToString());
if (RegionType != String.Empty)
config.Set("RegionType", RegionType);
if (m_maptileStaticUUID != UUID.Zero)
config.Set("MaptileStaticUUID", m_maptileStaticUUID.ToString());
if (MaptileStaticFile != null && MaptileStaticFile != String.Empty)
config.Set("MaptileStaticFile", MaptileStaticFile);
}
public void SaveRegionToFile(string description, string filename)
{
if (filename.ToLower().EndsWith(".ini"))
{
IniConfigSource source = new IniConfigSource();
try
{
source = new IniConfigSource(filename); // Load if it exists
}
catch (Exception)
{
}
WriteNiniConfig(source);
source.Save(filename);
return;
}
else
throw new Exception("Invalid file type for region persistence.");
}
public void loadConfigurationOptionsFromMe()
{
configMember.addConfigurationOption("sim_UUID", ConfigurationOption.ConfigurationTypes.TYPE_UUID_NULL_FREE,
"UUID of Region (Default is recommended, random UUID)",
RegionID.ToString(), true);
configMember.addConfigurationOption("sim_name", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY,
"Region Name", RegionName, true);
configMember.addConfigurationOption("sim_location_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
"Grid Location (X Axis)", RegionLocX.ToString(), true);
configMember.addConfigurationOption("sim_location_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
"Grid Location (Y Axis)", RegionLocY.ToString(), true);
configMember.addConfigurationOption("sim_size_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
"Size of region in X dimension", RegionSizeX.ToString(), true);
configMember.addConfigurationOption("sim_size_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
"Size of region in Y dimension", RegionSizeY.ToString(), true);
configMember.addConfigurationOption("sim_size_z", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
"Size of region in Z dimension", RegionSizeZ.ToString(), true);
//m_configMember.addConfigurationOption("datastore", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, "Filename for local storage", "OpenSim.db", false);
configMember.addConfigurationOption("internal_ip_address",
ConfigurationOption.ConfigurationTypes.TYPE_IP_ADDRESS,
"Internal IP Address for incoming UDP client connections",
m_internalEndPoint.Address.ToString(),
true);
configMember.addConfigurationOption("internal_ip_port", ConfigurationOption.ConfigurationTypes.TYPE_INT32,
"Internal IP Port for incoming UDP client connections",
m_internalEndPoint.Port.ToString(), true);
configMember.addConfigurationOption("allow_alternate_ports",
ConfigurationOption.ConfigurationTypes.TYPE_BOOLEAN,
"Allow sim to find alternate UDP ports when ports are in use?",
m_allow_alternate_ports.ToString(), true);
configMember.addConfigurationOption("external_host_name",
ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY,
"External Host Name", m_externalHostName, true);
configMember.addConfigurationOption("lastmap_uuid", ConfigurationOption.ConfigurationTypes.TYPE_UUID,
"Last Map UUID", lastMapUUID.ToString(), true);
configMember.addConfigurationOption("lastmap_refresh", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY,
"Last Map Refresh", Util.UnixTimeSinceEpoch().ToString(), true);
configMember.addConfigurationOption("nonphysical_prim_min", ConfigurationOption.ConfigurationTypes.TYPE_FLOAT,
"Minimum size for nonphysical prims", m_nonphysPrimMin.ToString(), true);
configMember.addConfigurationOption("nonphysical_prim_max", ConfigurationOption.ConfigurationTypes.TYPE_INT32,
"Maximum size for nonphysical prims", m_nonphysPrimMax.ToString(), true);
configMember.addConfigurationOption("physical_prim_min", ConfigurationOption.ConfigurationTypes.TYPE_FLOAT,
"Minimum size for nonphysical prims", m_physPrimMin.ToString(), true);
configMember.addConfigurationOption("physical_prim_max", ConfigurationOption.ConfigurationTypes.TYPE_INT32,
"Maximum size for physical prims", m_physPrimMax.ToString(), true);
configMember.addConfigurationOption("clamp_prim_size", ConfigurationOption.ConfigurationTypes.TYPE_BOOLEAN,
"Clamp prims to max size", m_clampPrimSize.ToString(), true);
configMember.addConfigurationOption("object_capacity", ConfigurationOption.ConfigurationTypes.TYPE_INT32,
"Max objects this sim will hold", m_objectCapacity.ToString(), true);
configMember.addConfigurationOption("linkset_capacity", ConfigurationOption.ConfigurationTypes.TYPE_INT32,
"Max prims an object will hold", m_linksetCapacity.ToString(), true);
configMember.addConfigurationOption("agent_capacity", ConfigurationOption.ConfigurationTypes.TYPE_INT32,
"Max avatars this sim will hold",AgentCapacity.ToString(), true);
configMember.addConfigurationOption("scope_id", ConfigurationOption.ConfigurationTypes.TYPE_UUID,
"Scope ID for this region", ScopeID.ToString(), true);
configMember.addConfigurationOption("region_type", ConfigurationOption.ConfigurationTypes.TYPE_STRING,
"Free form string describing the type of region", String.Empty, true);
configMember.addConfigurationOption("region_static_maptile", ConfigurationOption.ConfigurationTypes.TYPE_UUID,
"UUID of a texture to use as the map for this region", m_maptileStaticUUID.ToString(), true);
}
public void loadConfigurationOptions()
{
configMember.addConfigurationOption("sim_UUID", ConfigurationOption.ConfigurationTypes.TYPE_UUID,
"UUID of Region (Default is recommended, random UUID)",
UUID.Random().ToString(), true);
configMember.addConfigurationOption("sim_name", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY,
"Region Name", "OpenSim Test", false);
configMember.addConfigurationOption("sim_location_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
"Grid Location (X Axis)", "1000", false);
configMember.addConfigurationOption("sim_location_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
"Grid Location (Y Axis)", "1000", false);
configMember.addConfigurationOption("sim_size_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
"Size of region in X dimension", Constants.RegionSize.ToString(), false);
configMember.addConfigurationOption("sim_size_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
"Size of region in Y dimension", Constants.RegionSize.ToString(), false);
configMember.addConfigurationOption("sim_size_z", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
"Size of region in Z dimension", Constants.RegionHeight.ToString(), false);
//m_configMember.addConfigurationOption("datastore", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, "Filename for local storage", "OpenSim.db", false);
configMember.addConfigurationOption("internal_ip_address",
ConfigurationOption.ConfigurationTypes.TYPE_IP_ADDRESS,
"Internal IP Address for incoming UDP client connections", "0.0.0.0",
false);
configMember.addConfigurationOption("internal_ip_port", ConfigurationOption.ConfigurationTypes.TYPE_INT32,
"Internal IP Port for incoming UDP client connections",
ConfigSettings.DefaultRegionHttpPort.ToString(), false);
configMember.addConfigurationOption("allow_alternate_ports", ConfigurationOption.ConfigurationTypes.TYPE_BOOLEAN,
"Allow sim to find alternate UDP ports when ports are in use?",
"false", true);
configMember.addConfigurationOption("external_host_name",
ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY,
"External Host Name", "127.0.0.1", false);
configMember.addConfigurationOption("lastmap_uuid", ConfigurationOption.ConfigurationTypes.TYPE_UUID,
"Last Map UUID", lastMapUUID.ToString(), true);
configMember.addConfigurationOption("lastmap_refresh", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY,
"Last Map Refresh", Util.UnixTimeSinceEpoch().ToString(), true);
configMember.addConfigurationOption("nonphysical_prim_max", ConfigurationOption.ConfigurationTypes.TYPE_INT32,
"Maximum size for nonphysical prims", "0", true);
configMember.addConfigurationOption("physical_prim_max", ConfigurationOption.ConfigurationTypes.TYPE_INT32,
"Maximum size for physical prims", "0", true);
configMember.addConfigurationOption("clamp_prim_size", ConfigurationOption.ConfigurationTypes.TYPE_BOOLEAN,
"Clamp prims to max size", "false", true);
configMember.addConfigurationOption("object_capacity", ConfigurationOption.ConfigurationTypes.TYPE_INT32,
"Max objects this sim will hold", "15000", true);
configMember.addConfigurationOption("agent_capacity", ConfigurationOption.ConfigurationTypes.TYPE_INT32,
"Max avatars this sim will hold", "100", true);
configMember.addConfigurationOption("scope_id", ConfigurationOption.ConfigurationTypes.TYPE_UUID,
"Scope ID for this region", UUID.Zero.ToString(), true);
configMember.addConfigurationOption("region_type", ConfigurationOption.ConfigurationTypes.TYPE_STRING,
"Region Type", String.Empty, true);
configMember.addConfigurationOption("region_static_maptile", ConfigurationOption.ConfigurationTypes.TYPE_UUID,
"UUID of a texture to use as the map for this region", String.Empty, true);
}
public bool handleIncomingConfiguration(string configuration_key, object configuration_result)
{
switch (configuration_key)
{
case "sim_UUID":
RegionID = (UUID) configuration_result;
originRegionID = (UUID) configuration_result;
break;
case "sim_name":
RegionName = (string) configuration_result;
break;
case "sim_location_x":
RegionLocX = (uint) configuration_result;
break;
case "sim_location_y":
RegionLocY = (uint) configuration_result;
break;
case "sim_size_x":
RegionSizeX = (uint) configuration_result;
break;
case "sim_size_y":
RegionSizeY = (uint) configuration_result;
break;
case "sim_size_z":
RegionSizeZ = (uint) configuration_result;
break;
case "datastore":
DataStore = (string) configuration_result;
break;
case "internal_ip_address":
IPAddress address = (IPAddress) configuration_result;
m_internalEndPoint = new IPEndPoint(address, 0);
break;
case "internal_ip_port":
m_internalEndPoint.Port = (int) configuration_result;
break;
case "allow_alternate_ports":
m_allow_alternate_ports = (bool) configuration_result;
break;
case "external_host_name":
if ((string) configuration_result != "SYSTEMIP")
{
m_externalHostName = (string) configuration_result;
}
else
{
m_externalHostName = Util.GetLocalHost().ToString();
}
break;
case "lastmap_uuid":
lastMapUUID = (UUID)configuration_result;
break;
case "lastmap_refresh":
lastMapRefresh = (string)configuration_result;
break;
case "nonphysical_prim_max":
m_nonphysPrimMax = (int)configuration_result;
break;
case "physical_prim_max":
m_physPrimMax = (int)configuration_result;
break;
case "clamp_prim_size":
m_clampPrimSize = (bool)configuration_result;
break;
case "object_capacity":
m_objectCapacity = (int)configuration_result;
break;
case "linkset_capacity":
m_linksetCapacity = (int)configuration_result;
break;
case "agent_capacity":
AgentCapacity = (int)configuration_result;
break;
case "scope_id":
ScopeID = (UUID)configuration_result;
break;
case "region_type":
m_regionType = (string)configuration_result;
break;
case "region_static_maptile":
m_maptileStaticUUID = (UUID)configuration_result;
break;
}
return true;
}
public void SaveLastMapUUID(UUID mapUUID)
{
lastMapUUID = mapUUID;
lastMapRefresh = Util.UnixTimeSinceEpoch().ToString();
}
public OSDMap PackRegionInfoData()
{
OSDMap args = new OSDMap();
args["region_id"] = OSD.FromUUID(RegionID);
if ((RegionName != null) && !RegionName.Equals(""))
args["region_name"] = OSD.FromString(RegionName);
args["external_host_name"] = OSD.FromString(ExternalHostName);
args["http_port"] = OSD.FromString(HttpPort.ToString());
args["server_uri"] = OSD.FromString(ServerURI);
args["region_xloc"] = OSD.FromString(RegionLocX.ToString());
args["region_yloc"] = OSD.FromString(RegionLocY.ToString());
args["region_size_x"] = OSD.FromString(RegionSizeX.ToString());
args["region_size_y"] = OSD.FromString(RegionSizeY.ToString());
args["region_size_z"] = OSD.FromString(RegionSizeZ.ToString());
args["internal_ep_address"] = OSD.FromString(InternalEndPoint.Address.ToString());
args["internal_ep_port"] = OSD.FromString(InternalEndPoint.Port.ToString());
if ((RemotingAddress != null) && !RemotingAddress.Equals(""))
args["remoting_address"] = OSD.FromString(RemotingAddress);
args["remoting_port"] = OSD.FromString(RemotingPort.ToString());
args["allow_alt_ports"] = OSD.FromBoolean(m_allow_alternate_ports);
if ((proxyUrl != null) && !proxyUrl.Equals(""))
args["proxy_url"] = OSD.FromString(proxyUrl);
if (RegionType != String.Empty)
args["region_type"] = OSD.FromString(RegionType);
return args;
}
public void UnpackRegionInfoData(OSDMap args)
{
if (args["region_id"] != null)
RegionID = args["region_id"].AsUUID();
if (args["region_name"] != null)
RegionName = args["region_name"].AsString();
if (args["external_host_name"] != null)
ExternalHostName = args["external_host_name"].AsString();
if (args["http_port"] != null)
UInt32.TryParse(args["http_port"].AsString(), out m_httpPort);
if (args["server_uri"] != null)
ServerURI = args["server_uri"].AsString();
if (args["region_xloc"] != null)
{
uint locx;
UInt32.TryParse(args["region_xloc"].AsString(), out locx);
RegionLocX = locx;
}
if (args["region_yloc"] != null)
{
uint locy;
UInt32.TryParse(args["region_yloc"].AsString(), out locy);
RegionLocY = locy;
}
if (args.ContainsKey("region_size_x"))
RegionSizeX = (uint)args["region_size_x"].AsInteger();
if (args.ContainsKey("region_size_y"))
RegionSizeY = (uint)args["region_size_y"].AsInteger();
if (args.ContainsKey("region_size_z"))
RegionSizeZ = (uint)args["region_size_z"].AsInteger();
IPAddress ip_addr = null;
if (args["internal_ep_address"] != null)
{
IPAddress.TryParse(args["internal_ep_address"].AsString(), out ip_addr);
}
int port = 0;
if (args["internal_ep_port"] != null)
{
Int32.TryParse(args["internal_ep_port"].AsString(), out port);
}
InternalEndPoint = new IPEndPoint(ip_addr, port);
if (args["remoting_address"] != null)
RemotingAddress = args["remoting_address"].AsString();
if (args["remoting_port"] != null)
UInt32.TryParse(args["remoting_port"].AsString(), out m_remotingPort);
if (args["allow_alt_ports"] != null)
m_allow_alternate_ports = args["allow_alt_ports"].AsBoolean();
if (args["proxy_url"] != null)
proxyUrl = args["proxy_url"].AsString();
if (args["region_type"] != null)
m_regionType = args["region_type"].AsString();
}
public static RegionInfo Create(UUID regionID, string regionName, uint regX, uint regY, string externalHostName, uint httpPort, uint simPort, uint remotingPort, string serverURI)
{
RegionInfo regionInfo;
IPEndPoint neighbourInternalEndPoint = new IPEndPoint(Util.GetHostFromDNS(externalHostName), (int)simPort);
regionInfo = new RegionInfo(regX, regY, neighbourInternalEndPoint, externalHostName);
regionInfo.RemotingPort = remotingPort;
regionInfo.RemotingAddress = externalHostName;
regionInfo.HttpPort = httpPort;
regionInfo.RegionID = regionID;
regionInfo.RegionName = regionName;
regionInfo.ServerURI = serverURI;
return regionInfo;
}
public int getInternalEndPointPort()
{
return m_internalEndPoint.Port;
}
public Dictionary ToKeyValuePairs()
{
Dictionary kvp = new Dictionary();
kvp["uuid"] = RegionID.ToString();
kvp["locX"] = RegionLocX.ToString();
kvp["locY"] = RegionLocY.ToString();
kvp["external_ip_address"] = ExternalEndPoint.Address.ToString();
kvp["external_port"] = ExternalEndPoint.Port.ToString();
kvp["external_host_name"] = ExternalHostName;
kvp["http_port"] = HttpPort.ToString();
kvp["internal_ip_address"] = InternalEndPoint.Address.ToString();
kvp["internal_port"] = InternalEndPoint.Port.ToString();
kvp["alternate_ports"] = m_allow_alternate_ports.ToString();
kvp["server_uri"] = ServerURI;
return kvp;
}
}
}