diff --git a/.gitignore b/.gitignore
index e04c219d90..fae7509131 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,9 +29,14 @@ addon-modules/
bin/Debug/*.dll
bin/*.dll.mdb
bin/*.db
+bin/*.db-journal
bin/addin-db-*
bin/*.dll
bin/OpenSim.vshost.exe.config
+bin/OpenSim.32BitLaunch.vshost.exe.config
+bin/OpenSim.32BitLaunch.log
+UpgradeLog.XML
+_UpgradeReport_Files/
bin/ScriptEngines/*-*-*-*-*
bin/ScriptEngines/*.dll
bin/ScriptEngines/*/*.dll
@@ -64,6 +69,7 @@ Examples/*.dll
OpenSim.build
OpenSim.sln
OpenSim.suo
+OpenSim.userprefs
Prebuild/Prebuild.build
Prebuild/Prebuild.sln
TestResult.xml
diff --git a/.nant/local.include b/.nant/local.include
index 6d3e97228f..35f00589e8 100644
--- a/.nant/local.include
+++ b/.nant/local.include
@@ -135,14 +135,25 @@
-
+
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BUILDING.txt b/BUILDING.md
similarity index 58%
rename from BUILDING.txt
rename to BUILDING.md
index 12e5ea53cf..5210b58f1f 100644
--- a/BUILDING.txt
+++ b/BUILDING.md
@@ -1,6 +1,4 @@
-==== Building OpenSim ====
-
-=== Building on Windows ===
+# Building on Windows
Steps:
* runprebuild.bat
@@ -9,16 +7,15 @@ Steps:
* copy OpenSim.ini.example to OpenSim.ini and other appropriate files in bin/config-include
* run OpenSim.exe
-=== Building on Linux ===
+# Building on Linux
Prereqs:
- * Mono >= 2.4.3
- * Nant >= 0.85
- * On some Linux distributions you may need to install additional packages.
- See http://opensimulator.org/wiki/Dependencies for more information.
-
- * May also use xbuild (included in mono distributions)
- * May use Monodevelop, a cross-platform IDE
+* Mono >= 2.4.3
+* Nant >= 0.85
+* On some Linux distributions you may need to install additional packages.
+ See http://opensimulator.org/wiki/Dependencies for more information.
+* May also use xbuild (included in mono distributions)
+* May use Monodevelop, a cross-platform IDE
From the distribution type:
* ./runprebuild.sh
@@ -27,13 +24,13 @@ From the distribution type:
* copy OpenSim.ini.example to OpenSim.ini and other appropriate files in bin/config-include
* run mono OpenSim.exe
-=== Using Monodevelop ===
+# Using Monodevelop
From the distribution type:
* ./runprebuild.sh
* type monodevelop OpenSim.sln
-=== References ===
+# References
Helpful resources:
* http://opensimulator.org/wiki/Build_Instructions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 9dd0797411..e5a6d49413 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -1,5 +1,5 @@
-<<<>>>>The following people have contributed to OpenSim (Thank you
-for your effort!)
+ <<<>>>>The following people have contributed to OpenSim (Thank you
+for your effort!)
= Current OpenSim Developers (in very rough order of appearance) =
These folks represent the current core team for OpenSim, and are the
@@ -16,7 +16,7 @@ people that make the day to day of OpenSim happen.
* BlueWall (James Hughes)
* Nebadon Izumi (Michael Cerquoni, OSgrid)
* Snoopy Pfeffer
-* Richard Adams (Intel)
+* Robert Adams (Intel)
= Core Developers Following the White Rabbit =
Core developers who have temporarily (we hope) gone chasing the white rabbit.
@@ -92,6 +92,7 @@ what it is today.
* Flyte Xevious
* Garmin Kawaguichi
* Gryc Ueusp
+* Hiro Lecker
* Imaze Rhiano
* Intimidated
* Jeremy Bongio (IBM)
@@ -181,12 +182,14 @@ what it is today.
This software uses components from the following developers:
* Sleepycat Software (Berkeley DB)
+* Aurora-Sim (http://aurora-sim.org)
* SQLite (Public Domain)
* XmlRpcCS (http://xmlrpccs.sf.net/)
* MySQL, Inc. (MySQL Connector/NET)
* NUnit (http://www.nunit.org)
* AGEIA Inc. (PhysX)
* Russel L. Smith (ODE)
+* Erwin Coumans (Bullet)
* Prebuild (http://sourceforge.net/projects/dnpb/)
* LibOpenMetaverse (http://lib.openmetaverse.org/)
* DotNetOpenMail v0.5.8b (http://dotnetopenmail.sourceforge.net)
@@ -208,3 +211,4 @@ In addition, we would like to thank:
* The NANT Developers
* Microsoft (.NET, MSSQL-Adapters)
*x
+
diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs
index 437d150097..9c933eee35 100644
--- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs
+++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs
@@ -696,7 +696,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController
region.ExternalHostName = (string) requestData["external_address"];
- bool persist = Convert.ToBoolean((string) requestData["persist"]);
+ bool persist = Convert.ToBoolean(requestData["persist"]);
if (persist)
{
// default place for region configuration files is in the
@@ -852,7 +852,6 @@ namespace OpenSim.ApplicationPlugins.RemoteController
responseData["success"] = true;
responseData["region_name"] = region.RegionName;
responseData["region_id"] = region.RegionID.ToString();
- responseData["region_uuid"] = region.RegionID.ToString(); //Deprecate July 2012
m_log.Info("[RADMIN]: CreateRegion: request complete");
}
@@ -1106,8 +1105,8 @@ namespace OpenSim.ApplicationPlugins.RemoteController
string lastName = (string) requestData["user_lastname"];
string password = (string) requestData["user_password"];
- uint regionXLocation = Convert.ToUInt32((Int32) requestData["start_region_x"]);
- uint regionYLocation = Convert.ToUInt32((Int32) requestData["start_region_y"]);
+ uint regionXLocation = Convert.ToUInt32(requestData["start_region_x"]);
+ uint regionYLocation = Convert.ToUInt32(requestData["start_region_y"]);
string email = ""; // empty string for email
if (requestData.Contains("user_email"))
@@ -1304,9 +1303,9 @@ namespace OpenSim.ApplicationPlugins.RemoteController
if (requestData.ContainsKey("user_password")) password = (string) requestData["user_password"];
if (requestData.ContainsKey("start_region_x"))
- regionXLocation = Convert.ToUInt32((Int32) requestData["start_region_x"]);
+ regionXLocation = Convert.ToUInt32(requestData["start_region_x"]);
if (requestData.ContainsKey("start_region_y"))
- regionYLocation = Convert.ToUInt32((Int32) requestData["start_region_y"]);
+ regionYLocation = Convert.ToUInt32(requestData["start_region_y"]);
// if (requestData.ContainsKey("start_lookat_x"))
// ulaX = Convert.ToUInt32((Int32) requestData["start_lookat_x"]);
@@ -1493,6 +1492,8 @@ namespace OpenSim.ApplicationPlugins.RemoteController
/// profile url
/// - noassets
/// true if no assets should be saved
+ /// - all
+ /// true to save all the regions in the simulator
/// - perm
/// C and/or T
///
@@ -1549,6 +1550,11 @@ namespace OpenSim.ApplicationPlugins.RemoteController
options["checkPermissions"] = (string)requestData["perm"];
}
+ if ((string)requestData["all"] == "true")
+ {
+ options["all"] = (string)requestData["all"];
+ }
+
IRegionArchiverModule archiver = scene.RequestModuleInterface();
if (archiver != null)
@@ -2008,29 +2014,6 @@ namespace OpenSim.ApplicationPlugins.RemoteController
{
return;
}
- #region Deprecate July 2012
- //region_ID, regionid, region_uuid will be deprecated in July 2012!!!!!!
- else if (requestData.ContainsKey("regionid") &&
- !String.IsNullOrEmpty((string)requestData["regionid"]))
- {
- m_log.WarnFormat("[RADMIN]: Use of parameter regionid will be deprecated as of July 2012. Use region_id instead");
- }
- else if (requestData.ContainsKey("region_ID") &&
- !String.IsNullOrEmpty((string)requestData["region_ID"]))
- {
- m_log.WarnFormat("[RADMIN]: Use of parameter region_ID will be deprecated as of July 2012. Use region_id instead");
- }
- else if (requestData.ContainsKey("regionID") &&
- !String.IsNullOrEmpty((string)requestData["regionID"]))
- {
- m_log.WarnFormat("[RADMIN]: Use of parameter regionID will be deprecated as of July 2012. Use region_id instead");
- }
- else if (requestData.ContainsKey("region_uuid") &&
- !String.IsNullOrEmpty((string)requestData["region_uuid"]))
- {
- m_log.WarnFormat("[RADMIN]: Use of parameter region_uuid will be deprecated as of July 2012. Use region_id instead");
- }
- #endregion
else
{
responseData["accepted"] = false;
@@ -2052,56 +2035,6 @@ namespace OpenSim.ApplicationPlugins.RemoteController
throw new Exception(String.Format("Region ID {0} not found", regionID));
}
}
- #region Deprecate July 2012
- else if (requestData.ContainsKey("regionid") &&
- !String.IsNullOrEmpty((string)requestData["regionid"]))
- {
- m_log.WarnFormat("[RADMIN]: Use of parameter regionid will be deprecated as of July 2012. Use region_id instead");
-
- UUID regionID = (UUID)(string)requestData["regionid"];
- if (!m_application.SceneManager.TryGetScene(regionID, out scene))
- {
- responseData["error"] = String.Format("Region ID {0} not found", regionID);
- throw new Exception(String.Format("Region ID {0} not found", regionID));
- }
- }
- else if (requestData.ContainsKey("region_ID") &&
- !String.IsNullOrEmpty((string)requestData["region_ID"]))
- {
- m_log.WarnFormat("[RADMIN]: Use of parameter region_ID will be deprecated as of July 2012. Use region_id instead");
-
- UUID regionID = (UUID)(string)requestData["region_ID"];
- if (!m_application.SceneManager.TryGetScene(regionID, out scene))
- {
- responseData["error"] = String.Format("Region ID {0} not found", regionID);
- throw new Exception(String.Format("Region ID {0} not found", regionID));
- }
- }
- else if (requestData.ContainsKey("regionID") &&
- !String.IsNullOrEmpty((string)requestData["regionID"]))
- {
- m_log.WarnFormat("[RADMIN]: Use of parameter regionID will be deprecated as of July 2012. Use region_id instead");
-
- UUID regionID = (UUID)(string)requestData["regionID"];
- if (!m_application.SceneManager.TryGetScene(regionID, out scene))
- {
- responseData["error"] = String.Format("Region ID {0} not found", regionID);
- throw new Exception(String.Format("Region ID {0} not found", regionID));
- }
- }
- else if (requestData.ContainsKey("region_uuid") &&
- !String.IsNullOrEmpty((string)requestData["region_uuid"]))
- {
- m_log.WarnFormat("[RADMIN]: Use of parameter region_uuid will be deprecated as of July 2012. Use region_id instead");
-
- UUID regionID = (UUID)(string)requestData["region_uuid"];
- if (!m_application.SceneManager.TryGetScene(regionID, out scene))
- {
- responseData["error"] = String.Format("Region ID {0} not found", regionID);
- throw new Exception(String.Format("Region ID {0} not found", regionID));
- }
- }
- #endregion
else if (requestData.ContainsKey("region_name") &&
!String.IsNullOrEmpty((string)requestData["region_name"]))
{
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs
index cb88695600..072bd6f010 100644
--- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs
+++ b/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs
@@ -312,14 +312,16 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
// Now that everything is setup we can proceed to
// add THIS agent to the HTTP server's handler list
- if (!AddAgentHandler(Rest.Name,this))
- {
- Rest.Log.ErrorFormat("{0} Unable to activate handler interface", MsgId);
- foreach (IRest handler in handlers)
- {
- handler.Close();
- }
- }
+ // FIXME: If this code is ever to be re-enabled (most of it is disabled already) then this will
+ // have to be handled through the AddHttpHandler interface.
+// if (!AddAgentHandler(Rest.Name,this))
+// {
+// Rest.Log.ErrorFormat("{0} Unable to activate handler interface", MsgId);
+// foreach (IRest handler in handlers)
+// {
+// handler.Close();
+// }
+// }
}
catch (Exception e)
@@ -342,11 +344,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
Rest.Log.InfoFormat("{0} Plugin is terminating", MsgId);
- try
- {
- RemoveAgentHandler(Rest.Name, this);
- }
- catch (KeyNotFoundException){}
+ // FIXME: If this code is ever to be re-enabled (most of it is disabled already) then this will
+ // have to be handled through the AddHttpHandler interface.
+// try
+// {
+// RemoveAgentHandler(Rest.Name, this);
+// }
+// catch (KeyNotFoundException){}
foreach (IRest handler in handlers)
{
diff --git a/OpenSim/ApplicationPlugins/Rest/RestPlugin.cs b/OpenSim/ApplicationPlugins/Rest/RestPlugin.cs
index eb167502b0..a2425b5cf3 100644
--- a/OpenSim/ApplicationPlugins/Rest/RestPlugin.cs
+++ b/OpenSim/ApplicationPlugins/Rest/RestPlugin.cs
@@ -297,7 +297,9 @@ namespace OpenSim.ApplicationPlugins.Rest
{
if (!IsEnabled) return false;
_agents.Add(agentName, handler);
- return _httpd.AddAgentHandler(agentName, handler);
+// return _httpd.AddAgentHandler(agentName, handler);
+
+ return false;
}
///
@@ -316,7 +318,7 @@ namespace OpenSim.ApplicationPlugins.Rest
if (_agents[agentName] == handler)
{
_agents.Remove(agentName);
- return _httpd.RemoveAgentHandler(agentName, handler);
+// return _httpd.RemoveAgentHandler(agentName, handler);
}
return false;
}
@@ -358,10 +360,10 @@ namespace OpenSim.ApplicationPlugins.Rest
_httpd.RemoveStreamHandler(h.HttpMethod, h.Path);
}
_handlers = null;
- foreach (KeyValuePair h in _agents)
- {
- _httpd.RemoveAgentHandler(h.Key, h.Value);
- }
+// foreach (KeyValuePair h in _agents)
+// {
+// _httpd.RemoveAgentHandler(h.Key, h.Value);
+// }
_agents = null;
}
diff --git a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs
index 86e7aa08f3..6437d0b7b0 100644
--- a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs
+++ b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs
@@ -200,11 +200,25 @@ namespace OpenSim.Capabilities.Handlers
int start, end;
if (TryParseRange(range, out start, out end))
{
-
// Before clamping start make sure we can satisfy it in order to avoid
// sending back the last byte instead of an error status
if (start >= texture.Data.Length)
{
+// m_log.DebugFormat(
+// "[GETTEXTURE]: Client requested range for texture {0} starting at {1} but texture has end of {2}",
+// texture.ID, start, texture.Data.Length);
+
+ // Stricly speaking, as per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html, we should be sending back
+ // Requested Range Not Satisfiable (416) here. However, it appears that at least recent implementations
+ // of the Linden Lab viewer (3.2.1 and 3.3.4 and probably earlier), a viewer that has previously
+ // received a very small texture may attempt to fetch bytes from the server past the
+ // range of data that it received originally. Whether this happens appears to depend on whether
+ // the viewer's estimation of how large a request it needs to make for certain discard levels
+ // (http://wiki.secondlife.com/wiki/Image_System#Discard_Level_and_Mip_Mapping), chiefly discard
+ // level 2. If this estimate is greater than the total texture size, returning a RequestedRangeNotSatisfiable
+ // here will cause the viewer to treat the texture as bad and never display the full resolution
+ // However, if we return PartialContent (or OK) instead, the viewer will display that resolution.
+
// response.StatusCode = (int)System.Net.HttpStatusCode.RequestedRangeNotSatisfiable;
// viewers don't seem to handle RequestedRangeNotSatisfiable and keep retrying with same parameters
response["int_response_code"] = (int)System.Net.HttpStatusCode.NotFound;
@@ -215,7 +229,7 @@ namespace OpenSim.Capabilities.Handlers
start = Utils.Clamp(start, 0, end);
int len = end - start + 1;
- //m_log.Debug("Serving " + start + " to " + end + " of " + texture.Data.Length + " bytes for texture " + texture.ID);
+// m_log.Debug("Serving " + start + " to " + end + " of " + texture.Data.Length + " bytes for texture " + texture.ID);
response["content-type"] = texture.Metadata.ContentType;
diff --git a/OpenSim/Data/IRegionData.cs b/OpenSim/Data/IRegionData.cs
index 546b5e8cba..70e10653de 100644
--- a/OpenSim/Data/IRegionData.cs
+++ b/OpenSim/Data/IRegionData.cs
@@ -85,21 +85,6 @@ namespace OpenSim.Data
List GetHyperlinks(UUID scopeID);
}
- [Flags]
- public enum RegionFlags : int
- {
- DefaultRegion = 1, // Used for new Rez. Random if multiple defined
- FallbackRegion = 2, // Regions we redirect to when the destination is down
- RegionOnline = 4, // Set when a region comes online, unset when it unregisters and DeleteOnUnregister is false
- NoDirectLogin = 8, // Region unavailable for direct logins (by name)
- Persistent = 16, // Don't remove on unregister
- LockedOut = 32, // Don't allow registration
- NoMove = 64, // Don't allow moving this region
- Reservation = 128, // This is an inactive reservation
- Authenticate = 256, // Require authentication
- Hyperlink = 512 // Record represents a HG link
- }
-
public class RegionDataDistanceCompare : IComparer
{
private Vector2 m_origin;
diff --git a/OpenSim/Data/IXInventoryData.cs b/OpenSim/Data/IXInventoryData.cs
index 85a5c08d37..e64a82820d 100644
--- a/OpenSim/Data/IXInventoryData.cs
+++ b/OpenSim/Data/IXInventoryData.cs
@@ -40,6 +40,11 @@ namespace OpenSim.Data
public UUID folderID;
public UUID agentID;
public UUID parentFolderID;
+
+ public XInventoryFolder Clone()
+ {
+ return (XInventoryFolder)MemberwiseClone();
+ }
}
public class XInventoryItem
@@ -64,6 +69,11 @@ namespace OpenSim.Data
public UUID avatarID;
public UUID parentFolderID;
public int inventoryGroupPermissions;
+
+ public XInventoryItem Clone()
+ {
+ return (XInventoryItem)MemberwiseClone();
+ }
}
public interface IXInventoryData
diff --git a/OpenSim/Data/MSSQL/MSSQLRegionData.cs b/OpenSim/Data/MSSQL/MSSQLRegionData.cs
index 3ae87c3804..0d89706566 100644
--- a/OpenSim/Data/MSSQL/MSSQLRegionData.cs
+++ b/OpenSim/Data/MSSQL/MSSQLRegionData.cs
@@ -37,6 +37,7 @@ using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
+using RegionFlags = OpenSim.Framework.RegionFlags;
namespace OpenSim.Data.MSSQL
{
diff --git a/OpenSim/Data/MySQL/MySQLRegionData.cs b/OpenSim/Data/MySQL/MySQLRegionData.cs
index 0614879061..a2d4ae4647 100644
--- a/OpenSim/Data/MySQL/MySQLRegionData.cs
+++ b/OpenSim/Data/MySQL/MySQLRegionData.cs
@@ -30,11 +30,11 @@ using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
-
+using MySql.Data.MySqlClient;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Data;
-using MySql.Data.MySqlClient;
+using RegionFlags = OpenSim.Framework.RegionFlags;
namespace OpenSim.Data.MySQL
{
diff --git a/OpenSim/Data/MySQL/MySQLSimulationData.cs b/OpenSim/Data/MySQL/MySQLSimulationData.cs
index 4d7c0c937c..12c979aa50 100644
--- a/OpenSim/Data/MySQL/MySQLSimulationData.cs
+++ b/OpenSim/Data/MySQL/MySQLSimulationData.cs
@@ -747,95 +747,99 @@ namespace OpenSim.Data.MySQL
RegionLightShareData nWP = new RegionLightShareData();
nWP.OnSave += StoreRegionWindlightSettings;
- using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
+ lock (m_dbLock)
{
- dbcon.Open();
-
- string command = "select * from `regionwindlight` where region_id = ?regionID";
-
- using (MySqlCommand cmd = new MySqlCommand(command))
+ using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
- cmd.Connection = dbcon;
-
- cmd.Parameters.AddWithValue("?regionID", regionUUID.ToString());
-
- IDataReader result = ExecuteReader(cmd);
- if (!result.Read())
+ dbcon.Open();
+
+ string command = "select * from `regionwindlight` where region_id = ?regionID";
+
+ using (MySqlCommand cmd = new MySqlCommand(command))
{
- //No result, so store our default windlight profile and return it
- nWP.regionID = regionUUID;
- // StoreRegionWindlightSettings(nWP);
- return nWP;
- }
- else
- {
- nWP.regionID = DBGuid.FromDB(result["region_id"]);
- nWP.waterColor.X = Convert.ToSingle(result["water_color_r"]);
- nWP.waterColor.Y = Convert.ToSingle(result["water_color_g"]);
- nWP.waterColor.Z = Convert.ToSingle(result["water_color_b"]);
- nWP.waterFogDensityExponent = Convert.ToSingle(result["water_fog_density_exponent"]);
- nWP.underwaterFogModifier = Convert.ToSingle(result["underwater_fog_modifier"]);
- nWP.reflectionWaveletScale.X = Convert.ToSingle(result["reflection_wavelet_scale_1"]);
- nWP.reflectionWaveletScale.Y = Convert.ToSingle(result["reflection_wavelet_scale_2"]);
- nWP.reflectionWaveletScale.Z = Convert.ToSingle(result["reflection_wavelet_scale_3"]);
- nWP.fresnelScale = Convert.ToSingle(result["fresnel_scale"]);
- nWP.fresnelOffset = Convert.ToSingle(result["fresnel_offset"]);
- nWP.refractScaleAbove = Convert.ToSingle(result["refract_scale_above"]);
- nWP.refractScaleBelow = Convert.ToSingle(result["refract_scale_below"]);
- nWP.blurMultiplier = Convert.ToSingle(result["blur_multiplier"]);
- nWP.bigWaveDirection.X = Convert.ToSingle(result["big_wave_direction_x"]);
- nWP.bigWaveDirection.Y = Convert.ToSingle(result["big_wave_direction_y"]);
- nWP.littleWaveDirection.X = Convert.ToSingle(result["little_wave_direction_x"]);
- nWP.littleWaveDirection.Y = Convert.ToSingle(result["little_wave_direction_y"]);
- UUID.TryParse(result["normal_map_texture"].ToString(), out nWP.normalMapTexture);
- nWP.horizon.X = Convert.ToSingle(result["horizon_r"]);
- nWP.horizon.Y = Convert.ToSingle(result["horizon_g"]);
- nWP.horizon.Z = Convert.ToSingle(result["horizon_b"]);
- nWP.horizon.W = Convert.ToSingle(result["horizon_i"]);
- nWP.hazeHorizon = Convert.ToSingle(result["haze_horizon"]);
- nWP.blueDensity.X = Convert.ToSingle(result["blue_density_r"]);
- nWP.blueDensity.Y = Convert.ToSingle(result["blue_density_g"]);
- nWP.blueDensity.Z = Convert.ToSingle(result["blue_density_b"]);
- nWP.blueDensity.W = Convert.ToSingle(result["blue_density_i"]);
- nWP.hazeDensity = Convert.ToSingle(result["haze_density"]);
- nWP.densityMultiplier = Convert.ToSingle(result["density_multiplier"]);
- nWP.distanceMultiplier = Convert.ToSingle(result["distance_multiplier"]);
- nWP.maxAltitude = Convert.ToUInt16(result["max_altitude"]);
- nWP.sunMoonColor.X = Convert.ToSingle(result["sun_moon_color_r"]);
- nWP.sunMoonColor.Y = Convert.ToSingle(result["sun_moon_color_g"]);
- nWP.sunMoonColor.Z = Convert.ToSingle(result["sun_moon_color_b"]);
- nWP.sunMoonColor.W = Convert.ToSingle(result["sun_moon_color_i"]);
- nWP.sunMoonPosition = Convert.ToSingle(result["sun_moon_position"]);
- nWP.ambient.X = Convert.ToSingle(result["ambient_r"]);
- nWP.ambient.Y = Convert.ToSingle(result["ambient_g"]);
- nWP.ambient.Z = Convert.ToSingle(result["ambient_b"]);
- nWP.ambient.W = Convert.ToSingle(result["ambient_i"]);
- nWP.eastAngle = Convert.ToSingle(result["east_angle"]);
- nWP.sunGlowFocus = Convert.ToSingle(result["sun_glow_focus"]);
- nWP.sunGlowSize = Convert.ToSingle(result["sun_glow_size"]);
- nWP.sceneGamma = Convert.ToSingle(result["scene_gamma"]);
- nWP.starBrightness = Convert.ToSingle(result["star_brightness"]);
- nWP.cloudColor.X = Convert.ToSingle(result["cloud_color_r"]);
- nWP.cloudColor.Y = Convert.ToSingle(result["cloud_color_g"]);
- nWP.cloudColor.Z = Convert.ToSingle(result["cloud_color_b"]);
- nWP.cloudColor.W = Convert.ToSingle(result["cloud_color_i"]);
- nWP.cloudXYDensity.X = Convert.ToSingle(result["cloud_x"]);
- nWP.cloudXYDensity.Y = Convert.ToSingle(result["cloud_y"]);
- nWP.cloudXYDensity.Z = Convert.ToSingle(result["cloud_density"]);
- nWP.cloudCoverage = Convert.ToSingle(result["cloud_coverage"]);
- nWP.cloudScale = Convert.ToSingle(result["cloud_scale"]);
- nWP.cloudDetailXYDensity.X = Convert.ToSingle(result["cloud_detail_x"]);
- nWP.cloudDetailXYDensity.Y = Convert.ToSingle(result["cloud_detail_y"]);
- nWP.cloudDetailXYDensity.Z = Convert.ToSingle(result["cloud_detail_density"]);
- nWP.cloudScrollX = Convert.ToSingle(result["cloud_scroll_x"]);
- nWP.cloudScrollXLock = Convert.ToBoolean(result["cloud_scroll_x_lock"]);
- nWP.cloudScrollY = Convert.ToSingle(result["cloud_scroll_y"]);
- nWP.cloudScrollYLock = Convert.ToBoolean(result["cloud_scroll_y_lock"]);
- nWP.drawClassicClouds = Convert.ToBoolean(result["draw_classic_clouds"]);
- nWP.valid = true;
+ cmd.Connection = dbcon;
+
+ cmd.Parameters.AddWithValue("?regionID", regionUUID.ToString());
+
+ IDataReader result = ExecuteReader(cmd);
+ if (!result.Read())
+ {
+ //No result, so store our default windlight profile and return it
+ nWP.regionID = regionUUID;
+// StoreRegionWindlightSettings(nWP);
+ return nWP;
+ }
+ else
+ {
+ nWP.regionID = DBGuid.FromDB(result["region_id"]);
+ nWP.waterColor.X = Convert.ToSingle(result["water_color_r"]);
+ nWP.waterColor.Y = Convert.ToSingle(result["water_color_g"]);
+ nWP.waterColor.Z = Convert.ToSingle(result["water_color_b"]);
+ nWP.waterFogDensityExponent = Convert.ToSingle(result["water_fog_density_exponent"]);
+ nWP.underwaterFogModifier = Convert.ToSingle(result["underwater_fog_modifier"]);
+ nWP.reflectionWaveletScale.X = Convert.ToSingle(result["reflection_wavelet_scale_1"]);
+ nWP.reflectionWaveletScale.Y = Convert.ToSingle(result["reflection_wavelet_scale_2"]);
+ nWP.reflectionWaveletScale.Z = Convert.ToSingle(result["reflection_wavelet_scale_3"]);
+ nWP.fresnelScale = Convert.ToSingle(result["fresnel_scale"]);
+ nWP.fresnelOffset = Convert.ToSingle(result["fresnel_offset"]);
+ nWP.refractScaleAbove = Convert.ToSingle(result["refract_scale_above"]);
+ nWP.refractScaleBelow = Convert.ToSingle(result["refract_scale_below"]);
+ nWP.blurMultiplier = Convert.ToSingle(result["blur_multiplier"]);
+ nWP.bigWaveDirection.X = Convert.ToSingle(result["big_wave_direction_x"]);
+ nWP.bigWaveDirection.Y = Convert.ToSingle(result["big_wave_direction_y"]);
+ nWP.littleWaveDirection.X = Convert.ToSingle(result["little_wave_direction_x"]);
+ nWP.littleWaveDirection.Y = Convert.ToSingle(result["little_wave_direction_y"]);
+ UUID.TryParse(result["normal_map_texture"].ToString(), out nWP.normalMapTexture);
+ nWP.horizon.X = Convert.ToSingle(result["horizon_r"]);
+ nWP.horizon.Y = Convert.ToSingle(result["horizon_g"]);
+ nWP.horizon.Z = Convert.ToSingle(result["horizon_b"]);
+ nWP.horizon.W = Convert.ToSingle(result["horizon_i"]);
+ nWP.hazeHorizon = Convert.ToSingle(result["haze_horizon"]);
+ nWP.blueDensity.X = Convert.ToSingle(result["blue_density_r"]);
+ nWP.blueDensity.Y = Convert.ToSingle(result["blue_density_g"]);
+ nWP.blueDensity.Z = Convert.ToSingle(result["blue_density_b"]);
+ nWP.blueDensity.W = Convert.ToSingle(result["blue_density_i"]);
+ nWP.hazeDensity = Convert.ToSingle(result["haze_density"]);
+ nWP.densityMultiplier = Convert.ToSingle(result["density_multiplier"]);
+ nWP.distanceMultiplier = Convert.ToSingle(result["distance_multiplier"]);
+ nWP.maxAltitude = Convert.ToUInt16(result["max_altitude"]);
+ nWP.sunMoonColor.X = Convert.ToSingle(result["sun_moon_color_r"]);
+ nWP.sunMoonColor.Y = Convert.ToSingle(result["sun_moon_color_g"]);
+ nWP.sunMoonColor.Z = Convert.ToSingle(result["sun_moon_color_b"]);
+ nWP.sunMoonColor.W = Convert.ToSingle(result["sun_moon_color_i"]);
+ nWP.sunMoonPosition = Convert.ToSingle(result["sun_moon_position"]);
+ nWP.ambient.X = Convert.ToSingle(result["ambient_r"]);
+ nWP.ambient.Y = Convert.ToSingle(result["ambient_g"]);
+ nWP.ambient.Z = Convert.ToSingle(result["ambient_b"]);
+ nWP.ambient.W = Convert.ToSingle(result["ambient_i"]);
+ nWP.eastAngle = Convert.ToSingle(result["east_angle"]);
+ nWP.sunGlowFocus = Convert.ToSingle(result["sun_glow_focus"]);
+ nWP.sunGlowSize = Convert.ToSingle(result["sun_glow_size"]);
+ nWP.sceneGamma = Convert.ToSingle(result["scene_gamma"]);
+ nWP.starBrightness = Convert.ToSingle(result["star_brightness"]);
+ nWP.cloudColor.X = Convert.ToSingle(result["cloud_color_r"]);
+ nWP.cloudColor.Y = Convert.ToSingle(result["cloud_color_g"]);
+ nWP.cloudColor.Z = Convert.ToSingle(result["cloud_color_b"]);
+ nWP.cloudColor.W = Convert.ToSingle(result["cloud_color_i"]);
+ nWP.cloudXYDensity.X = Convert.ToSingle(result["cloud_x"]);
+ nWP.cloudXYDensity.Y = Convert.ToSingle(result["cloud_y"]);
+ nWP.cloudXYDensity.Z = Convert.ToSingle(result["cloud_density"]);
+ nWP.cloudCoverage = Convert.ToSingle(result["cloud_coverage"]);
+ nWP.cloudScale = Convert.ToSingle(result["cloud_scale"]);
+ nWP.cloudDetailXYDensity.X = Convert.ToSingle(result["cloud_detail_x"]);
+ nWP.cloudDetailXYDensity.Y = Convert.ToSingle(result["cloud_detail_y"]);
+ nWP.cloudDetailXYDensity.Z = Convert.ToSingle(result["cloud_detail_density"]);
+ nWP.cloudScrollX = Convert.ToSingle(result["cloud_scroll_x"]);
+ nWP.cloudScrollXLock = Convert.ToBoolean(result["cloud_scroll_x_lock"]);
+ nWP.cloudScrollY = Convert.ToSingle(result["cloud_scroll_y"]);
+ nWP.cloudScrollYLock = Convert.ToBoolean(result["cloud_scroll_y_lock"]);
+ nWP.drawClassicClouds = Convert.ToBoolean(result["draw_classic_clouds"]);
+ nWP.valid = true;
+ }
}
}
}
+
return nWP;
}
@@ -881,118 +885,124 @@ namespace OpenSim.Data.MySQL
public virtual void StoreRegionWindlightSettings(RegionLightShareData wl)
{
- using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
+ lock (m_dbLock)
{
- dbcon.Open();
-
- using (MySqlCommand cmd = dbcon.CreateCommand())
+ using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
- cmd.CommandText = "REPLACE INTO `regionwindlight` (`region_id`, `water_color_r`, `water_color_g`, ";
- cmd.CommandText += "`water_color_b`, `water_fog_density_exponent`, `underwater_fog_modifier`, ";
- cmd.CommandText += "`reflection_wavelet_scale_1`, `reflection_wavelet_scale_2`, `reflection_wavelet_scale_3`, ";
- cmd.CommandText += "`fresnel_scale`, `fresnel_offset`, `refract_scale_above`, `refract_scale_below`, ";
- cmd.CommandText += "`blur_multiplier`, `big_wave_direction_x`, `big_wave_direction_y`, `little_wave_direction_x`, ";
- cmd.CommandText += "`little_wave_direction_y`, `normal_map_texture`, `horizon_r`, `horizon_g`, `horizon_b`, ";
- cmd.CommandText += "`horizon_i`, `haze_horizon`, `blue_density_r`, `blue_density_g`, `blue_density_b`, ";
- cmd.CommandText += "`blue_density_i`, `haze_density`, `density_multiplier`, `distance_multiplier`, `max_altitude`, ";
- cmd.CommandText += "`sun_moon_color_r`, `sun_moon_color_g`, `sun_moon_color_b`, `sun_moon_color_i`, `sun_moon_position`, ";
- cmd.CommandText += "`ambient_r`, `ambient_g`, `ambient_b`, `ambient_i`, `east_angle`, `sun_glow_focus`, `sun_glow_size`, ";
- cmd.CommandText += "`scene_gamma`, `star_brightness`, `cloud_color_r`, `cloud_color_g`, `cloud_color_b`, `cloud_color_i`, ";
- cmd.CommandText += "`cloud_x`, `cloud_y`, `cloud_density`, `cloud_coverage`, `cloud_scale`, `cloud_detail_x`, ";
- cmd.CommandText += "`cloud_detail_y`, `cloud_detail_density`, `cloud_scroll_x`, `cloud_scroll_x_lock`, `cloud_scroll_y`, ";
- cmd.CommandText += "`cloud_scroll_y_lock`, `draw_classic_clouds`) VALUES (?region_id, ?water_color_r, ";
- cmd.CommandText += "?water_color_g, ?water_color_b, ?water_fog_density_exponent, ?underwater_fog_modifier, ?reflection_wavelet_scale_1, ";
- cmd.CommandText += "?reflection_wavelet_scale_2, ?reflection_wavelet_scale_3, ?fresnel_scale, ?fresnel_offset, ?refract_scale_above, ";
- cmd.CommandText += "?refract_scale_below, ?blur_multiplier, ?big_wave_direction_x, ?big_wave_direction_y, ?little_wave_direction_x, ";
- cmd.CommandText += "?little_wave_direction_y, ?normal_map_texture, ?horizon_r, ?horizon_g, ?horizon_b, ?horizon_i, ?haze_horizon, ";
- cmd.CommandText += "?blue_density_r, ?blue_density_g, ?blue_density_b, ?blue_density_i, ?haze_density, ?density_multiplier, ";
- cmd.CommandText += "?distance_multiplier, ?max_altitude, ?sun_moon_color_r, ?sun_moon_color_g, ?sun_moon_color_b, ";
- cmd.CommandText += "?sun_moon_color_i, ?sun_moon_position, ?ambient_r, ?ambient_g, ?ambient_b, ?ambient_i, ?east_angle, ";
- cmd.CommandText += "?sun_glow_focus, ?sun_glow_size, ?scene_gamma, ?star_brightness, ?cloud_color_r, ?cloud_color_g, ";
- cmd.CommandText += "?cloud_color_b, ?cloud_color_i, ?cloud_x, ?cloud_y, ?cloud_density, ?cloud_coverage, ?cloud_scale, ";
- cmd.CommandText += "?cloud_detail_x, ?cloud_detail_y, ?cloud_detail_density, ?cloud_scroll_x, ?cloud_scroll_x_lock, ";
- cmd.CommandText += "?cloud_scroll_y, ?cloud_scroll_y_lock, ?draw_classic_clouds)";
-
- cmd.Parameters.AddWithValue("region_id", wl.regionID);
- cmd.Parameters.AddWithValue("water_color_r", wl.waterColor.X);
- cmd.Parameters.AddWithValue("water_color_g", wl.waterColor.Y);
- cmd.Parameters.AddWithValue("water_color_b", wl.waterColor.Z);
- cmd.Parameters.AddWithValue("water_fog_density_exponent", wl.waterFogDensityExponent);
- cmd.Parameters.AddWithValue("underwater_fog_modifier", wl.underwaterFogModifier);
- cmd.Parameters.AddWithValue("reflection_wavelet_scale_1", wl.reflectionWaveletScale.X);
- cmd.Parameters.AddWithValue("reflection_wavelet_scale_2", wl.reflectionWaveletScale.Y);
- cmd.Parameters.AddWithValue("reflection_wavelet_scale_3", wl.reflectionWaveletScale.Z);
- cmd.Parameters.AddWithValue("fresnel_scale", wl.fresnelScale);
- cmd.Parameters.AddWithValue("fresnel_offset", wl.fresnelOffset);
- cmd.Parameters.AddWithValue("refract_scale_above", wl.refractScaleAbove);
- cmd.Parameters.AddWithValue("refract_scale_below", wl.refractScaleBelow);
- cmd.Parameters.AddWithValue("blur_multiplier", wl.blurMultiplier);
- cmd.Parameters.AddWithValue("big_wave_direction_x", wl.bigWaveDirection.X);
- cmd.Parameters.AddWithValue("big_wave_direction_y", wl.bigWaveDirection.Y);
- cmd.Parameters.AddWithValue("little_wave_direction_x", wl.littleWaveDirection.X);
- cmd.Parameters.AddWithValue("little_wave_direction_y", wl.littleWaveDirection.Y);
- cmd.Parameters.AddWithValue("normal_map_texture", wl.normalMapTexture);
- cmd.Parameters.AddWithValue("horizon_r", wl.horizon.X);
- cmd.Parameters.AddWithValue("horizon_g", wl.horizon.Y);
- cmd.Parameters.AddWithValue("horizon_b", wl.horizon.Z);
- cmd.Parameters.AddWithValue("horizon_i", wl.horizon.W);
- cmd.Parameters.AddWithValue("haze_horizon", wl.hazeHorizon);
- cmd.Parameters.AddWithValue("blue_density_r", wl.blueDensity.X);
- cmd.Parameters.AddWithValue("blue_density_g", wl.blueDensity.Y);
- cmd.Parameters.AddWithValue("blue_density_b", wl.blueDensity.Z);
- cmd.Parameters.AddWithValue("blue_density_i", wl.blueDensity.W);
- cmd.Parameters.AddWithValue("haze_density", wl.hazeDensity);
- cmd.Parameters.AddWithValue("density_multiplier", wl.densityMultiplier);
- cmd.Parameters.AddWithValue("distance_multiplier", wl.distanceMultiplier);
- cmd.Parameters.AddWithValue("max_altitude", wl.maxAltitude);
- cmd.Parameters.AddWithValue("sun_moon_color_r", wl.sunMoonColor.X);
- cmd.Parameters.AddWithValue("sun_moon_color_g", wl.sunMoonColor.Y);
- cmd.Parameters.AddWithValue("sun_moon_color_b", wl.sunMoonColor.Z);
- cmd.Parameters.AddWithValue("sun_moon_color_i", wl.sunMoonColor.W);
- cmd.Parameters.AddWithValue("sun_moon_position", wl.sunMoonPosition);
- cmd.Parameters.AddWithValue("ambient_r", wl.ambient.X);
- cmd.Parameters.AddWithValue("ambient_g", wl.ambient.Y);
- cmd.Parameters.AddWithValue("ambient_b", wl.ambient.Z);
- cmd.Parameters.AddWithValue("ambient_i", wl.ambient.W);
- cmd.Parameters.AddWithValue("east_angle", wl.eastAngle);
- cmd.Parameters.AddWithValue("sun_glow_focus", wl.sunGlowFocus);
- cmd.Parameters.AddWithValue("sun_glow_size", wl.sunGlowSize);
- cmd.Parameters.AddWithValue("scene_gamma", wl.sceneGamma);
- cmd.Parameters.AddWithValue("star_brightness", wl.starBrightness);
- cmd.Parameters.AddWithValue("cloud_color_r", wl.cloudColor.X);
- cmd.Parameters.AddWithValue("cloud_color_g", wl.cloudColor.Y);
- cmd.Parameters.AddWithValue("cloud_color_b", wl.cloudColor.Z);
- cmd.Parameters.AddWithValue("cloud_color_i", wl.cloudColor.W);
- cmd.Parameters.AddWithValue("cloud_x", wl.cloudXYDensity.X);
- cmd.Parameters.AddWithValue("cloud_y", wl.cloudXYDensity.Y);
- cmd.Parameters.AddWithValue("cloud_density", wl.cloudXYDensity.Z);
- cmd.Parameters.AddWithValue("cloud_coverage", wl.cloudCoverage);
- cmd.Parameters.AddWithValue("cloud_scale", wl.cloudScale);
- cmd.Parameters.AddWithValue("cloud_detail_x", wl.cloudDetailXYDensity.X);
- cmd.Parameters.AddWithValue("cloud_detail_y", wl.cloudDetailXYDensity.Y);
- cmd.Parameters.AddWithValue("cloud_detail_density", wl.cloudDetailXYDensity.Z);
- cmd.Parameters.AddWithValue("cloud_scroll_x", wl.cloudScrollX);
- cmd.Parameters.AddWithValue("cloud_scroll_x_lock", wl.cloudScrollXLock);
- cmd.Parameters.AddWithValue("cloud_scroll_y", wl.cloudScrollY);
- cmd.Parameters.AddWithValue("cloud_scroll_y_lock", wl.cloudScrollYLock);
- cmd.Parameters.AddWithValue("draw_classic_clouds", wl.drawClassicClouds);
-
- ExecuteNonQuery(cmd);
+ dbcon.Open();
+
+ using (MySqlCommand cmd = dbcon.CreateCommand())
+ {
+ cmd.CommandText = "REPLACE INTO `regionwindlight` (`region_id`, `water_color_r`, `water_color_g`, ";
+ cmd.CommandText += "`water_color_b`, `water_fog_density_exponent`, `underwater_fog_modifier`, ";
+ cmd.CommandText += "`reflection_wavelet_scale_1`, `reflection_wavelet_scale_2`, `reflection_wavelet_scale_3`, ";
+ cmd.CommandText += "`fresnel_scale`, `fresnel_offset`, `refract_scale_above`, `refract_scale_below`, ";
+ cmd.CommandText += "`blur_multiplier`, `big_wave_direction_x`, `big_wave_direction_y`, `little_wave_direction_x`, ";
+ cmd.CommandText += "`little_wave_direction_y`, `normal_map_texture`, `horizon_r`, `horizon_g`, `horizon_b`, ";
+ cmd.CommandText += "`horizon_i`, `haze_horizon`, `blue_density_r`, `blue_density_g`, `blue_density_b`, ";
+ cmd.CommandText += "`blue_density_i`, `haze_density`, `density_multiplier`, `distance_multiplier`, `max_altitude`, ";
+ cmd.CommandText += "`sun_moon_color_r`, `sun_moon_color_g`, `sun_moon_color_b`, `sun_moon_color_i`, `sun_moon_position`, ";
+ cmd.CommandText += "`ambient_r`, `ambient_g`, `ambient_b`, `ambient_i`, `east_angle`, `sun_glow_focus`, `sun_glow_size`, ";
+ cmd.CommandText += "`scene_gamma`, `star_brightness`, `cloud_color_r`, `cloud_color_g`, `cloud_color_b`, `cloud_color_i`, ";
+ cmd.CommandText += "`cloud_x`, `cloud_y`, `cloud_density`, `cloud_coverage`, `cloud_scale`, `cloud_detail_x`, ";
+ cmd.CommandText += "`cloud_detail_y`, `cloud_detail_density`, `cloud_scroll_x`, `cloud_scroll_x_lock`, `cloud_scroll_y`, ";
+ cmd.CommandText += "`cloud_scroll_y_lock`, `draw_classic_clouds`) VALUES (?region_id, ?water_color_r, ";
+ cmd.CommandText += "?water_color_g, ?water_color_b, ?water_fog_density_exponent, ?underwater_fog_modifier, ?reflection_wavelet_scale_1, ";
+ cmd.CommandText += "?reflection_wavelet_scale_2, ?reflection_wavelet_scale_3, ?fresnel_scale, ?fresnel_offset, ?refract_scale_above, ";
+ cmd.CommandText += "?refract_scale_below, ?blur_multiplier, ?big_wave_direction_x, ?big_wave_direction_y, ?little_wave_direction_x, ";
+ cmd.CommandText += "?little_wave_direction_y, ?normal_map_texture, ?horizon_r, ?horizon_g, ?horizon_b, ?horizon_i, ?haze_horizon, ";
+ cmd.CommandText += "?blue_density_r, ?blue_density_g, ?blue_density_b, ?blue_density_i, ?haze_density, ?density_multiplier, ";
+ cmd.CommandText += "?distance_multiplier, ?max_altitude, ?sun_moon_color_r, ?sun_moon_color_g, ?sun_moon_color_b, ";
+ cmd.CommandText += "?sun_moon_color_i, ?sun_moon_position, ?ambient_r, ?ambient_g, ?ambient_b, ?ambient_i, ?east_angle, ";
+ cmd.CommandText += "?sun_glow_focus, ?sun_glow_size, ?scene_gamma, ?star_brightness, ?cloud_color_r, ?cloud_color_g, ";
+ cmd.CommandText += "?cloud_color_b, ?cloud_color_i, ?cloud_x, ?cloud_y, ?cloud_density, ?cloud_coverage, ?cloud_scale, ";
+ cmd.CommandText += "?cloud_detail_x, ?cloud_detail_y, ?cloud_detail_density, ?cloud_scroll_x, ?cloud_scroll_x_lock, ";
+ cmd.CommandText += "?cloud_scroll_y, ?cloud_scroll_y_lock, ?draw_classic_clouds)";
+
+ cmd.Parameters.AddWithValue("region_id", wl.regionID);
+ cmd.Parameters.AddWithValue("water_color_r", wl.waterColor.X);
+ cmd.Parameters.AddWithValue("water_color_g", wl.waterColor.Y);
+ cmd.Parameters.AddWithValue("water_color_b", wl.waterColor.Z);
+ cmd.Parameters.AddWithValue("water_fog_density_exponent", wl.waterFogDensityExponent);
+ cmd.Parameters.AddWithValue("underwater_fog_modifier", wl.underwaterFogModifier);
+ cmd.Parameters.AddWithValue("reflection_wavelet_scale_1", wl.reflectionWaveletScale.X);
+ cmd.Parameters.AddWithValue("reflection_wavelet_scale_2", wl.reflectionWaveletScale.Y);
+ cmd.Parameters.AddWithValue("reflection_wavelet_scale_3", wl.reflectionWaveletScale.Z);
+ cmd.Parameters.AddWithValue("fresnel_scale", wl.fresnelScale);
+ cmd.Parameters.AddWithValue("fresnel_offset", wl.fresnelOffset);
+ cmd.Parameters.AddWithValue("refract_scale_above", wl.refractScaleAbove);
+ cmd.Parameters.AddWithValue("refract_scale_below", wl.refractScaleBelow);
+ cmd.Parameters.AddWithValue("blur_multiplier", wl.blurMultiplier);
+ cmd.Parameters.AddWithValue("big_wave_direction_x", wl.bigWaveDirection.X);
+ cmd.Parameters.AddWithValue("big_wave_direction_y", wl.bigWaveDirection.Y);
+ cmd.Parameters.AddWithValue("little_wave_direction_x", wl.littleWaveDirection.X);
+ cmd.Parameters.AddWithValue("little_wave_direction_y", wl.littleWaveDirection.Y);
+ cmd.Parameters.AddWithValue("normal_map_texture", wl.normalMapTexture);
+ cmd.Parameters.AddWithValue("horizon_r", wl.horizon.X);
+ cmd.Parameters.AddWithValue("horizon_g", wl.horizon.Y);
+ cmd.Parameters.AddWithValue("horizon_b", wl.horizon.Z);
+ cmd.Parameters.AddWithValue("horizon_i", wl.horizon.W);
+ cmd.Parameters.AddWithValue("haze_horizon", wl.hazeHorizon);
+ cmd.Parameters.AddWithValue("blue_density_r", wl.blueDensity.X);
+ cmd.Parameters.AddWithValue("blue_density_g", wl.blueDensity.Y);
+ cmd.Parameters.AddWithValue("blue_density_b", wl.blueDensity.Z);
+ cmd.Parameters.AddWithValue("blue_density_i", wl.blueDensity.W);
+ cmd.Parameters.AddWithValue("haze_density", wl.hazeDensity);
+ cmd.Parameters.AddWithValue("density_multiplier", wl.densityMultiplier);
+ cmd.Parameters.AddWithValue("distance_multiplier", wl.distanceMultiplier);
+ cmd.Parameters.AddWithValue("max_altitude", wl.maxAltitude);
+ cmd.Parameters.AddWithValue("sun_moon_color_r", wl.sunMoonColor.X);
+ cmd.Parameters.AddWithValue("sun_moon_color_g", wl.sunMoonColor.Y);
+ cmd.Parameters.AddWithValue("sun_moon_color_b", wl.sunMoonColor.Z);
+ cmd.Parameters.AddWithValue("sun_moon_color_i", wl.sunMoonColor.W);
+ cmd.Parameters.AddWithValue("sun_moon_position", wl.sunMoonPosition);
+ cmd.Parameters.AddWithValue("ambient_r", wl.ambient.X);
+ cmd.Parameters.AddWithValue("ambient_g", wl.ambient.Y);
+ cmd.Parameters.AddWithValue("ambient_b", wl.ambient.Z);
+ cmd.Parameters.AddWithValue("ambient_i", wl.ambient.W);
+ cmd.Parameters.AddWithValue("east_angle", wl.eastAngle);
+ cmd.Parameters.AddWithValue("sun_glow_focus", wl.sunGlowFocus);
+ cmd.Parameters.AddWithValue("sun_glow_size", wl.sunGlowSize);
+ cmd.Parameters.AddWithValue("scene_gamma", wl.sceneGamma);
+ cmd.Parameters.AddWithValue("star_brightness", wl.starBrightness);
+ cmd.Parameters.AddWithValue("cloud_color_r", wl.cloudColor.X);
+ cmd.Parameters.AddWithValue("cloud_color_g", wl.cloudColor.Y);
+ cmd.Parameters.AddWithValue("cloud_color_b", wl.cloudColor.Z);
+ cmd.Parameters.AddWithValue("cloud_color_i", wl.cloudColor.W);
+ cmd.Parameters.AddWithValue("cloud_x", wl.cloudXYDensity.X);
+ cmd.Parameters.AddWithValue("cloud_y", wl.cloudXYDensity.Y);
+ cmd.Parameters.AddWithValue("cloud_density", wl.cloudXYDensity.Z);
+ cmd.Parameters.AddWithValue("cloud_coverage", wl.cloudCoverage);
+ cmd.Parameters.AddWithValue("cloud_scale", wl.cloudScale);
+ cmd.Parameters.AddWithValue("cloud_detail_x", wl.cloudDetailXYDensity.X);
+ cmd.Parameters.AddWithValue("cloud_detail_y", wl.cloudDetailXYDensity.Y);
+ cmd.Parameters.AddWithValue("cloud_detail_density", wl.cloudDetailXYDensity.Z);
+ cmd.Parameters.AddWithValue("cloud_scroll_x", wl.cloudScrollX);
+ cmd.Parameters.AddWithValue("cloud_scroll_x_lock", wl.cloudScrollXLock);
+ cmd.Parameters.AddWithValue("cloud_scroll_y", wl.cloudScrollY);
+ cmd.Parameters.AddWithValue("cloud_scroll_y_lock", wl.cloudScrollYLock);
+ cmd.Parameters.AddWithValue("draw_classic_clouds", wl.drawClassicClouds);
+
+ ExecuteNonQuery(cmd);
+ }
}
}
}
public virtual void RemoveRegionWindlightSettings(UUID regionID)
{
- using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
+ lock (m_dbLock)
{
- dbcon.Open();
-
- using (MySqlCommand cmd = dbcon.CreateCommand())
+ using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
- cmd.CommandText = "delete from `regionwindlight` where `region_id`=?regionID";
- cmd.Parameters.AddWithValue("?regionID", regionID.ToString());
- ExecuteNonQuery(cmd);
+ dbcon.Open();
+
+ using (MySqlCommand cmd = dbcon.CreateCommand())
+ {
+ cmd.CommandText = "delete from `regionwindlight` where `region_id`=?regionID";
+ cmd.Parameters.AddWithValue("?regionID", regionID.ToString());
+ ExecuteNonQuery(cmd);
+ }
}
}
}
@@ -1000,26 +1010,29 @@ namespace OpenSim.Data.MySQL
#region RegionEnvironmentSettings
public string LoadRegionEnvironmentSettings(UUID regionUUID)
{
- using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
+ lock (m_dbLock)
{
- dbcon.Open();
-
- string command = "select * from `regionenvironment` where region_id = ?region_id";
-
- using (MySqlCommand cmd = new MySqlCommand(command))
+ using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
- cmd.Connection = dbcon;
-
- cmd.Parameters.AddWithValue("?region_id", regionUUID.ToString());
-
- IDataReader result = ExecuteReader(cmd);
- if (!result.Read())
+ dbcon.Open();
+
+ string command = "select * from `regionenvironment` where region_id = ?region_id";
+
+ using (MySqlCommand cmd = new MySqlCommand(command))
{
- return String.Empty;
- }
- else
- {
- return Convert.ToString(result["llsd_settings"]);
+ cmd.Connection = dbcon;
+
+ cmd.Parameters.AddWithValue("?region_id", regionUUID.ToString());
+
+ IDataReader result = ExecuteReader(cmd);
+ if (!result.Read())
+ {
+ return String.Empty;
+ }
+ else
+ {
+ return Convert.ToString(result["llsd_settings"]);
+ }
}
}
}
@@ -1027,33 +1040,39 @@ namespace OpenSim.Data.MySQL
public void StoreRegionEnvironmentSettings(UUID regionUUID, string settings)
{
- using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
+ lock (m_dbLock)
{
- dbcon.Open();
-
- using (MySqlCommand cmd = dbcon.CreateCommand())
+ using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
- cmd.CommandText = "REPLACE INTO `regionenvironment` (`region_id`, `llsd_settings`) VALUES (?region_id, ?llsd_settings)";
-
- cmd.Parameters.AddWithValue("region_id", regionUUID);
- cmd.Parameters.AddWithValue("llsd_settings", settings);
-
- ExecuteNonQuery(cmd);
+ dbcon.Open();
+
+ using (MySqlCommand cmd = dbcon.CreateCommand())
+ {
+ cmd.CommandText = "REPLACE INTO `regionenvironment` (`region_id`, `llsd_settings`) VALUES (?region_id, ?llsd_settings)";
+
+ cmd.Parameters.AddWithValue("region_id", regionUUID);
+ cmd.Parameters.AddWithValue("llsd_settings", settings);
+
+ ExecuteNonQuery(cmd);
+ }
}
}
}
public void RemoveRegionEnvironmentSettings(UUID regionUUID)
{
- using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
+ lock (m_dbLock)
{
- dbcon.Open();
-
- using (MySqlCommand cmd = dbcon.CreateCommand())
+ using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
- cmd.CommandText = "delete from `regionenvironment` where region_id = ?region_id";
- cmd.Parameters.AddWithValue("?region_id", regionUUID.ToString());
- ExecuteNonQuery(cmd);
+ dbcon.Open();
+
+ using (MySqlCommand cmd = dbcon.CreateCommand())
+ {
+ cmd.CommandText = "delete from `regionenvironment` where region_id = ?region_id";
+ cmd.Parameters.AddWithValue("?region_id", regionUUID.ToString());
+ ExecuteNonQuery(cmd);
+ }
}
}
}
diff --git a/OpenSim/Data/MySQL/Resources/GridUserStore.migrations b/OpenSim/Data/MySQL/Resources/GridUserStore.migrations
index 32b85ee8c1..d08e096364 100644
--- a/OpenSim/Data/MySQL/Resources/GridUserStore.migrations
+++ b/OpenSim/Data/MySQL/Resources/GridUserStore.migrations
@@ -17,3 +17,8 @@ CREATE TABLE `GridUser` (
) ENGINE=InnoDB;
COMMIT;
+
+:VERSION 2 # --------------------------
+BEGIN;
+
+COMMIT;
diff --git a/OpenSim/Data/Null/NullRegionData.cs b/OpenSim/Data/Null/NullRegionData.cs
index deb50cb4df..b4d701af50 100644
--- a/OpenSim/Data/Null/NullRegionData.cs
+++ b/OpenSim/Data/Null/NullRegionData.cs
@@ -33,6 +33,7 @@ using OpenSim.Framework;
using OpenSim.Data;
using System.Reflection;
using log4net;
+using RegionFlags = OpenSim.Framework.RegionFlags;
namespace OpenSim.Data.Null
{
diff --git a/OpenSim/Data/SQLite/SQLiteSimulationData.cs b/OpenSim/Data/SQLite/SQLiteSimulationData.cs
index 431709f8d5..42cd59de31 100644
--- a/OpenSim/Data/SQLite/SQLiteSimulationData.cs
+++ b/OpenSim/Data/SQLite/SQLiteSimulationData.cs
@@ -1366,6 +1366,13 @@ namespace OpenSim.Data.SQLite
createCol(land, "UserLookAtZ", typeof(Double));
createCol(land, "AuthbuyerID", typeof(String));
createCol(land, "OtherCleanTime", typeof(Int32));
+ createCol(land, "Dwell", typeof(Int32));
+ createCol(land, "MediaType", typeof(String));
+ createCol(land, "MediaDescription", typeof(String));
+ createCol(land, "MediaSize", typeof(String));
+ createCol(land, "MediaLoop", typeof(Boolean));
+ createCol(land, "ObscureMedia", typeof(Boolean));
+ createCol(land, "ObscureMusic", typeof(Boolean));
land.PrimaryKey = new DataColumn[] { land.Columns["UUID"] };
@@ -1781,9 +1788,16 @@ namespace OpenSim.Data.SQLite
newData.PassHours = Convert.ToSingle(row["PassHours"]);
newData.PassPrice = Convert.ToInt32(row["PassPrice"]);
newData.SnapshotID = (UUID)(String)row["SnapshotUUID"];
+ newData.Dwell = Convert.ToInt32(row["Dwell"]);
+ newData.MediaType = (String)row["MediaType"];
+ newData.MediaDescription = (String)row["MediaDescription"];
+ newData.MediaWidth = Convert.ToInt32((((string)row["MediaSize"]).Split(','))[0]);
+ newData.MediaHeight = Convert.ToInt32((((string)row["MediaSize"]).Split(','))[1]);
+ newData.MediaLoop = Convert.ToBoolean(row["MediaLoop"]);
+ newData.ObscureMedia = Convert.ToBoolean(row["ObscureMedia"]);
+ newData.ObscureMusic = Convert.ToBoolean(row["ObscureMusic"]);
try
{
-
newData.UserLocation =
new Vector3(Convert.ToSingle(row["UserLocationX"]), Convert.ToSingle(row["UserLocationY"]),
Convert.ToSingle(row["UserLocationZ"]));
@@ -2195,12 +2209,13 @@ namespace OpenSim.Data.SQLite
row["UserLookAtZ"] = land.UserLookAt.Z;
row["AuthbuyerID"] = land.AuthBuyerID.ToString();
row["OtherCleanTime"] = land.OtherCleanTime;
+ row["Dwell"] = land.Dwell;
row["MediaType"] = land.MediaType;
row["MediaDescription"] = land.MediaDescription;
- row["MediaSize"] = land.MediaWidth.ToString() + "," + land.MediaHeight.ToString();
- row["MediaLoop"] = land.MediaLoop.ToString();
- row["ObscureMusic"] = land.ObscureMusic.ToString();
- row["ObscureMedia"] = land.ObscureMedia.ToString();
+ row["MediaSize"] = String.Format("{0},{1}", land.MediaWidth, land.MediaHeight);
+ row["MediaLoop"] = land.MediaLoop;
+ row["ObscureMusic"] = land.ObscureMusic;
+ row["ObscureMedia"] = land.ObscureMedia;
}
///
diff --git a/OpenSim/Framework/AssetPermissions.cs b/OpenSim/Framework/AssetPermissions.cs
new file mode 100644
index 0000000000..4a905c2f5d
--- /dev/null
+++ b/OpenSim/Framework/AssetPermissions.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+
+using Nini.Config;
+using log4net;
+
+using OpenMetaverse;
+
+namespace OpenSim.Framework
+{
+ public class AssetPermissions
+ {
+ private static readonly ILog m_log =
+ LogManager.GetLogger(
+ MethodBase.GetCurrentMethod().DeclaringType);
+
+ private bool[] m_DisallowExport, m_DisallowImport;
+ private string[] m_AssetTypeNames;
+
+ public AssetPermissions(IConfig config)
+ {
+ Type enumType = typeof(AssetType);
+ m_AssetTypeNames = Enum.GetNames(enumType);
+ for (int i = 0; i < m_AssetTypeNames.Length; i++)
+ m_AssetTypeNames[i] = m_AssetTypeNames[i].ToLower();
+ int n = Enum.GetValues(enumType).Length;
+ m_DisallowExport = new bool[n];
+ m_DisallowImport = new bool[n];
+
+ LoadPermsFromConfig(config, "DisallowExport", m_DisallowExport);
+ LoadPermsFromConfig(config, "DisallowImport", m_DisallowImport);
+
+ }
+
+ private void LoadPermsFromConfig(IConfig assetConfig, string variable, bool[] bitArray)
+ {
+ if (assetConfig == null)
+ return;
+
+ string perms = assetConfig.GetString(variable, String.Empty);
+ string[] parts = perms.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ foreach (string s in parts)
+ {
+ int index = Array.IndexOf(m_AssetTypeNames, s.Trim().ToLower());
+ if (index >= 0)
+ bitArray[index] = true;
+ else
+ m_log.WarnFormat("[Asset Permissions]: Invalid AssetType {0}", s);
+ }
+
+ }
+
+ public bool AllowedExport(sbyte type)
+ {
+ string assetTypeName = ((AssetType)type).ToString();
+
+ int index = Array.IndexOf(m_AssetTypeNames, assetTypeName.ToLower());
+ if (index >= 0 && m_DisallowExport[index])
+ {
+ m_log.DebugFormat("[Asset Permissions]: Export denied: configuration does not allow export of AssetType {0}", assetTypeName);
+ return false;
+ }
+
+ return true;
+ }
+
+ public bool AllowedImport(sbyte type)
+ {
+ string assetTypeName = ((AssetType)type).ToString();
+
+ int index = Array.IndexOf(m_AssetTypeNames, assetTypeName.ToLower());
+ if (index >= 0 && m_DisallowImport[index])
+ {
+ m_log.DebugFormat("[Asset Permissions]: Import denied: configuration does not allow import of AssetType {0}", assetTypeName);
+ return false;
+ }
+
+ return true;
+ }
+
+
+ }
+}
diff --git a/OpenSim/Framework/AvatarAppearance.cs b/OpenSim/Framework/AvatarAppearance.cs
index c5d9641da7..16385417e6 100644
--- a/OpenSim/Framework/AvatarAppearance.cs
+++ b/OpenSim/Framework/AvatarAppearance.cs
@@ -358,6 +358,9 @@ namespace OpenSim.Framework
SetVisualParams(visualParams);
}
+ ///
+ /// Set avatar height by a calculation based on their visual parameters.
+ ///
public virtual void SetHeight()
{
// Start with shortest possible female avatar height
diff --git a/OpenSim/Framework/Cache.cs b/OpenSim/Framework/Cache.cs
index 79e20fc88e..31cab4a021 100644
--- a/OpenSim/Framework/Cache.cs
+++ b/OpenSim/Framework/Cache.cs
@@ -199,7 +199,14 @@ namespace OpenSim.Framework
//
public class Cache
{
+ ///
+ /// Must only be accessed under lock.
+ ///
private List m_Index = new List();
+
+ ///
+ /// Must only be accessed under m_Index lock.
+ ///
private Dictionary m_Lookup =
new Dictionary();
@@ -320,19 +327,19 @@ namespace OpenSim.Framework
{
if (m_Lookup.ContainsKey(index))
item = m_Lookup[index];
- }
- if (item == null)
- {
+ if (item == null)
+ {
+ Expire(true);
+ return null;
+ }
+
+ item.hits++;
+ item.lastUsed = DateTime.Now;
+
Expire(true);
- return null;
}
- item.hits++;
- item.lastUsed = DateTime.Now;
-
- Expire(true);
-
return item;
}
@@ -385,7 +392,10 @@ namespace OpenSim.Framework
//
public Object Find(Predicate d)
{
- CacheItemBase item = m_Index.Find(d);
+ CacheItemBase item;
+
+ lock (m_Index)
+ item = m_Index.Find(d);
if (item == null)
return null;
@@ -419,12 +429,12 @@ namespace OpenSim.Framework
public virtual void Store(string index, Object data, Type container,
Object[] parameters)
{
- Expire(false);
-
CacheItemBase item;
lock (m_Index)
{
+ Expire(false);
+
if (m_Index.Contains(new CacheItemBase(index)))
{
if ((m_Flags & CacheFlags.AllowUpdate) != 0)
@@ -450,9 +460,17 @@ namespace OpenSim.Framework
m_Index.Add(item);
m_Lookup[index] = item;
}
+
item.Store(data);
}
+ ///
+ /// Expire items as appropriate.
+ ///
+ ///
+ /// Callers must lock m_Index.
+ ///
+ ///
protected virtual void Expire(bool getting)
{
if (getting && (m_Strategy == CacheStrategy.Aggressive))
@@ -475,12 +493,10 @@ namespace OpenSim.Framework
switch (m_Strategy)
{
- case CacheStrategy.Aggressive:
- if (Count < Size)
- return;
+ case CacheStrategy.Aggressive:
+ if (Count < Size)
+ return;
- lock (m_Index)
- {
m_Index.Sort(new SortLRU());
m_Index.Reverse();
@@ -490,7 +506,7 @@ namespace OpenSim.Framework
ExpireDelegate doExpire = OnExpire;
- if (doExpire != null)
+ if (doExpire != null)
{
List candidates =
m_Index.GetRange(target, Count - target);
@@ -513,27 +529,34 @@ namespace OpenSim.Framework
foreach (CacheItemBase item in m_Index)
m_Lookup[item.uuid] = item;
}
- }
- break;
- default:
- break;
+
+ break;
+
+ default:
+ break;
}
}
public void Invalidate(string uuid)
{
- if (!m_Lookup.ContainsKey(uuid))
- return;
+ lock (m_Index)
+ {
+ if (!m_Lookup.ContainsKey(uuid))
+ return;
- CacheItemBase item = m_Lookup[uuid];
- m_Lookup.Remove(uuid);
- m_Index.Remove(item);
+ CacheItemBase item = m_Lookup[uuid];
+ m_Lookup.Remove(uuid);
+ m_Index.Remove(item);
+ }
}
public void Clear()
{
- m_Index.Clear();
- m_Lookup.Clear();
+ lock (m_Index)
+ {
+ m_Index.Clear();
+ m_Lookup.Clear();
+ }
}
}
-}
+}
\ No newline at end of file
diff --git a/OpenSim/Framework/Client/IClientChat.cs b/OpenSim/Framework/Client/IClientChat.cs
index 078ea9b668..86b1faa8f6 100644
--- a/OpenSim/Framework/Client/IClientChat.cs
+++ b/OpenSim/Framework/Client/IClientChat.cs
@@ -33,7 +33,8 @@ namespace OpenSim.Framework.Client
{
event ChatMessage OnChatFromClient;
- void SendChatMessage(string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, byte source,
- byte audible);
+ void SendChatMessage(
+ string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, UUID ownerID, byte source,
+ byte audible);
}
-}
+}
\ No newline at end of file
diff --git a/OpenSim/Framework/Console/ConsoleUtil.cs b/OpenSim/Framework/Console/ConsoleUtil.cs
new file mode 100644
index 0000000000..16a63e0ca5
--- /dev/null
+++ b/OpenSim/Framework/Console/ConsoleUtil.cs
@@ -0,0 +1,228 @@
+/*
+ * 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.IO;
+using System.Linq;
+using System.Reflection;
+using log4net;
+using OpenMetaverse;
+
+namespace OpenSim.Framework.Console
+{
+ public class ConsoleUtil
+ {
+ // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ public const int LocalIdNotFound = 0;
+
+ ///
+ /// Used by modules to display stock co-ordinate help, though possibly this should be under some general section
+ /// rather than in each help summary.
+ ///
+ public const string CoordHelp
+ = @"Each component of the coord is comma separated. There must be no spaces between the commas.
+ If you don't care about the z component you can simply omit it.
+ If you don't care about the x or y components then you can leave them blank (though a comma is still required)
+ If you want to specify the maxmimum value of a component then you can use ~ instead of a number
+ If you want to specify the minimum value of a component then you can use -~ instead of a number
+ e.g.
+ delete object pos 20,20,20 to 40,40,40
+ delete object pos 20,20 to 40,40
+ delete object pos ,20,20 to ,40,40
+ delete object pos ,,30 to ,,~
+ delete object pos ,,-~ to ,,30";
+
+ public const string MinRawConsoleVectorValue = "-~";
+ public const string MaxRawConsoleVectorValue = "~";
+
+ public const string VectorSeparator = ",";
+ public static char[] VectorSeparatorChars = VectorSeparator.ToCharArray();
+
+ ///
+ /// Check if the given file path exists.
+ ///
+ /// If not, warning is printed to the given console.
+ /// true if the file does not exist, false otherwise.
+ ///
+ ///
+ public static bool CheckFileDoesNotExist(ICommandConsole console, string path)
+ {
+ if (File.Exists(path))
+ {
+ console.OutputFormat("File {0} already exists. Please move or remove it.", path);
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Try to parse a console UUID from the console.
+ ///
+ ///
+ /// Will complain to the console if parsing fails.
+ ///
+ ///
+ /// If null then no complaint is printed.
+ ///
+ ///
+ public static bool TryParseConsoleUuid(ICommandConsole console, string rawUuid, out UUID uuid)
+ {
+ if (!UUID.TryParse(rawUuid, out uuid))
+ {
+ if (console != null)
+ console.OutputFormat("{0} is not a valid uuid", rawUuid);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ public static bool TryParseConsoleLocalId(ICommandConsole console, string rawLocalId, out uint localId)
+ {
+ if (!uint.TryParse(rawLocalId, out localId))
+ {
+ if (console != null)
+ console.OutputFormat("{0} is not a valid local id", localId);
+
+ return false;
+ }
+
+ if (localId == 0)
+ {
+ if (console != null)
+ console.OutputFormat("{0} is not a valid local id - it must be greater than 0", localId);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Tries to parse the input as either a UUID or a local ID.
+ ///
+ /// true if parsing succeeded, false otherwise.
+ ///
+ ///
+ ///
+ ///
+ /// Will be set to ConsoleUtil.LocalIdNotFound if parsing result was a UUID or no parse succeeded.
+ ///
+ public static bool TryParseConsoleId(ICommandConsole console, string rawId, out UUID uuid, out uint localId)
+ {
+ if (TryParseConsoleUuid(null, rawId, out uuid))
+ {
+ localId = LocalIdNotFound;
+ return true;
+ }
+
+ if (TryParseConsoleLocalId(null, rawId, out localId))
+ {
+ return true;
+ }
+
+ if (console != null)
+ console.OutputFormat("{0} is not a valid UUID or local id", rawId);
+
+ return false;
+ }
+
+ ///
+ /// Convert a minimum vector input from the console to an OpenMetaverse.Vector3
+ ///
+ /// /param>
+ ///
+ ///
+ public static bool TryParseConsoleMinVector(string rawConsoleVector, out Vector3 vector)
+ {
+ return TryParseConsoleVector(rawConsoleVector, c => float.MinValue.ToString(), out vector);
+ }
+
+ ///
+ /// Convert a maximum vector input from the console to an OpenMetaverse.Vector3
+ ///
+ /// /param>
+ ///
+ ///
+ public static bool TryParseConsoleMaxVector(string rawConsoleVector, out Vector3 vector)
+ {
+ return TryParseConsoleVector(rawConsoleVector, c => float.MaxValue.ToString(), out vector);
+ }
+
+ ///
+ /// Convert a vector input from the console to an OpenMetaverse.Vector3
+ ///
+ ///
+ /// A string in the form ,, where there is no space between values.
+ /// Any component can be missing (e.g. ,,40). blankComponentFunc is invoked to replace the blank with a suitable value
+ /// Also, if the blank component is at the end, then the comma can be missed off entirely (e.g. 40,30 or 40)
+ /// The strings "~" and "-~" are valid in components. The first substitutes float.MaxValue whilst the second is float.MinValue
+ /// Other than that, component values must be numeric.
+ ///
+ ///
+ ///
+ ///
+ public static bool TryParseConsoleVector(
+ string rawConsoleVector, Func blankComponentFunc, out Vector3 vector)
+ {
+ List components = rawConsoleVector.Split(VectorSeparatorChars).ToList();
+
+ if (components.Count < 1 || components.Count > 3)
+ {
+ vector = Vector3.Zero;
+ return false;
+ }
+
+ for (int i = components.Count; i < 3; i++)
+ components.Add("");
+
+ List semiDigestedComponents
+ = components.ConvertAll(
+ c =>
+ {
+ if (c == "")
+ return blankComponentFunc.Invoke(c);
+ else if (c == MaxRawConsoleVectorValue)
+ return float.MaxValue.ToString();
+ else if (c == MinRawConsoleVectorValue)
+ return float.MinValue.ToString();
+ else
+ return c;
+ });
+
+ string semiDigestedConsoleVector = string.Join(VectorSeparator, semiDigestedComponents.ToArray());
+
+ // m_log.DebugFormat("[CONSOLE UTIL]: Parsing {0} into OpenMetaverse.Vector3", semiDigestedConsoleVector);
+
+ return Vector3.TryParse(semiDigestedConsoleVector, out vector);
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenSim/Framework/Constants.cs b/OpenSim/Framework/Constants.cs
index 1b1aaf2bc4..a2eb5ee18e 100644
--- a/OpenSim/Framework/Constants.cs
+++ b/OpenSim/Framework/Constants.cs
@@ -31,6 +31,7 @@ namespace OpenSim.Framework
public class Constants
{
public const uint RegionSize = 256;
+ public const uint RegionHeight = 4096;
public const byte TerrainPatchSize = 16;
public const string DefaultTexture = "89556747-24cb-43ed-920b-47caed15465f";
diff --git a/OpenSim/Framework/EstateSettings.cs b/OpenSim/Framework/EstateSettings.cs
index 9020761cad..e03750bcc5 100644
--- a/OpenSim/Framework/EstateSettings.cs
+++ b/OpenSim/Framework/EstateSettings.cs
@@ -419,11 +419,11 @@ namespace OpenSim.Framework
public void SetFromFlags(ulong regionFlags)
{
- ResetHomeOnTeleport = ((regionFlags & (ulong)RegionFlags.ResetHomeOnTeleport) == (ulong)RegionFlags.ResetHomeOnTeleport);
- BlockDwell = ((regionFlags & (ulong)RegionFlags.BlockDwell) == (ulong)RegionFlags.BlockDwell);
- AllowLandmark = ((regionFlags & (ulong)RegionFlags.AllowLandmark) == (ulong)RegionFlags.AllowLandmark);
- AllowParcelChanges = ((regionFlags & (ulong)RegionFlags.AllowParcelChanges) == (ulong)RegionFlags.AllowParcelChanges);
- AllowSetHome = ((regionFlags & (ulong)RegionFlags.AllowSetHome) == (ulong)RegionFlags.AllowSetHome);
+ ResetHomeOnTeleport = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.ResetHomeOnTeleport) == (ulong)OpenMetaverse.RegionFlags.ResetHomeOnTeleport);
+ BlockDwell = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.BlockDwell) == (ulong)OpenMetaverse.RegionFlags.BlockDwell);
+ AllowLandmark = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.AllowLandmark) == (ulong)OpenMetaverse.RegionFlags.AllowLandmark);
+ AllowParcelChanges = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.AllowParcelChanges) == (ulong)OpenMetaverse.RegionFlags.AllowParcelChanges);
+ AllowSetHome = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.AllowSetHome) == (ulong)OpenMetaverse.RegionFlags.AllowSetHome);
}
public bool GroupAccess(UUID groupID)
diff --git a/OpenSim/Framework/GridInstantMessage.cs b/OpenSim/Framework/GridInstantMessage.cs
index a6bf6e3c32..6ae0488fc2 100644
--- a/OpenSim/Framework/GridInstantMessage.cs
+++ b/OpenSim/Framework/GridInstantMessage.cs
@@ -44,7 +44,6 @@ namespace OpenSim.Framework
public Vector3 Position;
public byte[] binaryBucket;
-
public uint ParentEstateID;
public Guid RegionID;
public uint timestamp;
@@ -58,7 +57,7 @@ namespace OpenSim.Framework
string _fromAgentName, UUID _toAgentID,
byte _dialog, bool _fromGroup, string _message,
UUID _imSessionID, bool _offline, Vector3 _position,
- byte[] _binaryBucket)
+ byte[] _binaryBucket, bool addTimestamp)
{
fromAgentID = _fromAgentID.Guid;
fromAgentName = _fromAgentName;
@@ -79,7 +78,9 @@ namespace OpenSim.Framework
ParentEstateID = scene.RegionInfo.EstateSettings.ParentEstateID;
RegionID = scene.RegionInfo.RegionSettings.RegionUUID.Guid;
}
- timestamp = (uint)Util.UnixTimeSinceEpoch();
+
+ if (addTimestamp)
+ timestamp = (uint)Util.UnixTimeSinceEpoch();
}
public GridInstantMessage(IScene scene, UUID _fromAgentID,
@@ -87,7 +88,7 @@ namespace OpenSim.Framework
string _message, bool _offline,
Vector3 _position) : this(scene, _fromAgentID, _fromAgentName,
_toAgentID, _dialog, false, _message,
- _fromAgentID ^ _toAgentID, _offline, _position, new byte[0])
+ _fromAgentID ^ _toAgentID, _offline, _position, new byte[0], true)
{
}
}
diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs
index e31c7f6d23..1c6685aec6 100644
--- a/OpenSim/Framework/IClientAPI.cs
+++ b/OpenSim/Framework/IClientAPI.cs
@@ -815,8 +815,23 @@ namespace OpenSim.Framework
event Action OnRegionHandShakeReply;
event GenericCall1 OnRequestWearables;
event Action OnCompleteMovementToRegion;
+
+ ///
+ /// Called when an AgentUpdate message is received and before OnAgentUpdate.
+ ///
+ ///
+ /// Listeners must not retain a reference to AgentUpdateArgs since this object may be reused for subsequent AgentUpdates.
+ ///
event UpdateAgent OnPreAgentUpdate;
+
+ ///
+ /// Called when an AgentUpdate message is received and after OnPreAgentUpdate.
+ ///
+ ///
+ /// Listeners must not retain a reference to AgentUpdateArgs since this object may be reused for subsequent AgentUpdates.
+ ///
event UpdateAgent OnAgentUpdate;
+
event AgentRequestSit OnAgentRequestSit;
event AgentSit OnAgentSit;
event AvatarPickerRequest OnAvatarPickerRequest;
@@ -1046,8 +1061,21 @@ namespace OpenSim.Framework
void InPacket(object NewPack);
void ProcessInPacket(Packet NewPack);
+
+ ///
+ /// Close this client
+ ///
void Close();
- void Close(bool sendStop);
+
+ ///
+ /// Close this client
+ ///
+ ///
+ /// If true, attempts the close without checking active status. You do not want to try this except as a last
+ /// ditch attempt where Active == false but the ScenePresence still exists.
+ ///
+ void Close(bool sendStop, bool force);
+
void Kick(string message);
///
@@ -1084,8 +1112,20 @@ namespace OpenSim.Framework
void SendAnimations(UUID[] animID, int[] seqs, UUID sourceAgentId, UUID[] objectIDs);
void SendRegionHandshake(RegionInfo regionInfo, RegionHandshakeArgs args);
- void SendChatMessage(string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, byte source,
- byte audible);
+ ///
+ /// Send chat to the viewer.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ void SendChatMessage(
+ string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, UUID ownerID, byte source,
+ byte audible);
void SendInstantMessage(GridInstantMessage im);
diff --git a/OpenSim/Framework/InventoryFolderBase.cs b/OpenSim/Framework/InventoryFolderBase.cs
index a12183c535..b3457a65b9 100644
--- a/OpenSim/Framework/InventoryFolderBase.cs
+++ b/OpenSim/Framework/InventoryFolderBase.cs
@@ -73,33 +73,27 @@ namespace OpenSim.Framework
{
}
- public InventoryFolderBase(UUID id)
+ public InventoryFolderBase(UUID id) : this()
{
ID = id;
}
- public InventoryFolderBase(UUID id, UUID owner)
+ public InventoryFolderBase(UUID id, UUID owner) : this(id)
{
- ID = id;
Owner = owner;
}
- public InventoryFolderBase(UUID id, string name, UUID owner, UUID parent)
+ public InventoryFolderBase(UUID id, string name, UUID owner, UUID parent) : this(id, owner)
{
- ID = id;
Name = name;
- Owner = owner;
ParentID = parent;
}
- public InventoryFolderBase(UUID id, string name, UUID owner, short type, UUID parent, ushort version)
+ public InventoryFolderBase(
+ UUID id, string name, UUID owner, short type, UUID parent, ushort version) : this(id, name, owner, parent)
{
- ID = id;
- Name = name;
- Owner = owner;
Type = type;
- ParentID = parent;
Version = version;
}
}
-}
+}
\ No newline at end of file
diff --git a/OpenSim/Framework/LandData.cs b/OpenSim/Framework/LandData.cs
index dcaa46d960..4dffd3f22d 100644
--- a/OpenSim/Framework/LandData.cs
+++ b/OpenSim/Framework/LandData.cs
@@ -49,8 +49,8 @@ namespace OpenSim.Framework
// use only one serializer to give the runtime a chance to
// optimize it (it won't do that if you use a new instance
// every time)
- private static XmlSerializer serializer = new XmlSerializer(typeof (LandData));
-
+ private static XmlSerializer serializer = new XmlSerializer(typeof(LandData));
+
private Vector3 _AABBMax = new Vector3();
private Vector3 _AABBMin = new Vector3();
private int _area = 0;
@@ -65,11 +65,11 @@ namespace OpenSim.Framework
private byte[] _bitmap = new byte[512];
private string _description = String.Empty;
- private uint _flags = (uint) ParcelFlags.AllowFly | (uint) ParcelFlags.AllowLandmark |
- (uint) ParcelFlags.AllowAPrimitiveEntry |
- (uint) ParcelFlags.AllowDeedToGroup |
- (uint) ParcelFlags.CreateObjects | (uint) ParcelFlags.AllowOtherScripts |
- (uint) ParcelFlags.SoundLocal | (uint) ParcelFlags.AllowVoiceChat;
+ private uint _flags = (uint)ParcelFlags.AllowFly | (uint)ParcelFlags.AllowLandmark |
+ (uint)ParcelFlags.AllowAPrimitiveEntry |
+ (uint)ParcelFlags.AllowDeedToGroup |
+ (uint)ParcelFlags.CreateObjects | (uint)ParcelFlags.AllowOtherScripts |
+ (uint)ParcelFlags.AllowVoiceChat;
private byte _landingType = 0;
private string _name = "Your Parcel";
@@ -97,16 +97,36 @@ namespace OpenSim.Framework
private bool _mediaLoop = false;
private bool _obscureMusic = false;
private bool _obscureMedia = false;
+ private float _dwell = 0;
+
+ ///
+ /// Traffic count of parcel
+ ///
+ [XmlIgnore]
+ public float Dwell
+ {
+ get
+ {
+ return _dwell;
+ }
+ set
+ {
+ _dwell = value;
+ }
+ }
///
/// Whether to obscure parcel media URL
///
[XmlIgnore]
- public bool ObscureMedia {
- get {
+ public bool ObscureMedia
+ {
+ get
+ {
return _obscureMedia;
}
- set {
+ set
+ {
_obscureMedia = value;
}
}
@@ -115,11 +135,14 @@ namespace OpenSim.Framework
/// Whether to obscure parcel music URL
///
[XmlIgnore]
- public bool ObscureMusic {
- get {
+ public bool ObscureMusic
+ {
+ get
+ {
return _obscureMusic;
}
- set {
+ set
+ {
_obscureMusic = value;
}
}
@@ -128,11 +151,14 @@ namespace OpenSim.Framework
/// Whether to loop parcel media
///
[XmlIgnore]
- public bool MediaLoop {
- get {
+ public bool MediaLoop
+ {
+ get
+ {
return _mediaLoop;
}
- set {
+ set
+ {
_mediaLoop = value;
}
}
@@ -141,11 +167,14 @@ namespace OpenSim.Framework
/// Height of parcel media render
///
[XmlIgnore]
- public int MediaHeight {
- get {
+ public int MediaHeight
+ {
+ get
+ {
return _mediaHeight;
}
- set {
+ set
+ {
_mediaHeight = value;
}
}
@@ -154,11 +183,14 @@ namespace OpenSim.Framework
/// Width of parcel media render
///
[XmlIgnore]
- public int MediaWidth {
- get {
+ public int MediaWidth
+ {
+ get
+ {
return _mediaWidth;
}
- set {
+ set
+ {
_mediaWidth = value;
}
}
@@ -167,11 +199,14 @@ namespace OpenSim.Framework
/// Upper corner of the AABB for the parcel
///
[XmlIgnore]
- public Vector3 AABBMax {
- get {
+ public Vector3 AABBMax
+ {
+ get
+ {
return _AABBMax;
}
- set {
+ set
+ {
_AABBMax = value;
}
}
@@ -179,11 +214,14 @@ namespace OpenSim.Framework
/// Lower corner of the AABB for the parcel
///
[XmlIgnore]
- public Vector3 AABBMin {
- get {
+ public Vector3 AABBMin
+ {
+ get
+ {
return _AABBMin;
}
- set {
+ set
+ {
_AABBMin = value;
}
}
@@ -191,11 +229,14 @@ namespace OpenSim.Framework
///
/// Area in meters^2 the parcel contains
///
- public int Area {
- get {
+ public int Area
+ {
+ get
+ {
return _area;
}
- set {
+ set
+ {
_area = value;
}
}
@@ -203,11 +244,14 @@ namespace OpenSim.Framework
///
/// ID of auction (3rd Party Integration) when parcel is being auctioned
///
- public uint AuctionID {
- get {
+ public uint AuctionID
+ {
+ get
+ {
return _auctionID;
}
- set {
+ set
+ {
_auctionID = value;
}
}
@@ -215,11 +259,14 @@ namespace OpenSim.Framework
///
/// UUID of authorized buyer of parcel. This is UUID.Zero if anyone can buy it.
///
- public UUID AuthBuyerID {
- get {
+ public UUID AuthBuyerID
+ {
+ get
+ {
return _authBuyerID;
}
- set {
+ set
+ {
_authBuyerID = value;
}
}
@@ -227,11 +274,14 @@ namespace OpenSim.Framework
///
/// Category of parcel. Used for classifying the parcel in classified listings
///
- public ParcelCategory Category {
- get {
+ public ParcelCategory Category
+ {
+ get
+ {
return _category;
}
- set {
+ set
+ {
_category = value;
}
}
@@ -239,11 +289,14 @@ namespace OpenSim.Framework
///
/// Date that the current owner purchased or claimed the parcel
///
- public int ClaimDate {
- get {
+ public int ClaimDate
+ {
+ get
+ {
return _claimDate;
}
- set {
+ set
+ {
_claimDate = value;
}
}
@@ -251,11 +304,14 @@ namespace OpenSim.Framework
///
/// The last price that the parcel was sold at
///
- public int ClaimPrice {
- get {
+ public int ClaimPrice
+ {
+ get
+ {
return _claimPrice;
}
- set {
+ set
+ {
_claimPrice = value;
}
}
@@ -263,11 +319,14 @@ namespace OpenSim.Framework
///
/// Global ID for the parcel. (3rd Party Integration)
///
- public UUID GlobalID {
- get {
+ public UUID GlobalID
+ {
+ get
+ {
return _globalID;
}
- set {
+ set
+ {
_globalID = value;
}
}
@@ -275,11 +334,14 @@ namespace OpenSim.Framework
///
/// Unique ID of the Group that owns
///
- public UUID GroupID {
- get {
+ public UUID GroupID
+ {
+ get
+ {
return _groupID;
}
- set {
+ set
+ {
_groupID = value;
}
}
@@ -287,11 +349,14 @@ namespace OpenSim.Framework
///
/// Returns true if the Land Parcel is owned by a group
///
- public bool IsGroupOwned {
- get {
+ public bool IsGroupOwned
+ {
+ get
+ {
return _isGroupOwned;
}
- set {
+ set
+ {
_isGroupOwned = value;
}
}
@@ -299,11 +364,14 @@ namespace OpenSim.Framework
///
/// jp2 data for the image representative of the parcel in the parcel dialog
///
- public byte[] Bitmap {
- get {
+ public byte[] Bitmap
+ {
+ get
+ {
return _bitmap;
}
- set {
+ set
+ {
_bitmap = value;
}
}
@@ -311,11 +379,14 @@ namespace OpenSim.Framework
///
/// Parcel Description
///
- public string Description {
- get {
+ public string Description
+ {
+ get
+ {
return _description;
}
- set {
+ set
+ {
_description = value;
}
}
@@ -323,11 +394,14 @@ namespace OpenSim.Framework
///
/// Parcel settings. Access flags, Fly, NoPush, Voice, Scripts allowed, etc. ParcelFlags
///
- public uint Flags {
- get {
+ public uint Flags
+ {
+ get
+ {
return _flags;
}
- set {
+ set
+ {
_flags = value;
}
}
@@ -336,11 +410,14 @@ namespace OpenSim.Framework
/// Determines if people are able to teleport where they please on the parcel or if they
/// get constrainted to a specific point on teleport within the parcel
///
- public byte LandingType {
- get {
+ public byte LandingType
+ {
+ get
+ {
return _landingType;
}
- set {
+ set
+ {
_landingType = value;
}
}
@@ -348,11 +425,14 @@ namespace OpenSim.Framework
///
/// Parcel Name
///
- public string Name {
- get {
+ public string Name
+ {
+ get
+ {
return _name;
}
- set {
+ set
+ {
_name = value;
}
}
@@ -360,11 +440,14 @@ namespace OpenSim.Framework
///
/// Status of Parcel, Leased, Abandoned, For Sale
///
- public ParcelStatus Status {
- get {
+ public ParcelStatus Status
+ {
+ get
+ {
return _status;
}
- set {
+ set
+ {
_status = value;
}
}
@@ -372,11 +455,14 @@ namespace OpenSim.Framework
///
/// Internal ID of the parcel. Sometimes the client will try to use this value
///
- public int LocalID {
- get {
+ public int LocalID
+ {
+ get
+ {
return _localID;
}
- set {
+ set
+ {
_localID = value;
}
}
@@ -384,11 +470,14 @@ namespace OpenSim.Framework
///
/// Determines if we scale the media based on the surface it's on
///
- public byte MediaAutoScale {
- get {
+ public byte MediaAutoScale
+ {
+ get
+ {
return _mediaAutoScale;
}
- set {
+ set
+ {
_mediaAutoScale = value;
}
}
@@ -396,11 +485,14 @@ namespace OpenSim.Framework
///
/// Texture Guid to replace with the output of the media stream
///
- public UUID MediaID {
- get {
+ public UUID MediaID
+ {
+ get
+ {
return _mediaID;
}
- set {
+ set
+ {
_mediaID = value;
}
}
@@ -408,11 +500,14 @@ namespace OpenSim.Framework
///
/// URL to the media file to display
///
- public string MediaURL {
- get {
+ public string MediaURL
+ {
+ get
+ {
return _mediaURL;
}
- set {
+ set
+ {
_mediaURL = value;
}
}
@@ -432,11 +527,14 @@ namespace OpenSim.Framework
///
/// URL to the shoutcast music stream to play on the parcel
///
- public string MusicURL {
- get {
+ public string MusicURL
+ {
+ get
+ {
return _musicURL;
}
- set {
+ set
+ {
_musicURL = value;
}
}
@@ -445,11 +543,14 @@ namespace OpenSim.Framework
/// Owner Avatar or Group of the parcel. Naturally, all land masses must be
/// owned by someone
///
- public UUID OwnerID {
- get {
+ public UUID OwnerID
+ {
+ get
+ {
return _ownerID;
}
- set {
+ set
+ {
_ownerID = value;
}
}
@@ -457,11 +558,14 @@ namespace OpenSim.Framework
///
/// List of access data for the parcel. User data, some bitflags, and a time
///
- public List ParcelAccessList {
- get {
+ public List ParcelAccessList
+ {
+ get
+ {
return _parcelAccessList;
}
- set {
+ set
+ {
_parcelAccessList = value;
}
}
@@ -469,11 +573,14 @@ namespace OpenSim.Framework
///
/// How long in hours a Pass to the parcel is given
///
- public float PassHours {
- get {
+ public float PassHours
+ {
+ get
+ {
return _passHours;
}
- set {
+ set
+ {
_passHours = value;
}
}
@@ -481,11 +588,14 @@ namespace OpenSim.Framework
///
/// Price to purchase a Pass to a restricted parcel
///
- public int PassPrice {
- get {
+ public int PassPrice
+ {
+ get
+ {
return _passPrice;
}
- set {
+ set
+ {
_passPrice = value;
}
}
@@ -493,11 +603,14 @@ namespace OpenSim.Framework
///
/// When the parcel is being sold, this is the price to purchase the parcel
///
- public int SalePrice {
- get {
+ public int SalePrice
+ {
+ get
+ {
return _salePrice;
}
- set {
+ set
+ {
_salePrice = value;
}
}
@@ -506,11 +619,14 @@ namespace OpenSim.Framework
/// Number of meters^2 in the Simulator
///
[XmlIgnore]
- public int SimwideArea {
- get {
+ public int SimwideArea
+ {
+ get
+ {
return _simwideArea;
}
- set {
+ set
+ {
_simwideArea = value;
}
}
@@ -519,11 +635,14 @@ namespace OpenSim.Framework
/// Number of SceneObjectPart in the Simulator
///
[XmlIgnore]
- public int SimwidePrims {
- get {
+ public int SimwidePrims
+ {
+ get
+ {
return _simwidePrims;
}
- set {
+ set
+ {
_simwidePrims = value;
}
}
@@ -531,11 +650,14 @@ namespace OpenSim.Framework
///
/// ID of the snapshot used in the client parcel dialog of the parcel
///
- public UUID SnapshotID {
- get {
+ public UUID SnapshotID
+ {
+ get
+ {
return _snapshotID;
}
- set {
+ set
+ {
_snapshotID = value;
}
}
@@ -544,11 +666,14 @@ namespace OpenSim.Framework
/// When teleporting is restricted to a certain point, this is the location
/// that the user will be redirected to
///
- public Vector3 UserLocation {
- get {
+ public Vector3 UserLocation
+ {
+ get
+ {
return _userLocation;
}
- set {
+ set
+ {
_userLocation = value;
}
}
@@ -557,11 +682,14 @@ namespace OpenSim.Framework
/// When teleporting is restricted to a certain point, this is the rotation
/// that the user will be positioned
///
- public Vector3 UserLookAt {
- get {
+ public Vector3 UserLookAt
+ {
+ get
+ {
return _userLookAt;
}
- set {
+ set
+ {
_userLookAt = value;
}
}
@@ -570,11 +698,14 @@ namespace OpenSim.Framework
/// Autoreturn number of minutes to return SceneObjectGroup that are owned by someone who doesn't own
/// the parcel and isn't set to the same 'group' as the parcel.
///
- public int OtherCleanTime {
- get {
+ public int OtherCleanTime
+ {
+ get
+ {
return _otherCleanTime;
}
- set {
+ set
+ {
_otherCleanTime = value;
}
}
@@ -582,11 +713,14 @@ namespace OpenSim.Framework
///
/// parcel media description
///
- public string MediaDescription {
- get {
+ public string MediaDescription
+ {
+ get
+ {
return _mediaDescription;
}
- set {
+ set
+ {
_mediaDescription = value;
}
}
@@ -622,7 +756,7 @@ namespace OpenSim.Framework
landData._mediaURL = _mediaURL;
landData._musicURL = _musicURL;
landData._ownerID = _ownerID;
- landData._bitmap = (byte[]) _bitmap.Clone();
+ landData._bitmap = (byte[])_bitmap.Clone();
landData._description = _description;
landData._flags = _flags;
landData._name = _name;
@@ -643,6 +777,7 @@ namespace OpenSim.Framework
landData._obscureMedia = _obscureMedia;
landData._simwideArea = _simwideArea;
landData._simwidePrims = _simwidePrims;
+ landData._dwell = _dwell;
landData._parcelAccessList.Clear();
foreach (LandAccessEntry entry in _parcelAccessList)
diff --git a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
index 9ee087694b..446e3c0697 100644
--- a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
+++ b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
@@ -43,27 +43,32 @@ namespace OpenSim.Framework.Monitoring
StringBuilder sb = new StringBuilder(Environment.NewLine);
sb.Append("MEMORY STATISTICS");
sb.Append(Environment.NewLine);
- sb.Append(
- string.Format(
+ sb.AppendFormat(
"Allocated to OpenSim objects: {0} MB\n",
- Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0)));
+ Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0));
+
+ sb.AppendFormat(
+ "OpenSim last object memory churn : {0} MB/s\n",
+ Math.Round((MemoryWatchdog.LastMemoryChurn * 1000) / 1024.0 / 1024, 3));
+
+ sb.AppendFormat(
+ "OpenSim average object memory churn : {0} MB/s\n",
+ Math.Round((MemoryWatchdog.AverageMemoryChurn * 1000) / 1024.0 / 1024, 3));
Process myprocess = Process.GetCurrentProcess();
if (!myprocess.HasExited)
{
myprocess.Refresh();
- sb.Append(
- string.Format(
+ sb.AppendFormat(
"Process memory: Physical {0} MB \t Paged {1} MB \t Virtual {2} MB\n",
Math.Round(Process.GetCurrentProcess().WorkingSet64 / 1024.0 / 1024.0),
Math.Round(Process.GetCurrentProcess().PagedMemorySize64 / 1024.0 / 1024.0),
- Math.Round(Process.GetCurrentProcess().VirtualMemorySize64 / 1024.0 / 1024.0)));
- sb.Append(
- string.Format(
+ Math.Round(Process.GetCurrentProcess().VirtualMemorySize64 / 1024.0 / 1024.0));
+ sb.AppendFormat(
"Peak process memory: Physical {0} MB \t Paged {1} MB \t Virtual {2} MB\n",
Math.Round(Process.GetCurrentProcess().PeakWorkingSet64 / 1024.0 / 1024.0),
Math.Round(Process.GetCurrentProcess().PeakPagedMemorySize64 / 1024.0 / 1024.0),
- Math.Round(Process.GetCurrentProcess().PeakVirtualMemorySize64 / 1024.0 / 1024.0)));
+ Math.Round(Process.GetCurrentProcess().PeakVirtualMemorySize64 / 1024.0 / 1024.0));
}
else
sb.Append("Process reported as Exited \n");
diff --git a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs
index a23cf1fea8..c6010cd092 100644
--- a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs
+++ b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs
@@ -60,13 +60,21 @@ namespace OpenSim.Framework.Monitoring
private static bool m_enabled;
///
- /// Average memory churn in bytes per millisecond.
+ /// Last memory churn in bytes per millisecond.
///
public static double AverageMemoryChurn
{
get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; }
}
+ ///
+ /// Average memory churn in bytes per millisecond.
+ ///
+ public static double LastMemoryChurn
+ {
+ get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; }
+ }
+
///
/// Maximum number of statistical samples.
///
diff --git a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
index cdd7cc711e..aa862027b8 100644
--- a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
+++ b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
@@ -355,10 +355,25 @@ Asset service request failures: {3}" + Environment.NewLine,
sb.Append(Environment.NewLine);
sb.Append(
string.Format(
- "{0,6:0} {1,6:0} {2,6:0} {3,6:0} {4,6:0} {5,6:0.0} {6,6:0.0} {7,6:0.0} {8,6:0.0} {9,6:0.0} {10,6:0.0}",
+ "{0,6:0} {1,6:0} {2,6:0} {3,6:0} {4,6:0} {5,6:0.0} {6,6:0.0} {7,6:0.0} {8,6:0.0} {9,6:0.0} {10,6:0.0}\n\n",
inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime,
netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime));
- sb.Append(Environment.NewLine);
+
+ Dictionary> sceneStats;
+
+ if (StatsManager.TryGetStats("scene", out sceneStats))
+ {
+ foreach (KeyValuePair> kvp in sceneStats)
+ {
+ foreach (Stat stat in kvp.Value.Values)
+ {
+ if (stat.Verbosity == StatVerbosity.Info)
+ {
+ sb.AppendFormat("{0} ({1}): {2}{3}\n", stat.Name, stat.Container, stat.Value, stat.UnitName);
+ }
+ }
+ }
+ }
/*
sb.Append(Environment.NewLine);
diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs
index d78fa6a1f0..4844336e31 100644
--- a/OpenSim/Framework/Monitoring/StatsManager.cs
+++ b/OpenSim/Framework/Monitoring/StatsManager.cs
@@ -25,6 +25,9 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+using System;
+using System.Collections.Generic;
+
namespace OpenSim.Framework.Monitoring
{
///
@@ -32,6 +35,24 @@ namespace OpenSim.Framework.Monitoring
///
public class StatsManager
{
+ // Subcommand used to list other stats.
+ public const string AllSubCommand = "all";
+
+ // Subcommand used to list other stats.
+ public const string ListSubCommand = "list";
+
+ // All subcommands
+ public static HashSet SubCommands = new HashSet { AllSubCommand, ListSubCommand };
+
+ ///
+ /// Registered stats categorized by category/container/shortname
+ ///
+ ///
+ /// Do not add or remove directly from this dictionary.
+ ///
+ public static Dictionary>> RegisteredStats
+ = new Dictionary>>();
+
private static AssetStatsCollector assetStats;
private static UserStatsCollector userStats;
private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector();
@@ -40,6 +61,75 @@ namespace OpenSim.Framework.Monitoring
public static UserStatsCollector UserStats { get { return userStats; } }
public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } }
+ public static void RegisterConsoleCommands(ICommandConsole console)
+ {
+ console.Commands.AddCommand(
+ "General",
+ false,
+ "show stats",
+ "show stats [list|all|]",
+ "Show statistical information for this server",
+ "If no final argument is specified then legacy statistics information is currently shown.\n"
+ + "If list is specified then statistic categories are shown.\n"
+ + "If all is specified then all registered statistics are shown.\n"
+ + "If a category name is specified then only statistics from that category are shown.\n"
+ + "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS",
+ HandleShowStatsCommand);
+ }
+
+ public static void HandleShowStatsCommand(string module, string[] cmd)
+ {
+ ICommandConsole con = MainConsole.Instance;
+
+ if (cmd.Length > 2)
+ {
+ var categoryName = cmd[2];
+
+ if (categoryName == AllSubCommand)
+ {
+ foreach (var category in RegisteredStats.Values)
+ {
+ OutputCategoryStatsToConsole(con, category);
+ }
+ }
+ else if (categoryName == ListSubCommand)
+ {
+ con.Output("Statistic categories available are:");
+ foreach (string category in RegisteredStats.Keys)
+ con.OutputFormat(" {0}", category);
+ }
+ else
+ {
+ Dictionary> category;
+ if (!RegisteredStats.TryGetValue(categoryName, out category))
+ {
+ con.OutputFormat("No such category as {0}", categoryName);
+ }
+ else
+ {
+ OutputCategoryStatsToConsole(con, category);
+ }
+ }
+ }
+ else
+ {
+ // Legacy
+ con.Output(SimExtraStats.Report());
+ }
+ }
+
+ private static void OutputCategoryStatsToConsole(
+ ICommandConsole con, Dictionary> category)
+ {
+ foreach (var container in category.Values)
+ {
+ foreach (Stat stat in container.Values)
+ {
+ con.Output(stat.ToConsoleString());
+ }
+ }
+ }
+
///
/// Start collecting statistics related to assets.
/// Should only be called once.
@@ -61,5 +151,275 @@ namespace OpenSim.Framework.Monitoring
return userStats;
}
+
+ ///
+ /// Registers a statistic.
+ ///
+ ///
+ ///
+ public static bool RegisterStat(Stat stat)
+ {
+ Dictionary> category = null, newCategory;
+ Dictionary container = null, newContainer;
+
+ lock (RegisteredStats)
+ {
+ // Stat name is not unique across category/container/shortname key.
+ // XXX: For now just return false. This is to avoid problems in regression tests where all tests
+ // in a class are run in the same instance of the VM.
+ if (TryGetStat(stat, out category, out container))
+ return false;
+
+ // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed.
+ // This means that we don't need to lock or copy them on iteration, which will be a much more
+ // common operation after startup.
+ if (container != null)
+ newContainer = new Dictionary(container);
+ else
+ newContainer = new Dictionary();
+
+ if (category != null)
+ newCategory = new Dictionary>(category);
+ else
+ newCategory = new Dictionary>();
+
+ newContainer[stat.ShortName] = stat;
+ newCategory[stat.Container] = newContainer;
+ RegisteredStats[stat.Category] = newCategory;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Deregister a statistic
+ /// >
+ ///
+ /// > category = null, newCategory;
+ Dictionary container = null, newContainer;
+
+ lock (RegisteredStats)
+ {
+ if (!TryGetStat(stat, out category, out container))
+ return false;
+
+ newContainer = new Dictionary(container);
+ newContainer.Remove(stat.ShortName);
+
+ newCategory = new Dictionary>(category);
+ newCategory.Remove(stat.Container);
+
+ newCategory[stat.Container] = newContainer;
+ RegisteredStats[stat.Category] = newCategory;
+
+ return true;
+ }
+ }
+
+ public static bool TryGetStats(string category, out Dictionary> stats)
+ {
+ return RegisteredStats.TryGetValue(category, out stats);
+ }
+
+ public static bool TryGetStat(
+ Stat stat,
+ out Dictionary> category,
+ out Dictionary container)
+ {
+ category = null;
+ container = null;
+
+ lock (RegisteredStats)
+ {
+ if (RegisteredStats.TryGetValue(stat.Category, out category))
+ {
+ if (category.TryGetValue(stat.Container, out container))
+ {
+ if (container.ContainsKey(stat.ShortName))
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ }
+
+ ///
+ /// Stat type.
+ ///
+ ///
+ /// A push stat is one which is continually updated and so it's value can simply by read.
+ /// A pull stat is one where reading the value triggers a collection method - the stat is not continually updated.
+ ///
+ public enum StatType
+ {
+ Push,
+ Pull
+ }
+
+ ///
+ /// Verbosity of stat.
+ ///
+ ///
+ /// Info will always be displayed.
+ ///
+ public enum StatVerbosity
+ {
+ Debug,
+ Info
+ }
+
+ ///
+ /// Holds individual static details
+ ///
+ public class Stat
+ {
+ ///
+ /// Category of this stat (e.g. cache, scene, etc).
+ ///
+ public string Category { get; private set; }
+
+ ///
+ /// Containing name for this stat.
+ /// FIXME: In the case of a scene, this is currently the scene name (though this leaves
+ /// us with a to-be-resolved problem of non-unique region names).
+ ///
+ ///
+ /// The container.
+ ///
+ public string Container { get; private set; }
+
+ public StatType StatType { get; private set; }
+
+ ///
+ /// Action used to update this stat when the value is requested if it's a pull type.
+ ///
+ public Action PullAction { get; private set; }
+
+ public StatVerbosity Verbosity { get; private set; }
+ public string ShortName { get; private set; }
+ public string Name { get; private set; }
+ public string Description { get; private set; }
+ public virtual string UnitName { get; private set; }
+
+ public virtual double Value
+ {
+ get
+ {
+ // Asking for an update here means that the updater cannot access this value without infinite recursion.
+ // XXX: A slightly messy but simple solution may be to flick a flag so we can tell if this is being
+ // called by the pull action and just return the value.
+ if (StatType == StatType.Pull)
+ PullAction(this);
+
+ return m_value;
+ }
+
+ set
+ {
+ m_value = value;
+ }
+ }
+
+ private double m_value;
+
+ ///
+ /// Constructor
+ ///
+ /// Short name for the stat. Must not contain spaces. e.g. "LongFrames"
+ /// Human readable name for the stat. e.g. "Long frames"
+ /// Description of stat
+ ///
+ /// Unit name for the stat. Should be preceeded by a space if the unit name isn't normally appeneded immediately to the value.
+ /// e.g. " frames"
+ ///
+ /// Category under which this stat should appear, e.g. "scene". Do not capitalize.
+ /// Entity to which this stat relates. e.g. scene name if this is a per scene stat.
+ /// Push or pull
+ /// Pull stats need an action to update the stat on request. Push stats should set null here.
+ /// Verbosity of stat. Controls whether it will appear in short stat display or only full display.
+ public Stat(
+ string shortName,
+ string name,
+ string description,
+ string unitName,
+ string category,
+ string container,
+ StatType type,
+ Action pullAction,
+ StatVerbosity verbosity)
+ {
+ if (StatsManager.SubCommands.Contains(category))
+ throw new Exception(
+ string.Format("Stat cannot be in category '{0}' since this is reserved for a subcommand", category));
+
+ ShortName = shortName;
+ Name = name;
+ Description = description;
+ UnitName = unitName;
+ Category = category;
+ Container = container;
+ StatType = type;
+
+ if (StatType == StatType.Push && pullAction != null)
+ throw new Exception("A push stat cannot have a pull action");
+ else
+ PullAction = pullAction;
+
+ Verbosity = verbosity;
+ }
+
+ public virtual string ToConsoleString()
+ {
+ return string.Format(
+ "{0}.{1}.{2} : {3}{4}", Category, Container, ShortName, Value, UnitName);
+ }
+ }
+
+ public class PercentageStat : Stat
+ {
+ public int Antecedent { get; set; }
+ public int Consequent { get; set; }
+
+ public override double Value
+ {
+ get
+ {
+ int c = Consequent;
+
+ // Avoid any chance of a multi-threaded divide-by-zero
+ if (c == 0)
+ return 0;
+
+ return (double)Antecedent / c * 100;
+ }
+
+ set
+ {
+ throw new Exception("Cannot set value on a PercentageStat");
+ }
+ }
+
+ public PercentageStat(
+ string shortName,
+ string name,
+ string description,
+ string category,
+ string container,
+ StatType type,
+ Action pullAction,
+ StatVerbosity verbosity)
+ : base(shortName, name, description, "%", category, container, type, pullAction, verbosity) {}
+
+ public override string ToConsoleString()
+ {
+ return string.Format(
+ "{0}.{1}.{2} : {3:0.##}{4} ({5}/{6})",
+ Category, Container, ShortName, Value, UnitName, Antecedent, Consequent);
+ }
}
}
\ No newline at end of file
diff --git a/OpenSim/Framework/Monitoring/Watchdog.cs b/OpenSim/Framework/Monitoring/Watchdog.cs
index b709baa3c7..28d6d5cce5 100644
--- a/OpenSim/Framework/Monitoring/Watchdog.cs
+++ b/OpenSim/Framework/Monitoring/Watchdog.cs
@@ -89,6 +89,17 @@ namespace OpenSim.Framework.Monitoring
FirstTick = Environment.TickCount & Int32.MaxValue;
LastTick = FirstTick;
}
+
+ public ThreadWatchdogInfo(ThreadWatchdogInfo previousTwi)
+ {
+ Thread = previousTwi.Thread;
+ FirstTick = previousTwi.FirstTick;
+ LastTick = previousTwi.LastTick;
+ Timeout = previousTwi.Timeout;
+ IsTimedOut = previousTwi.IsTimedOut;
+ AlarmIfTimeout = previousTwi.AlarmIfTimeout;
+ AlarmMethod = previousTwi.AlarmMethod;
+ }
}
///
@@ -220,7 +231,25 @@ namespace OpenSim.Framework.Monitoring
private static bool RemoveThread(int threadID)
{
lock (m_threads)
- return m_threads.Remove(threadID);
+ {
+ ThreadWatchdogInfo twi;
+ if (m_threads.TryGetValue(threadID, out twi))
+ {
+ m_log.DebugFormat(
+ "[WATCHDOG]: Removing thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId);
+
+ m_threads.Remove(threadID);
+
+ return true;
+ }
+ else
+ {
+ m_log.WarnFormat(
+ "[WATCHDOG]: Requested to remove thread with ID {0} but this is not being monitored", threadID);
+
+ return false;
+ }
+ }
}
public static bool AbortThread(int threadID)
@@ -335,7 +364,9 @@ namespace OpenSim.Framework.Monitoring
if (callbackInfos == null)
callbackInfos = new List();
- callbackInfos.Add(threadInfo);
+ // Send a copy of the watchdog info to prevent race conditions where the watchdog
+ // thread updates the monitoring info after an alarm has been sent out.
+ callbackInfos.Add(new ThreadWatchdogInfo(threadInfo));
}
}
}
diff --git a/OpenSim/Framework/Pool.cs b/OpenSim/Framework/Pool.cs
new file mode 100644
index 0000000000..5484f5c2d5
--- /dev/null
+++ b/OpenSim/Framework/Pool.cs
@@ -0,0 +1,91 @@
+/*
+ * 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;
+
+namespace OpenSim.Framework
+{
+ ///
+ /// Naive pool implementation.
+ ///
+ ///
+ /// Currently assumes that objects are in a useable state when returned.
+ ///
+ public class Pool
+ {
+ ///
+ /// Number of objects in the pool.
+ ///
+ public int Count
+ {
+ get
+ {
+ lock (m_pool)
+ return m_pool.Count;
+ }
+ }
+
+ private Stack m_pool;
+
+ ///
+ /// Maximum pool size. Beyond this, any returned objects are not pooled.
+ ///
+ private int m_maxPoolSize;
+
+ private Func m_createFunction;
+
+ public Pool(Func createFunction, int maxSize)
+ {
+ m_maxPoolSize = maxSize;
+ m_createFunction = createFunction;
+ m_pool = new Stack(m_maxPoolSize);
+ }
+
+ public T GetObject()
+ {
+ lock (m_pool)
+ {
+ if (m_pool.Count > 0)
+ return m_pool.Pop();
+ else
+ return m_createFunction();
+ }
+ }
+
+ public void ReturnObject(T obj)
+ {
+ lock (m_pool)
+ {
+ if (m_pool.Count >= m_maxPoolSize)
+ return;
+ else
+ m_pool.Push(obj);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenSim/Framework/RegionFlags.cs b/OpenSim/Framework/RegionFlags.cs
new file mode 100644
index 0000000000..a3089b077d
--- /dev/null
+++ b/OpenSim/Framework/RegionFlags.cs
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+namespace OpenSim.Framework
+{
+ ///
+ /// Region flags used internally by OpenSimulator to store installation specific information about regions.
+ ///
+ ///
+ /// Don't confuse with OpenMetaverse.RegionFlags which are client facing flags (i.e. they go over the wire).
+ /// Returned by IGridService.GetRegionFlags()
+ ///
+ [Flags]
+ public enum RegionFlags : int
+ {
+ DefaultRegion = 1, // Used for new Rez. Random if multiple defined
+ FallbackRegion = 2, // Regions we redirect to when the destination is down
+ RegionOnline = 4, // Set when a region comes online, unset when it unregisters and DeleteOnUnregister is false
+ NoDirectLogin = 8, // Region unavailable for direct logins (by name)
+ Persistent = 16, // Don't remove on unregister
+ LockedOut = 32, // Don't allow registration
+ NoMove = 64, // Don't allow moving this region
+ Reservation = 128, // This is an inactive reservation
+ Authenticate = 256, // Require authentication
+ Hyperlink = 512 // Record represents a HG link
+ }
+}
\ No newline at end of file
diff --git a/OpenSim/Framework/RegionInfo.cs b/OpenSim/Framework/RegionInfo.cs
index 4bde7be2e1..e7bed6aeb0 100644
--- a/OpenSim/Framework/RegionInfo.cs
+++ b/OpenSim/Framework/RegionInfo.cs
@@ -122,10 +122,13 @@ namespace OpenSim.Framework
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_linksetCapacity = 0;
private int m_agentCapacity = 0;
private string m_regionType = String.Empty;
private RegionLightShareData m_windlight = new RegionLightShareData();
@@ -287,11 +290,21 @@ namespace OpenSim.Framework
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; }
@@ -307,6 +320,11 @@ namespace OpenSim.Framework
get { return m_objectCapacity; }
}
+ public int LinksetCapacity
+ {
+ get { return m_linksetCapacity; }
+ }
+
public int AgentCapacity
{
get { return m_agentCapacity; }
@@ -625,16 +643,31 @@ namespace OpenSim.Framework
m_regionType = config.GetString("RegionType", String.Empty);
allKeys.Remove("RegionType");
- // Prim stuff
- //
- m_nonphysPrimMax = config.GetInt("NonphysicalPrimMax", 0);
- allKeys.Remove("NonphysicalPrimMax");
+ #region Prim 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_linksetCapacity = config.GetInt("LinksetPrims", 0);
+ allKeys.Remove("LinksetPrims");
+
+ #endregion
+
m_agentCapacity = config.GetInt("MaxAgents", 100);
allKeys.Remove("MaxAgents");
@@ -673,16 +706,27 @@ namespace OpenSim.Framework
config.Set("ExternalHostName", m_externalHostName);
- if (m_nonphysPrimMax != 0)
+ if (m_nonphysPrimMin > 0)
+ config.Set("NonphysicalPrimMax", m_nonphysPrimMin);
+
+ if (m_nonphysPrimMax > 0)
config.Set("NonphysicalPrimMax", m_nonphysPrimMax);
- if (m_physPrimMax != 0)
+
+ 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)
+ if (m_objectCapacity > 0)
config.Set("MaxPrims", m_objectCapacity);
- if (m_agentCapacity != 0)
+ if (m_linksetCapacity > 0)
+ config.Set("LinksetPrims", m_linksetCapacity);
+
+ if (m_agentCapacity > 0)
config.Set("MaxAgents", m_agentCapacity);
if (ScopeID != UUID.Zero)
@@ -759,9 +803,15 @@ namespace OpenSim.Framework
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);
@@ -771,6 +821,9 @@ namespace OpenSim.Framework
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", m_agentCapacity.ToString(), true);
@@ -892,6 +945,9 @@ namespace OpenSim.Framework
case "object_capacity":
m_objectCapacity = (int)configuration_result;
break;
+ case "linkset_capacity":
+ m_linksetCapacity = (int)configuration_result;
+ break;
case "agent_capacity":
m_agentCapacity = (int)configuration_result;
break;
diff --git a/OpenSim/Framework/Serialization/ArchiveConstants.cs b/OpenSim/Framework/Serialization/ArchiveConstants.cs
index 2c5e0018ec..48f1c4f94f 100644
--- a/OpenSim/Framework/Serialization/ArchiveConstants.cs
+++ b/OpenSim/Framework/Serialization/ArchiveConstants.cs
@@ -52,6 +52,11 @@ namespace OpenSim.Framework.Serialization
///
public const string INVENTORY_PATH = "inventory/";
+ ///
+ /// Path for regions in a multi-region archive
+ ///
+ public const string REGIONS_PATH = "regions/";
+
///
/// Path for the prims file
///
diff --git a/OpenSim/Framework/Serialization/External/OspResolver.cs b/OpenSim/Framework/Serialization/External/OspResolver.cs
index d31d27c4a6..fa7160f874 100644
--- a/OpenSim/Framework/Serialization/External/OspResolver.cs
+++ b/OpenSim/Framework/Serialization/External/OspResolver.cs
@@ -65,9 +65,14 @@ namespace OpenSim.Framework.Serialization
UserAccount account = userService.GetUserAccount(UUID.Zero, userId);
if (account != null)
+ {
return MakeOspa(account.FirstName, account.LastName);
+ }
// else
+// {
// m_log.WarnFormat("[OSP RESOLVER]: No user account for {0}", userId);
+// System.Console.WriteLine("[OSP RESOLVER]: No user account for {0}", userId);
+// }
return null;
}
@@ -79,10 +84,13 @@ namespace OpenSim.Framework.Serialization
///
public static string MakeOspa(string firstName, string lastName)
{
-// m_log.DebugFormat("[OSP RESOLVER]: Making OSPA for {0} {1}", firstName, lastName);
+ string ospa
+ = OSPA_PREFIX + OSPA_NAME_KEY + OSPA_PAIR_SEPARATOR + firstName + OSPA_NAME_VALUE_SEPARATOR + lastName;
+
+// m_log.DebugFormat("[OSP RESOLVER]: Made OSPA {0} for {1} {2}", ospa, firstName, lastName);
+// System.Console.WriteLine("[OSP RESOLVER]: Made OSPA {0} for {1} {2}", ospa, firstName, lastName);
- return
- OSPA_PREFIX + OSPA_NAME_KEY + OSPA_PAIR_SEPARATOR + firstName + OSPA_NAME_VALUE_SEPARATOR + lastName;
+ return ospa;
}
///
diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
index cf19002baf..605909dcc6 100644
--- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs
+++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
@@ -96,11 +96,6 @@ namespace OpenSim.Framework.Servers
get { return m_httpServer; }
}
- ///
- /// Holds the non-viewer statistics collection object for this service/server
- ///
- protected IStatsCollector m_stats;
-
public BaseOpenSimServer()
{
m_startuptime = DateTime.Now;
@@ -177,10 +172,6 @@ namespace OpenSim.Framework.Servers
"show info",
"Show general information about the server", HandleShow);
- m_console.Commands.AddCommand("General", false, "show stats",
- "show stats",
- "Show statistics", HandleShow);
-
m_console.Commands.AddCommand("General", false, "show threads",
"show threads",
"Show thread status", HandleShow);
@@ -201,8 +192,19 @@ namespace OpenSim.Framework.Servers
"threads show",
"Show thread status. Synonym for \"show threads\"",
(string module, string[] args) => Notice(GetThreadsReport()));
+
+ m_console.Commands.AddCommand("General", false, "force gc",
+ "force gc",
+ "Manually invoke runtime garbage collection. For debugging purposes",
+ HandleForceGc);
}
}
+
+ private void HandleForceGc(string module, string[] args)
+ {
+ MainConsole.Instance.Output("Manually invoking runtime garbage collection");
+ GC.Collect();
+ }
///
/// Should be overriden and referenced by descendents if they need to perform extra shutdown processing
@@ -226,12 +228,7 @@ namespace OpenSim.Framework.Servers
{
StringBuilder sb = new StringBuilder("DIAGNOSTICS\n\n");
sb.Append(GetUptimeReport());
-
- if (m_stats != null)
- {
- sb.Append(m_stats.Report());
- }
-
+ sb.Append(StatsManager.SimExtraStats.Report());
sb.Append(Environment.NewLine);
sb.Append(GetThreadsReport());
@@ -382,10 +379,6 @@ namespace OpenSim.Framework.Servers
{
Notice("set log level [level] - change the console logging level only. For example, off or debug.");
Notice("show info - show server information (e.g. startup path).");
-
- if (m_stats != null)
- Notice("show stats - show statistical information for this server");
-
Notice("show threads - list tracked threads");
Notice("show uptime - show server startup time and uptime.");
Notice("show version - show server version.");
@@ -409,11 +402,6 @@ namespace OpenSim.Framework.Servers
ShowInfo();
break;
- case "stats":
- if (m_stats != null)
- Notice(m_stats.Report());
- break;
-
case "threads":
Notice(GetThreadsReport());
break;
@@ -604,8 +592,7 @@ namespace OpenSim.Framework.Servers
public string osSecret {
// Secret uuid for the simulator
- get { return m_osSecret; }
-
+ get { return m_osSecret; }
}
public string StatReport(IOSHttpRequest httpRequest)
@@ -613,11 +600,11 @@ namespace OpenSim.Framework.Servers
// If we catch a request for "callback", wrap the response in the value for jsonp
if (httpRequest.Query.ContainsKey("callback"))
{
- return httpRequest.Query["callback"].ToString() + "(" + m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version) + ");";
+ return httpRequest.Query["callback"].ToString() + "(" + StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version) + ");";
}
else
{
- return m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version);
+ return StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version);
}
}
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
index 788a0b95ee..3198891a30 100644
--- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
+++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
@@ -54,8 +54,23 @@ namespace OpenSim.Framework.Servers.HttpServer
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private HttpServerLogWriter httpserverlog = new HttpServerLogWriter();
+ ///
+ /// Gets or sets the debug level.
+ ///
+ ///
+ /// See MainServer.DebugLevel.
+ ///
public int DebugLevel { get; set; }
+ ///
+ /// Request number for diagnostic purposes.
+ ///
+ ///
+ /// This is an internal number. In some debug situations an external number may also be supplied in the
+ /// opensim-request-id header but we are not currently logging this.
+ ///
+ public int RequestNumber { get; private set; }
+
private volatile int NotSocketErrors = 0;
public volatile bool HTTPDRunning = false;
@@ -67,7 +82,7 @@ namespace OpenSim.Framework.Servers.HttpServer
protected Dictionary m_llsdHandlers = new Dictionary();
protected Dictionary m_streamHandlers = new Dictionary();
protected Dictionary m_HTTPHandlers = new Dictionary();
- protected Dictionary m_agentHandlers = new Dictionary();
+// protected Dictionary m_agentHandlers = new Dictionary();
protected Dictionary m_pollHandlers =
new Dictionary();
@@ -245,29 +260,29 @@ namespace OpenSim.Framework.Servers.HttpServer
return new List(m_pollHandlers.Keys);
}
- // Note that the agent string is provided simply to differentiate
- // the handlers - it is NOT required to be an actual agent header
- // value.
- public bool AddAgentHandler(string agent, IHttpAgentHandler handler)
- {
- lock (m_agentHandlers)
- {
- if (!m_agentHandlers.ContainsKey(agent))
- {
- m_agentHandlers.Add(agent, handler);
- return true;
- }
- }
-
- //must already have a handler for that path so return false
- return false;
- }
-
- public List GetAgentHandlerKeys()
- {
- lock (m_agentHandlers)
- return new List(m_agentHandlers.Keys);
- }
+// // Note that the agent string is provided simply to differentiate
+// // the handlers - it is NOT required to be an actual agent header
+// // value.
+// public bool AddAgentHandler(string agent, IHttpAgentHandler handler)
+// {
+// lock (m_agentHandlers)
+// {
+// if (!m_agentHandlers.ContainsKey(agent))
+// {
+// m_agentHandlers.Add(agent, handler);
+// return true;
+// }
+// }
+//
+// //must already have a handler for that path so return false
+// return false;
+// }
+//
+// public List GetAgentHandlerKeys()
+// {
+// lock (m_agentHandlers)
+// return new List(m_agentHandlers.Keys);
+// }
public bool AddLLSDHandler(string path, LLSDMethod handler)
{
@@ -296,6 +311,8 @@ namespace OpenSim.Framework.Servers.HttpServer
private void OnRequest(object source, RequestEventArgs args)
{
+ RequestNumber++;
+
try
{
IHttpClientContext context = (IHttpClientContext)source;
@@ -406,7 +423,6 @@ namespace OpenSim.Framework.Servers.HttpServer
string requestMethod = request.HttpMethod;
string uriString = request.RawUrl;
-// string reqnum = "unknown";
int requestStartTick = Environment.TickCount;
// Will be adjusted later on.
@@ -423,22 +439,22 @@ namespace OpenSim.Framework.Servers.HttpServer
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", true);
- // This is the REST agent interface. We require an agent to properly identify
- // itself. If the REST handler recognizes the prefix it will attempt to
- // satisfy the request. If it is not recognizable, and no damage has occurred
- // the request can be passed through to the other handlers. This is a low
- // probability event; if a request is matched it is normally expected to be
- // handled
- IHttpAgentHandler agentHandler;
-
- if (TryGetAgentHandler(request, response, out agentHandler))
- {
- if (HandleAgentRequest(agentHandler, request, response))
- {
- requestEndTick = Environment.TickCount;
- return;
- }
- }
+// // This is the REST agent interface. We require an agent to properly identify
+// // itself. If the REST handler recognizes the prefix it will attempt to
+// // satisfy the request. If it is not recognizable, and no damage has occurred
+// // the request can be passed through to the other handlers. This is a low
+// // probability event; if a request is matched it is normally expected to be
+// // handled
+// IHttpAgentHandler agentHandler;
+//
+// if (TryGetAgentHandler(request, response, out agentHandler))
+// {
+// if (HandleAgentRequest(agentHandler, request, response))
+// {
+// requestEndTick = Environment.TickCount;
+// return;
+// }
+// }
//response.KeepAlive = true;
response.SendChunked = false;
@@ -450,9 +466,7 @@ namespace OpenSim.Framework.Servers.HttpServer
if (TryGetStreamHandler(handlerKey, out requestHandler))
{
if (DebugLevel >= 3)
- m_log.DebugFormat(
- "[BASE HTTP SERVER]: Found stream handler for {0} {1} {2} {3}",
- request.HttpMethod, request.Url.PathAndQuery, requestHandler.Name, requestHandler.Description);
+ LogIncomingToStreamHandler(request, requestHandler);
response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type.
@@ -529,11 +543,8 @@ namespace OpenSim.Framework.Servers.HttpServer
{
case null:
case "text/html":
-
if (DebugLevel >= 3)
- m_log.DebugFormat(
- "[BASE HTTP SERVER]: Found a {0} content type handler for {1} {2}",
- request.ContentType, request.HttpMethod, request.Url.PathAndQuery);
+ LogIncomingToContentTypeHandler(request);
buffer = HandleHTTPRequest(request, response);
break;
@@ -541,11 +552,8 @@ namespace OpenSim.Framework.Servers.HttpServer
case "application/llsd+xml":
case "application/xml+llsd":
case "application/llsd+json":
-
if (DebugLevel >= 3)
- m_log.DebugFormat(
- "[BASE HTTP SERVER]: Found a {0} content type handler for {1} {2}",
- request.ContentType, request.HttpMethod, request.Url.PathAndQuery);
+ LogIncomingToContentTypeHandler(request);
buffer = HandleLLSDRequests(request, response);
break;
@@ -564,9 +572,7 @@ namespace OpenSim.Framework.Servers.HttpServer
if (DoWeHaveALLSDHandler(request.RawUrl))
{
if (DebugLevel >= 3)
- m_log.DebugFormat(
- "[BASE HTTP SERVER]: Found a {0} content type handler for {1} {2}",
- request.ContentType, request.HttpMethod, request.Url.PathAndQuery);
+ LogIncomingToContentTypeHandler(request);
buffer = HandleLLSDRequests(request, response);
}
@@ -574,18 +580,14 @@ namespace OpenSim.Framework.Servers.HttpServer
else if (DoWeHaveAHTTPHandler(request.RawUrl))
{
if (DebugLevel >= 3)
- m_log.DebugFormat(
- "[BASE HTTP SERVER]: Found a {0} content type handler for {1} {2}",
- request.ContentType, request.HttpMethod, request.Url.PathAndQuery);
+ LogIncomingToContentTypeHandler(request);
buffer = HandleHTTPRequest(request, response);
}
else
{
if (DebugLevel >= 3)
- m_log.DebugFormat(
- "[BASE HTTP SERVER]: Assuming a generic XMLRPC request for {0} {1}",
- request.HttpMethod, request.Url.PathAndQuery);
+ LogIncomingToXmlRpcHandler(request);
// generic login request.
buffer = HandleXmlRpcRequests(request, response);
@@ -629,11 +631,11 @@ namespace OpenSim.Framework.Servers.HttpServer
}
catch (IOException e)
{
- m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.Message), e);
+ m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e);
}
catch (Exception e)
{
- m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.Message), e);
+ m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e);
SendHTML500(response);
}
finally
@@ -644,14 +646,90 @@ namespace OpenSim.Framework.Servers.HttpServer
if (tickdiff > 3000 && (requestHandler == null || requestHandler.Name == null || requestHandler.Name != "GetTexture"))
{
m_log.InfoFormat(
- "[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} from {4} took {5}ms",
+ "[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms",
+ RequestNumber,
requestMethod,
uriString,
requestHandler != null ? requestHandler.Name : "",
requestHandler != null ? requestHandler.Description : "",
- request.RemoteIPEndPoint.ToString(),
+ request.RemoteIPEndPoint,
tickdiff);
}
+ else if (DebugLevel >= 4)
+ {
+ m_log.DebugFormat(
+ "[BASE HTTP SERVER]: HTTP IN {0} :{1} took {2}ms",
+ RequestNumber,
+ Port,
+ tickdiff);
+ }
+ }
+ }
+
+ private void LogIncomingToStreamHandler(OSHttpRequest request, IRequestHandler requestHandler)
+ {
+ m_log.DebugFormat(
+ "[BASE HTTP SERVER]: HTTP IN {0} :{1} stream handler {2} {3} {4} {5} from {6}",
+ RequestNumber,
+ Port,
+ request.HttpMethod,
+ request.Url.PathAndQuery,
+ requestHandler.Name,
+ requestHandler.Description,
+ request.RemoteIPEndPoint);
+
+ if (DebugLevel >= 5)
+ LogIncomingInDetail(request);
+ }
+
+ private void LogIncomingToContentTypeHandler(OSHttpRequest request)
+ {
+ m_log.DebugFormat(
+ "[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}",
+ RequestNumber,
+ Port,
+ (request.ContentType == null || request.ContentType == "") ? "not set" : request.ContentType,
+ request.HttpMethod,
+ request.Url.PathAndQuery,
+ request.RemoteIPEndPoint);
+
+ if (DebugLevel >= 5)
+ LogIncomingInDetail(request);
+ }
+
+ private void LogIncomingToXmlRpcHandler(OSHttpRequest request)
+ {
+ m_log.DebugFormat(
+ "[BASE HTTP SERVER]: HTTP IN {0} :{1} assumed generic XMLRPC request {2} {3} from {4}",
+ RequestNumber,
+ Port,
+ request.HttpMethod,
+ request.Url.PathAndQuery,
+ request.RemoteIPEndPoint);
+
+ if (DebugLevel >= 5)
+ LogIncomingInDetail(request);
+ }
+
+ private void LogIncomingInDetail(OSHttpRequest request)
+ {
+ using (StreamReader reader = new StreamReader(Util.Copy(request.InputStream), Encoding.UTF8))
+ {
+ string output;
+
+ if (DebugLevel == 5)
+ {
+ const int sampleLength = 80;
+ char[] sampleChars = new char[sampleLength];
+ reader.Read(sampleChars, 0, sampleLength);
+ output = new string(sampleChars);
+ }
+ else
+ {
+ output = reader.ReadToEnd();
+ }
+
+ m_log.DebugFormat("[BASE HTTP SERVER]: {0}...", output.Replace("\n", @"\n"));
}
}
@@ -747,24 +825,24 @@ namespace OpenSim.Framework.Servers.HttpServer
}
}
- private bool TryGetAgentHandler(OSHttpRequest request, OSHttpResponse response, out IHttpAgentHandler agentHandler)
- {
- agentHandler = null;
-
- lock (m_agentHandlers)
- {
- foreach (IHttpAgentHandler handler in m_agentHandlers.Values)
- {
- if (handler.Match(request, response))
- {
- agentHandler = handler;
- return true;
- }
- }
- }
-
- return false;
- }
+// private bool TryGetAgentHandler(OSHttpRequest request, OSHttpResponse response, out IHttpAgentHandler agentHandler)
+// {
+// agentHandler = null;
+//
+// lock (m_agentHandlers)
+// {
+// foreach (IHttpAgentHandler handler in m_agentHandlers.Values)
+// {
+// if (handler.Match(request, response))
+// {
+// agentHandler = handler;
+// return true;
+// }
+// }
+// }
+//
+// return false;
+// }
///
/// Try all the registered xmlrpc handlers when an xmlrpc request is received.
@@ -1737,21 +1815,21 @@ namespace OpenSim.Framework.Servers.HttpServer
m_pollHandlers.Remove(path);
}
- public bool RemoveAgentHandler(string agent, IHttpAgentHandler handler)
- {
- lock (m_agentHandlers)
- {
- IHttpAgentHandler foundHandler;
-
- if (m_agentHandlers.TryGetValue(agent, out foundHandler) && foundHandler == handler)
- {
- m_agentHandlers.Remove(agent);
- return true;
- }
- }
-
- return false;
- }
+// public bool RemoveAgentHandler(string agent, IHttpAgentHandler handler)
+// {
+// lock (m_agentHandlers)
+// {
+// IHttpAgentHandler foundHandler;
+//
+// if (m_agentHandlers.TryGetValue(agent, out foundHandler) && foundHandler == handler)
+// {
+// m_agentHandlers.Remove(agent);
+// return true;
+// }
+// }
+//
+// return false;
+// }
public void RemoveXmlRPCHandler(string method)
{
diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs
index db58f6f468..0bd3aae7d5 100644
--- a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs
+++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs
@@ -41,10 +41,10 @@ namespace OpenSim.Framework.Servers.HttpServer
uint Port { get; }
bool UseSSL { get; }
- // Note that the agent string is provided simply to differentiate
- // the handlers - it is NOT required to be an actual agent header
- // value.
- bool AddAgentHandler(string agent, IHttpAgentHandler handler);
+// // Note that the agent string is provided simply to differentiate
+// // the handlers - it is NOT required to be an actual agent header
+// // value.
+// bool AddAgentHandler(string agent, IHttpAgentHandler handler);
///
/// Add a handler for an HTTP request.
@@ -106,13 +106,13 @@ namespace OpenSim.Framework.Servers.HttpServer
bool SetDefaultLLSDHandler(DefaultLLSDMethod handler);
- ///
- /// Remove the agent if it is registered.
- ///
- ///
- ///
- ///
- bool RemoveAgentHandler(string agent, IHttpAgentHandler handler);
+// ///
+// /// Remove the agent if it is registered.
+// ///
+// ///
+// ///
+// ///
+// bool RemoveAgentHandler(string agent, IHttpAgentHandler handler);
///
/// Remove an HTTP handler
diff --git a/OpenSim/Framework/Servers/MainServer.cs b/OpenSim/Framework/Servers/MainServer.cs
index 8dc0e3a71d..ae7d515e13 100644
--- a/OpenSim/Framework/Servers/MainServer.cs
+++ b/OpenSim/Framework/Servers/MainServer.cs
@@ -29,6 +29,7 @@ using System;
using System.Collections.Generic;
using System.Reflection;
using System.Net;
+using System.Text;
using log4net;
using OpenSim.Framework;
using OpenSim.Framework.Console;
@@ -47,9 +48,12 @@ namespace OpenSim.Framework.Servers
/// Control the printing of certain debug messages.
///
///
- /// If DebugLevel >= 1, then short warnings are logged when receiving bad input data.
- /// If DebugLevel >= 2, then long warnings are logged when receiving bad input data.
- /// If DebugLevel >= 3, then short notices about all incoming non-poll HTTP requests are logged.
+ /// If DebugLevel >= 1 then short warnings are logged when receiving bad input data.
+ /// If DebugLevel >= 2 then long warnings are logged when receiving bad input data.
+ /// If DebugLevel >= 3 then short notices about all incoming non-poll HTTP requests are logged.
+ /// If DebugLevel >= 4 then the time taken to fulfill the request is logged.
+ /// If DebugLevel >= 5 then the start of the body of incoming non-poll HTTP requests will be logged.
+ /// If DebugLevel >= 6 then the entire body of incoming non-poll HTTP requests will be logged.
///
public static int DebugLevel
{
@@ -101,17 +105,28 @@ namespace OpenSim.Framework.Servers
get { return new Dictionary(m_Servers); }
}
-
public static void RegisterHttpConsoleCommands(ICommandConsole console)
{
console.Commands.AddCommand(
- "Debug", false, "debug http", "debug http []",
- "Turn on inbound non-poll http request debugging.",
- "If level <= 0, then no extra logging is done.\n"
- + "If level >= 1, then short warnings are logged when receiving bad input data.\n"
- + "If level >= 2, then long warnings are logged when receiving bad input data.\n"
- + "If level >= 3, then short notices about all incoming non-poll HTTP requests are logged.\n"
- + "If no level is specified then the current level is returned.",
+ "Comms", false, "show http-handlers",
+ "show http-handlers",
+ "Show all registered http handlers", HandleShowHttpHandlersCommand);
+
+ console.Commands.AddCommand(
+ "Debug", false, "debug http", "debug http []",
+ "Turn on http request logging.",
+ "If in or all and\n"
+ + " level <= 0 then no extra logging is done.\n"
+ + " level >= 1 then short warnings are logged when receiving bad input data.\n"
+ + " level >= 2 then long warnings are logged when receiving bad input data.\n"
+ + " level >= 3 then short notices about all incoming non-poll HTTP requests are logged.\n"
+ + " level >= 4 then the time taken to fulfill the request is logged.\n"
+ + " level >= 5 then a sample from the beginning of the incoming data is logged.\n"
+ + " level >= 6 then the entire incoming data is logged.\n"
+ + " no level is specified then the current level is returned.\n\n"
+ + "If out or all and\n"
+ + " level >= 3 then short notices about all outgoing requests going through WebUtil are logged.\n"
+ + " level >= 4 then the time taken to fulfill the request is logged.\n",
HandleDebugHttpCommand);
}
@@ -119,25 +134,120 @@ namespace OpenSim.Framework.Servers
/// Turn on some debugging values for OpenSim.
///
///
- private static void HandleDebugHttpCommand(string module, string[] args)
+ private static void HandleDebugHttpCommand(string module, string[] cmdparams)
{
- if (args.Length == 3)
+ if (cmdparams.Length < 3)
{
- int newDebug;
- if (int.TryParse(args[2], out newDebug))
- {
- MainServer.DebugLevel = newDebug;
- MainConsole.Instance.OutputFormat("Debug http level set to {0}", newDebug);
- }
+ MainConsole.Instance.Output("Usage: debug http 0..6");
+ return;
}
- else if (args.Length == 2)
+
+ bool inReqs = false;
+ bool outReqs = false;
+ bool allReqs = false;
+
+ string subCommand = cmdparams[2];
+
+ if (subCommand.ToLower() == "in")
{
- MainConsole.Instance.OutputFormat("Current debug http level is {0}", MainServer.DebugLevel);
+ inReqs = true;
+ }
+ else if (subCommand.ToLower() == "out")
+ {
+ outReqs = true;
+ }
+ else if (subCommand.ToLower() == "all")
+ {
+ allReqs = true;
}
else
{
- MainConsole.Instance.Output("Usage: debug http 0..3");
+ MainConsole.Instance.Output("You must specify in, out or all");
+ return;
}
+
+ if (cmdparams.Length >= 4)
+ {
+ string rawNewDebug = cmdparams[3];
+ int newDebug;
+
+ if (!int.TryParse(rawNewDebug, out newDebug))
+ {
+ MainConsole.Instance.OutputFormat("{0} is not a valid debug level", rawNewDebug);
+ return;
+ }
+
+ if (newDebug < 0 || newDebug > 6)
+ {
+ MainConsole.Instance.OutputFormat("{0} is outside the valid debug level range of 0..6", newDebug);
+ return;
+ }
+
+ if (allReqs || inReqs)
+ {
+ MainServer.DebugLevel = newDebug;
+ MainConsole.Instance.OutputFormat("IN debug level set to {0}", newDebug);
+ }
+
+ if (allReqs || outReqs)
+ {
+ WebUtil.DebugLevel = newDebug;
+ MainConsole.Instance.OutputFormat("OUT debug level set to {0}", newDebug);
+ }
+ }
+ else
+ {
+ if (allReqs || inReqs)
+ MainConsole.Instance.OutputFormat("Current IN debug level is {0}", MainServer.DebugLevel);
+
+ if (allReqs || outReqs)
+ MainConsole.Instance.OutputFormat("Current OUT debug level is {0}", WebUtil.DebugLevel);
+ }
+ }
+
+ private static void HandleShowHttpHandlersCommand(string module, string[] args)
+ {
+ if (args.Length != 2)
+ {
+ MainConsole.Instance.Output("Usage: show http-handlers");
+ return;
+ }
+
+ StringBuilder handlers = new StringBuilder();
+
+ lock (m_Servers)
+ {
+ foreach (BaseHttpServer httpServer in m_Servers.Values)
+ {
+ handlers.AppendFormat(
+ "Registered HTTP Handlers for server at {0}:{1}\n", httpServer.ListenIPAddress, httpServer.Port);
+
+ handlers.AppendFormat("* XMLRPC:\n");
+ foreach (String s in httpServer.GetXmlRpcHandlerKeys())
+ handlers.AppendFormat("\t{0}\n", s);
+
+ handlers.AppendFormat("* HTTP:\n");
+ List poll = httpServer.GetPollServiceHandlerKeys();
+ foreach (String s in httpServer.GetHTTPHandlerKeys())
+ handlers.AppendFormat("\t{0} {1}\n", s, (poll.Contains(s) ? "(poll service)" : string.Empty));
+
+// handlers.AppendFormat("* Agent:\n");
+// foreach (String s in httpServer.GetAgentHandlerKeys())
+// handlers.AppendFormat("\t{0}\n", s);
+
+ handlers.AppendFormat("* LLSD:\n");
+ foreach (String s in httpServer.GetLLSDHandlerKeys())
+ handlers.AppendFormat("\t{0}\n", s);
+
+ handlers.AppendFormat("* StreamHandlers ({0}):\n", httpServer.GetStreamHandlerKeys().Count);
+ foreach (String s in httpServer.GetStreamHandlerKeys())
+ handlers.AppendFormat("\t{0}\n", s);
+
+ handlers.Append("\n");
+ }
+ }
+
+ MainConsole.Instance.Output(handlers.ToString());
}
///
diff --git a/OpenSim/Framework/Servers/VersionInfo.cs b/OpenSim/Framework/Servers/VersionInfo.cs
index 016a1744ef..bb094ed6f5 100644
--- a/OpenSim/Framework/Servers/VersionInfo.cs
+++ b/OpenSim/Framework/Servers/VersionInfo.cs
@@ -29,7 +29,7 @@ namespace OpenSim
{
public class VersionInfo
{
- private const string VERSION_NUMBER = "0.7.4CM";
+ private const string VERSION_NUMBER = "0.7.5CM";
private const Flavour VERSION_FLAVOUR = Flavour.Dev;
public enum Flavour
diff --git a/OpenSim/Framework/TaskInventoryDictionary.cs b/OpenSim/Framework/TaskInventoryDictionary.cs
index 4d07746d2a..62ecbd1018 100644
--- a/OpenSim/Framework/TaskInventoryDictionary.cs
+++ b/OpenSim/Framework/TaskInventoryDictionary.cs
@@ -39,10 +39,12 @@ using OpenMetaverse;
namespace OpenSim.Framework
{
///
- /// A dictionary for task inventory.
+ /// A dictionary containing task inventory items. Indexed by item UUID.
///
+ ///
/// This class is not thread safe. Callers must synchronize on Dictionary methods or Clone() this object before
/// iterating over it.
+ ///
public class TaskInventoryDictionary : Dictionary,
ICloneable, IXmlSerializable
{
diff --git a/OpenSim/Framework/TaskInventoryItem.cs b/OpenSim/Framework/TaskInventoryItem.cs
index fb818ee74b..574ee567f4 100644
--- a/OpenSim/Framework/TaskInventoryItem.cs
+++ b/OpenSim/Framework/TaskInventoryItem.cs
@@ -73,9 +73,6 @@ namespace OpenSim.Framework
private bool _ownerChanged = false;
- // This used ONLY during copy. It can't be relied on at other times!
- private bool _scriptRunning = true;
-
public UUID AssetID {
get {
return _assetID;
@@ -353,14 +350,13 @@ namespace OpenSim.Framework
}
}
- public bool ScriptRunning {
- get {
- return _scriptRunning;
- }
- set {
- _scriptRunning = value;
- }
- }
+ ///
+ /// This used ONLY during copy. It can't be relied on at other times!
+ ///
+ ///
+ /// For true script running status, use IEntityInventory.TryGetScriptInstanceRunning() for now.
+ ///
+ public bool ScriptRunning { get; set; }
// See ICloneable
@@ -388,6 +384,7 @@ namespace OpenSim.Framework
public TaskInventoryItem()
{
+ ScriptRunning = true;
CreationDate = (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
}
}
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
index 384f7160f3..e76a37b9a1 100644
--- a/OpenSim/Framework/Util.cs
+++ b/OpenSim/Framework/Util.cs
@@ -545,6 +545,19 @@ namespace OpenSim.Framework
return (x + y - (min >> 1) - (min >> 2) + (min >> 4));
}
+ ///
+ /// Determines whether a point is inside a bounding box.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static bool IsInsideBox(Vector3 v, Vector3 min, Vector3 max)
+ {
+ return v.X >= min.X & v.Y >= min.Y && v.Z >= min.Z
+ && v.X <= max.X && v.Y <= max.Y && v.Z <= max.Z;
+ }
+
///
/// Are the co-ordinates of the new region visible from the old region?
///
@@ -862,6 +875,12 @@ namespace OpenSim.Framework
return Math.Min(Math.Max(x, min), max);
}
+ public static Vector3 Clip(Vector3 vec, float min, float max)
+ {
+ return new Vector3(Clip(vec.X, min, max), Clip(vec.Y, min, max),
+ Clip(vec.Z, min, max));
+ }
+
///
/// Convert an UUID to a raw uuid string. Right now this is a string without hyphens.
///
@@ -1013,6 +1032,38 @@ namespace OpenSim.Framework
}
}
+ ///
+ /// Copy data from one stream to another, leaving the read position of both streams at the beginning.
+ ///
+ ///
+ /// Input stream. Must be seekable.
+ ///
+ ///
+ /// Thrown if the input stream is not seekable.
+ ///
+ public static Stream Copy(Stream inputStream)
+ {
+ if (!inputStream.CanSeek)
+ throw new ArgumentException("Util.Copy(Stream inputStream) must receive an inputStream that can seek");
+
+ const int readSize = 256;
+ byte[] buffer = new byte[readSize];
+ MemoryStream ms = new MemoryStream();
+
+ int count = inputStream.Read(buffer, 0, readSize);
+
+ while (count > 0)
+ {
+ ms.Write(buffer, 0, count);
+ count = inputStream.Read(buffer, 0, readSize);
+ }
+
+ ms.Position = 0;
+ inputStream.Position = 0;
+
+ return ms;
+ }
+
public static XmlRpcResponse XmlRpcCommand(string url, string methodName, params object[] args)
{
return SendXmlRpcCommand(url, methodName, args);
diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs
index 30a8c28072..b85d93d53a 100644
--- a/OpenSim/Framework/WebUtil.cs
+++ b/OpenSim/Framework/WebUtil.cs
@@ -53,10 +53,18 @@ namespace OpenSim.Framework
LogManager.GetLogger(
MethodBase.GetCurrentMethod().DeclaringType);
+ ///
+ /// Control the printing of certain debug messages.
+ ///
+ ///
+ /// If DebugLevel >= 3 then short notices about outgoing HTTP requests are logged.
+ ///
+ public static int DebugLevel { get; set; }
+
///
/// Request number for diagnostic purposes.
///
- public static int RequestNumber = 0;
+ public static int RequestNumber { get; internal set; }
///
/// this is the header field used to communicate the local request id
@@ -146,7 +154,11 @@ namespace OpenSim.Framework
private static OSDMap ServiceOSDRequestWorker(string url, OSDMap data, string method, int timeout, bool compressed)
{
int reqnum = RequestNumber++;
- // m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method);
+
+ if (DebugLevel >= 3)
+ m_log.DebugFormat(
+ "[WEB UTIL]: HTTP OUT {0} ServiceOSD {1} {2} (timeout {3}, compressed {4})",
+ reqnum, method, url, timeout, compressed);
string errorMessage = "unknown error";
int tickstart = Util.EnvironmentTickCount();
@@ -230,7 +242,7 @@ namespace OpenSim.Framework
int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
if (tickdiff > LongCallTime)
m_log.InfoFormat(
- "[OSD REQUEST]: Slow request to <{0}> {1} {2} took {3}ms, {4}ms writing, {5}",
+ "[WEB UTIL]: Slow ServiceOSD request {0} {1} {2} took {3}ms, {4}ms writing, {5}",
reqnum,
method,
url,
@@ -239,10 +251,14 @@ namespace OpenSim.Framework
strBuffer != null
? (strBuffer.Length > MaxRequestDiagLength ? strBuffer.Remove(MaxRequestDiagLength) : strBuffer)
: "");
+ else if (DebugLevel >= 4)
+ m_log.DebugFormat(
+ "[WEB UTIL]: HTTP OUT {0} took {1}ms, {2}ms writing",
+ reqnum, tickdiff, tickdata);
}
m_log.DebugFormat(
- "[WEB UTIL]: <{0}> osd request for {1}, method {2} FAILED: {3}", reqnum, url, method, errorMessage);
+ "[WEB UTIL]: ServiceOSD request {0} {1} {2} FAILED: {3}", reqnum, url, method, errorMessage);
return ErrorResponseMap(errorMessage);
}
@@ -318,7 +334,11 @@ namespace OpenSim.Framework
{
int reqnum = RequestNumber++;
string method = (data != null && data["RequestMethod"] != null) ? data["RequestMethod"] : "unknown";
- // m_log.DebugFormat("[WEB UTIL]: <{0}> start form request for {1}, method {2}",reqnum,url,method);
+
+ if (DebugLevel >= 3)
+ m_log.DebugFormat(
+ "[WEB UTIL]: HTTP OUT {0} ServiceForm {1} {2} (timeout {3})",
+ reqnum, method, url, timeout);
string errorMessage = "unknown error";
int tickstart = Util.EnvironmentTickCount();
@@ -381,7 +401,7 @@ namespace OpenSim.Framework
int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
if (tickdiff > LongCallTime)
m_log.InfoFormat(
- "[SERVICE FORM]: Slow request to <{0}> {1} {2} took {3}ms, {4}ms writing, {5}",
+ "[WEB UTIL]: Slow ServiceForm request {0} {1} {2} took {3}ms, {4}ms writing, {5}",
reqnum,
method,
url,
@@ -390,9 +410,13 @@ namespace OpenSim.Framework
queryString != null
? (queryString.Length > MaxRequestDiagLength) ? queryString.Remove(MaxRequestDiagLength) : queryString
: "");
+ else if (DebugLevel >= 4)
+ m_log.DebugFormat(
+ "[WEB UTIL]: HTTP OUT {0} took {1}ms, {2}ms writing",
+ reqnum, tickdiff, tickdata);
}
- m_log.WarnFormat("[SERVICE FORM]: <{0}> form request to {1} failed: {2}", reqnum, url, errorMessage);
+ m_log.WarnFormat("[WEB UTIL]: ServiceForm request {0} {1} {2} failed: {2}", reqnum, method, url, errorMessage);
return ErrorResponseMap(errorMessage);
}
@@ -644,7 +668,6 @@ namespace OpenSim.Framework
///
public static string[] GetPreferredImageTypes(string accept)
{
-
if (accept == null || accept == string.Empty)
return new string[0];
@@ -703,14 +726,16 @@ namespace OpenSim.Framework
int maxConnections)
{
int reqnum = WebUtil.RequestNumber++;
- // m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method);
+
+ if (WebUtil.DebugLevel >= 3)
+ m_log.DebugFormat(
+ "[WEB UTIL]: HTTP OUT {0} AsynchronousRequestObject {1} {2}",
+ reqnum, verb, requestUrl);
int tickstart = Util.EnvironmentTickCount();
// int tickdata = 0;
int tickdiff = 0;
-// m_log.DebugFormat("[ASYNC REQUEST]: Starting {0} {1}", verb, requestUrl);
-
Type type = typeof(TRequest);
WebRequest request = WebRequest.Create(requestUrl);
@@ -868,7 +893,7 @@ namespace OpenSim.Framework
}
m_log.InfoFormat(
- "[ASYNC REQUEST]: Slow request to <{0}> {1} {2} took {3}ms, {4}ms writing, {5}",
+ "[ASYNC REQUEST]: Slow request {0} {1} {2} took {3}ms, {4}ms writing, {5}",
reqnum,
verb,
requestUrl,
@@ -883,6 +908,12 @@ namespace OpenSim.Framework
requestUrl,
tickdiff);
}
+ else if (WebUtil.DebugLevel >= 4)
+ {
+ m_log.DebugFormat(
+ "[WEB UTIL]: HTTP OUT {0} took {1}ms",
+ reqnum, tickdiff);
+ }
}
}
@@ -903,7 +934,11 @@ namespace OpenSim.Framework
public static string MakeRequest(string verb, string requestUrl, string obj)
{
int reqnum = WebUtil.RequestNumber++;
- // m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method);
+
+ if (WebUtil.DebugLevel >= 3)
+ m_log.DebugFormat(
+ "[WEB UTIL]: HTTP OUT {0} SynchronousRestForms {1} {2}",
+ reqnum, verb, requestUrl);
int tickstart = Util.EnvironmentTickCount();
int tickdata = 0;
@@ -990,7 +1025,7 @@ namespace OpenSim.Framework
int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
if (tickdiff > WebUtil.LongCallTime)
m_log.InfoFormat(
- "[FORMS]: Slow request to <{0}> {1} {2} took {3}ms {4}ms writing {5}",
+ "[FORMS]: Slow request {0} {1} {2} took {3}ms, {4}ms writing, {5}",
reqnum,
verb,
requestUrl,
@@ -998,6 +1033,10 @@ namespace OpenSim.Framework
tickset,
tickdata,
obj.Length > WebUtil.MaxRequestDiagLength ? obj.Remove(WebUtil.MaxRequestDiagLength) : obj);
+ else if (WebUtil.DebugLevel >= 4)
+ m_log.DebugFormat(
+ "[WEB UTIL]: HTTP OUT {0} took {1}ms, {2}ms writing",
+ reqnum, tickdiff, tickdata);
return respstring;
}
@@ -1032,7 +1071,11 @@ namespace OpenSim.Framework
public static TResponse MakeRequest(string verb, string requestUrl, TRequest obj, int pTimeout, int maxConnections)
{
int reqnum = WebUtil.RequestNumber++;
- // m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method);
+
+ if (WebUtil.DebugLevel >= 3)
+ m_log.DebugFormat(
+ "[WEB UTIL]: HTTP OUT {0} SynchronousRestObject {1} {2}",
+ reqnum, verb, requestUrl);
int tickstart = Util.EnvironmentTickCount();
int tickdata = 0;
@@ -1151,7 +1194,7 @@ namespace OpenSim.Framework
}
m_log.InfoFormat(
- "[SynchronousRestObjectRequester]: Slow request to <{0}> {1} {2} took {3}ms, {4}ms writing, {5}",
+ "[SynchronousRestObjectRequester]: Slow request {0} {1} {2} took {3}ms, {4}ms writing, {5}",
reqnum,
verb,
requestUrl,
@@ -1159,6 +1202,12 @@ namespace OpenSim.Framework
tickdata,
originalRequest);
}
+ else if (WebUtil.DebugLevel >= 4)
+ {
+ m_log.DebugFormat(
+ "[WEB UTIL]: HTTP OUT {0} took {1}ms, {2}ms writing",
+ reqnum, tickdiff, tickdata);
+ }
return deserial;
}
diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs
index 6255515c32..5f072722a2 100644
--- a/OpenSim/Region/Application/OpenSim.cs
+++ b/OpenSim/Region/Application/OpenSim.cs
@@ -35,6 +35,7 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Timers;
using log4net;
+using NDesk.Options;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Framework;
@@ -253,8 +254,14 @@ namespace OpenSim
m_console.Commands.AddCommand("Debug", false, "debug teleport", "debug teleport", "Toggle teleport route debugging", Debug);
m_console.Commands.AddCommand("Debug", false, "debug scene",
- "debug scene ",
- "Turn on scene debugging", Debug);
+ "debug scene active|collisions|physics|scripting|teleport true|false",
+ "Turn on scene debugging.",
+ "If active is false then main scene update and maintenance loops are suspended.\n"
+ + "If collisions is false then collisions with other objects are turned off.\n"
+ + "If physics is false then all physics objects are non-physical.\n"
+ + "If scripting is false then no scripting operations happen.\n"
+ + "If teleport is true then some extra teleport debug information is logged.",
+ Debug);
m_console.Commands.AddCommand("General", false, "change region",
"change region ",
@@ -291,7 +298,7 @@ namespace OpenSim
m_console.Commands.AddCommand("Archiving", false, "save oar",
//"save oar [-v|--version=] [-p|--profile=] []",
- "save oar [-h|--home=] [--noassets] [--publish] [--perm=] []",
+ "save oar [-h|--home=] [--noassets] [--publish] [--perm=] [--all] []",
"Save a region's data to an OAR archive.",
// "-v|--version= generates scene objects as per older versions of the serialization (e.g. -v=0)" + Environment.NewLine
"-h|--home= adds the url of the profile service to the saved user information.\n"
@@ -301,6 +308,7 @@ namespace OpenSim
+ " this is useful if you're making oars generally available that might be reloaded to the same grid from which you published\n"
+ "--perm= stops objects with insufficient permissions from being saved to the OAR.\n"
+ " can contain one or more of these characters: \"C\" = Copy, \"T\" = Transfer\n"
+ + "--all saves all the regions in the simulator, instead of just the current region.\n"
+ "The OAR path must be a filesystem path."
+ " If this is not given then the oar is saved to region.oar in the current directory.",
SaveOar);
@@ -310,8 +318,11 @@ namespace OpenSim
"Change the scale of a named prim", HandleEditScale);
m_console.Commands.AddCommand("Users", false, "kick user",
- "kick user [message]",
- "Kick a user off the simulator", KickUserCommand);
+ "kick user [--force] [message]",
+ "Kick a user off the simulator",
+ "The --force option will kick the user without any checks to see whether it's already in the process of closing\n"
+ + "Only use this option if you are sure the avatar is inactive and a normal kick user operation does not removed them",
+ KickUserCommand);
m_console.Commands.AddCommand("Users", false, "show users",
"show users [full]",
@@ -328,10 +339,6 @@ namespace OpenSim
"show circuits",
"Show agent circuit data", HandleShow);
- m_console.Commands.AddCommand("Comms", false, "show http-handlers",
- "show http-handlers",
- "Show all registered http handlers", HandleShow);
-
m_console.Commands.AddCommand("Comms", false, "show pending-objects",
"show pending-objects",
"Show # of objects on the pending queues of all scene viewers", HandleShow);
@@ -416,6 +423,7 @@ namespace OpenSim
{
RunCommandScript(m_shutdownCommandsFile);
}
+
base.ShutdownSpecific();
}
@@ -453,11 +461,17 @@ namespace OpenSim
/// name of avatar to kick
private void KickUserCommand(string module, string[] cmdparams)
{
- if (cmdparams.Length < 4)
+ bool force = false;
+
+ OptionSet options = new OptionSet().Add("f|force", delegate (string v) { force = v != null; });
+
+ List mainParams = options.Parse(cmdparams);
+
+ if (mainParams.Count < 4)
return;
string alert = null;
- if (cmdparams.Length > 4)
+ if (mainParams.Count > 4)
alert = String.Format("\n{0}\n", String.Join(" ", cmdparams, 4, cmdparams.Length - 4));
IList agents = SceneManager.GetCurrentSceneAvatars();
@@ -466,8 +480,8 @@ namespace OpenSim
{
RegionInfo regionInfo = presence.Scene.RegionInfo;
- if (presence.Firstname.ToLower().Contains(cmdparams[2].ToLower()) &&
- presence.Lastname.ToLower().Contains(cmdparams[3].ToLower()))
+ if (presence.Firstname.ToLower().Contains(mainParams[2].ToLower()) &&
+ presence.Lastname.ToLower().Contains(mainParams[3].ToLower()))
{
MainConsole.Instance.Output(
String.Format(
@@ -480,7 +494,7 @@ namespace OpenSim
else
presence.ControllingClient.Kick("\nYou have been logged out by an administrator.\n");
- presence.Scene.IncomingCloseAgent(presence.UUID);
+ presence.Scene.IncomingCloseAgent(presence.UUID, force);
}
}
@@ -922,7 +936,8 @@ namespace OpenSim
}
else
{
- MainConsole.Instance.Output("Usage: debug scene scripting|collisions|physics|teleport true|false");
+ MainConsole.Instance.Output(
+ "Usage: debug scene active|scripting|collisions|physics|teleport true|false");
}
break;
@@ -1002,33 +1017,6 @@ namespace OpenSim
HandleShowCircuits();
break;
- case "http-handlers":
- System.Text.StringBuilder handlers = new System.Text.StringBuilder("Registered HTTP Handlers:\n");
-
- handlers.AppendFormat("* XMLRPC:\n");
- foreach (String s in HttpServer.GetXmlRpcHandlerKeys())
- handlers.AppendFormat("\t{0}\n", s);
-
- handlers.AppendFormat("* HTTP:\n");
- List poll = HttpServer.GetPollServiceHandlerKeys();
- foreach (String s in HttpServer.GetHTTPHandlerKeys())
- handlers.AppendFormat("\t{0} {1}\n", s, (poll.Contains(s) ? "(poll service)" : string.Empty));
-
- handlers.AppendFormat("* Agent:\n");
- foreach (String s in HttpServer.GetAgentHandlerKeys())
- handlers.AppendFormat("\t{0}\n", s);
-
- handlers.AppendFormat("* LLSD:\n");
- foreach (String s in HttpServer.GetLLSDHandlerKeys())
- handlers.AppendFormat("\t{0}\n", s);
-
- handlers.AppendFormat("* StreamHandlers ({0}):\n", HttpServer.GetStreamHandlerKeys().Count);
- foreach (String s in HttpServer.GetStreamHandlerKeys())
- handlers.AppendFormat("\t{0}\n", s);
-
- MainConsole.Instance.Output(handlers.ToString());
- break;
-
case "modules":
MainConsole.Instance.Output("The currently loaded shared modules are:");
foreach (IRegionModule module in m_moduleLoader.GetLoadedSharedModules)
@@ -1123,7 +1111,7 @@ namespace OpenSim
aCircuit.Name,
aCircuit.child ? "child" : "root",
aCircuit.circuitcode.ToString(),
- aCircuit.IPAddress.ToString(),
+ aCircuit.IPAddress != null ? aCircuit.IPAddress.ToString() : "not set",
aCircuit.Viewer);
});
diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs
index d107b7a461..7232383a77 100644
--- a/OpenSim/Region/Application/OpenSimBase.cs
+++ b/OpenSim/Region/Application/OpenSimBase.cs
@@ -232,8 +232,6 @@ namespace OpenSim
base.StartupSpecific();
- m_stats = StatsManager.SimExtraStats;
-
// Create a ModuleLoader instance
m_moduleLoader = new ModuleLoader(m_config.Source);
@@ -249,51 +247,51 @@ namespace OpenSim
plugin.PostInitialise();
}
- AddPluginCommands();
- }
-
- protected virtual void AddPluginCommands()
- {
- // If console exists add plugin commands.
if (m_console != null)
{
- List topics = GetHelpTopics();
+ StatsManager.RegisterConsoleCommands(m_console);
+ AddPluginCommands(m_console);
+ }
+ }
- foreach (string topic in topics)
+ protected virtual void AddPluginCommands(CommandConsole console)
+ {
+ List topics = GetHelpTopics();
+
+ foreach (string topic in topics)
+ {
+ string capitalizedTopic = char.ToUpper(topic[0]) + topic.Substring(1);
+
+ // This is a hack to allow the user to enter the help command in upper or lowercase. This will go
+ // away at some point.
+ console.Commands.AddCommand(capitalizedTopic, false, "help " + topic,
+ "help " + capitalizedTopic,
+ "Get help on plugin command '" + topic + "'",
+ HandleCommanderHelp);
+ console.Commands.AddCommand(capitalizedTopic, false, "help " + capitalizedTopic,
+ "help " + capitalizedTopic,
+ "Get help on plugin command '" + topic + "'",
+ HandleCommanderHelp);
+
+ ICommander commander = null;
+
+ Scene s = SceneManager.CurrentOrFirstScene;
+
+ if (s != null && s.GetCommanders() != null)
{
- string capitalizedTopic = char.ToUpper(topic[0]) + topic.Substring(1);
+ if (s.GetCommanders().ContainsKey(topic))
+ commander = s.GetCommanders()[topic];
+ }
- // This is a hack to allow the user to enter the help command in upper or lowercase. This will go
- // away at some point.
- m_console.Commands.AddCommand(capitalizedTopic, false, "help " + topic,
- "help " + capitalizedTopic,
- "Get help on plugin command '" + topic + "'",
- HandleCommanderHelp);
- m_console.Commands.AddCommand(capitalizedTopic, false, "help " + capitalizedTopic,
- "help " + capitalizedTopic,
- "Get help on plugin command '" + topic + "'",
- HandleCommanderHelp);
+ if (commander == null)
+ continue;
- ICommander commander = null;
-
- Scene s = SceneManager.CurrentOrFirstScene;
-
- if (s != null && s.GetCommanders() != null)
- {
- if (s.GetCommanders().ContainsKey(topic))
- commander = s.GetCommanders()[topic];
- }
-
- if (commander == null)
- continue;
-
- foreach (string command in commander.Commands.Keys)
- {
- m_console.Commands.AddCommand(capitalizedTopic, false,
- topic + " " + command,
- topic + " " + commander.Commands[command].ShortHelp(),
- String.Empty, HandleCommanderCommand);
- }
+ foreach (string command in commander.Commands.Keys)
+ {
+ console.Commands.AddCommand(capitalizedTopic, false,
+ topic + " " + command,
+ topic + " " + commander.Commands[command].ShortHelp(),
+ String.Empty, HandleCommanderCommand);
}
}
}
@@ -623,7 +621,7 @@ namespace OpenSim
if (account == null)
{
m_log.ErrorFormat(
- "[OPENSIM]: Unable to store account. If this simulator is connected to a grid, you must create the estate owner account first.");
+ "[OPENSIM]: Unable to store account. If this simulator is connected to a grid, you must create the estate owner account first at the grid level.");
}
else
{
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs
index 650cd508f5..f6146a9171 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs
@@ -241,8 +241,8 @@ namespace OpenSim.Region.ClientStack.Linden
m_HostCapsObj.RegisterHandler(
"SEED", new RestStreamHandler("POST", capsBase + m_requestPath, SeedCapRequest, "SEED", null));
- m_log.DebugFormat(
- "[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID);
+// m_log.DebugFormat(
+// "[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID);
//m_capsHandlers["MapLayer"] =
// new LLSDStreamhandler("POST",
@@ -337,11 +337,12 @@ namespace OpenSim.Region.ClientStack.Linden
public string SeedCapRequest(string request, string path, string param,
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
-// m_log.Debug("[CAPS]: Seed Caps Request in region: " + m_regionName);
+ m_log.DebugFormat(
+ "[CAPS]: Received SEED caps request in {0} for agent {1}", m_regionName, m_HostCapsObj.AgentID);
if (!m_Scene.CheckClient(m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint))
{
- m_log.DebugFormat(
+ m_log.WarnFormat(
"[CAPS]: Unauthorized CAPS client {0} from {1}",
m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint);
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs
index e113c60fd1..5bbdce8345 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs
@@ -94,7 +94,7 @@ namespace OpenSim.Region.ClientStack.Linden
//scene.CommsManager.HttpServer.AddLLSDHandler("/CAPS/EQG/", EventQueueFallBack);
- scene.EventManager.OnNewClient += OnNewClient;
+// scene.EventManager.OnNewClient += OnNewClient;
// TODO: Leaving these open, or closing them when we
// become a child is incorrect. It messes up TP in a big
@@ -102,6 +102,7 @@ namespace OpenSim.Region.ClientStack.Linden
// circuit is there.
scene.EventManager.OnClientClosed += ClientClosed;
+
scene.EventManager.OnMakeChildAgent += MakeChildAgent;
scene.EventManager.OnRegisterCaps += OnRegisterCaps;
@@ -110,10 +111,10 @@ namespace OpenSim.Region.ClientStack.Linden
false,
"debug eq",
"debug eq [0|1|2]",
- "Turn on event queue debugging"
- + "<= 0 - turns off all event queue logging"
- + ">= 1 - turns on outgoing event logging"
- + ">= 2 - turns on poll notification",
+ "Turn on event queue debugging\n"
+ + " <= 0 - turns off all event queue logging\n"
+ + " >= 1 - turns on outgoing event logging\n"
+ + " >= 2 - turns on poll notification",
HandleDebugEq);
}
else
@@ -226,16 +227,6 @@ namespace OpenSim.Region.ClientStack.Linden
#endregion
- private void OnNewClient(IClientAPI client)
- {
- //client.OnLogout += ClientClosed;
- }
-
-// private void ClientClosed(IClientAPI client)
-// {
-// ClientClosed(client.AgentId);
-// }
-
private void ClientClosed(UUID agentID, Scene scene)
{
// m_log.DebugFormat("[EVENTQUEUE]: Closed client {0} in region {1}", agentID, m_scene.RegionInfo.RegionName);
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs
index cd70410680..d604cf6f51 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs
@@ -94,7 +94,7 @@ namespace OpenSim.Region.ClientStack.Linden.Tests
UUID spId = TestHelpers.ParseTail(0x1);
SceneHelpers.AddScenePresence(m_scene, spId);
- m_scene.IncomingCloseAgent(spId);
+ m_scene.IncomingCloseAgent(spId, false);
// TODO: Add more assertions for the other aspects of event queues
Assert.That(MainServer.Instance.GetPollServiceHandlerKeys().Count, Is.EqualTo(0));
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs
index 0a5ad0f02a..fcac182f45 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs
@@ -109,7 +109,7 @@ namespace OpenSim.Region.ClientStack.Linden
UUID capID = UUID.Random();
- m_log.DebugFormat("[REGION CONSOLE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
+// m_log.DebugFormat("[REGION CONSOLE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
caps.RegisterHandler(
"SimConsoleAsync",
new ConsoleHandler("/CAPS/" + capID + "/", "SimConsoleAsync", agentID, this, m_scene));
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs b/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs
index 1b8535cacb..e22670b3a9 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs
@@ -45,7 +45,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public Packet Packet;
///
- /// Default constructor
+ /// No arg constructor.
+ ///
+ public IncomingPacket() {}
+
+ ///
+ /// Constructor
///
/// Reference to the client this packet came from
/// Packet data
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
index ae9ed7f422..c9aa4ca404 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
@@ -47,6 +47,7 @@ using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using Timer = System.Timers.Timer;
using AssetLandmark = OpenSim.Framework.AssetLandmark;
+using RegionFlags = OpenMetaverse.RegionFlags;
using Nini.Config;
using System.IO;
@@ -355,7 +356,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private bool m_deliverPackets = true;
private int m_animationSequenceNumber = 1;
private bool m_SendLogoutPacketWhenClosing = true;
- private AgentUpdateArgs lastarg;
+
+ ///
+ /// We retain a single AgentUpdateArgs so that we can constantly reuse it rather than construct a new one for
+ /// every single incoming AgentUpdate. Every client sends 10 AgentUpdate UDP messages per second, even if it
+ /// is doing absolutely nothing.
+ ///
+ ///
+ /// This does mean that agent updates must be processed synchronously, at least for each client, and called methods
+ /// cannot retain a reference to it outside of that method.
+ ///
+ private AgentUpdateArgs m_lastAgentUpdateArgs;
protected Dictionary m_packetHandlers = new Dictionary();
protected Dictionary m_genericPacketHandlers = new Dictionary(); //PauPaw:Local Generic Message handlers
@@ -510,19 +521,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
///
public void Close()
{
- Close(true);
+ Close(true, false);
}
- ///
- /// Shut down the client view
- ///
- public void Close(bool sendStop)
+ public void Close(bool sendStop, bool force)
{
// We lock here to prevent race conditions between two threads calling close simultaneously (e.g.
// a simultaneous relog just as a client is being closed out due to no packet ack from the old connection.
lock (CloseSyncLock)
{
- if (!IsActive)
+ // We still perform a force close inside the sync lock since this is intended to attempt close where
+ // there is some unidentified connection problem, not where we have issues due to deadlock
+ if (!IsActive && !force)
return;
IsActive = false;
@@ -837,8 +847,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
OutPacket(mov, ThrottleOutPacketType.Unknown);
}
- public void SendChatMessage(string message, byte type, Vector3 fromPos, string fromName,
- UUID fromAgentID, byte source, byte audible)
+ public void SendChatMessage(
+ string message, byte type, Vector3 fromPos, string fromName,
+ UUID fromAgentID, UUID ownerID, byte source, byte audible)
{
ChatFromSimulatorPacket reply = (ChatFromSimulatorPacket)PacketPool.Instance.GetPacket(PacketType.ChatFromSimulator);
reply.ChatData.Audible = audible;
@@ -847,7 +858,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
reply.ChatData.SourceType = source;
reply.ChatData.Position = fromPos;
reply.ChatData.FromName = Util.StringToBytes256(fromName);
- reply.ChatData.OwnerID = fromAgentID;
+ reply.ChatData.OwnerID = ownerID;
reply.ChatData.SourceID = fromAgentID;
OutPacket(reply, ThrottleOutPacketType.Unknown);
@@ -3985,7 +3996,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
List blocks = terseAgentUpdateBlocks.Value;
- ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
+ ImprovedTerseObjectUpdatePacket packet
+ = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate);
packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
packet.RegionData.TimeDilation = timeDilation;
packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
@@ -4030,7 +4042,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
List blocks = terseUpdateBlocks.Value;
- ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
+ ImprovedTerseObjectUpdatePacket packet
+ = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(
+ PacketType.ImprovedTerseObjectUpdate);
packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
packet.RegionData.TimeDilation = timeDilation;
packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
@@ -4038,7 +4052,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
for (int i = 0; i < blocks.Count; i++)
packet.ObjectData[i] = blocks[i];
- OutPacket(packet, ThrottleOutPacketType.Task, true);
+ OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseUpdates.Value, oPacket); });
}
#endregion Packet Sending
@@ -4535,7 +4549,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
returnblock[j] = new EstateOwnerMessagePacket.ParamListBlock();
}
- j = 0;
+ j = 0;
returnblock[j].Parameter = Utils.StringToBytes(estateID.ToString()); j++;
returnblock[j].Parameter = Utils.StringToBytes(((int)Constants.EstateAccessCodex.EstateBans).ToString()); j++;
@@ -5039,7 +5053,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Y, -64.0f, 64.0f), data, pos); pos += 2;
Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Z, -64.0f, 64.0f), data, pos); pos += 2;
- ImprovedTerseObjectUpdatePacket.ObjectDataBlock block = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock();
+ ImprovedTerseObjectUpdatePacket.ObjectDataBlock block
+ = PacketPool.Instance.GetDataBlock();
+
block.Data = data;
if (textureEntry != null && textureEntry.Length > 0)
@@ -5289,14 +5305,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
protected virtual void RegisterLocalPacketHandlers()
{
AddLocalPacketHandler(PacketType.LogoutRequest, HandleLogout);
+
+ // If AgentUpdate is ever handled asynchronously, then we will also need to construct a new AgentUpdateArgs
+ // for each AgentUpdate packet.
AddLocalPacketHandler(PacketType.AgentUpdate, HandleAgentUpdate, false);
+
AddLocalPacketHandler(PacketType.ViewerEffect, HandleViewerEffect, false);
AddLocalPacketHandler(PacketType.AgentCachedTexture, HandleAgentTextureCached, false);
AddLocalPacketHandler(PacketType.MultipleObjectUpdate, HandleMultipleObjUpdate, false);
AddLocalPacketHandler(PacketType.MoneyTransferRequest, HandleMoneyTransferRequest, false);
AddLocalPacketHandler(PacketType.ParcelBuy, HandleParcelBuyRequest, false);
- AddLocalPacketHandler(PacketType.UUIDGroupNameRequest, HandleUUIDGroupNameRequest, false);
- AddLocalPacketHandler(PacketType.ObjectGroup, HandleObjectGroupRequest, false);
+ AddLocalPacketHandler(PacketType.UUIDGroupNameRequest, HandleUUIDGroupNameRequest);
+ AddLocalPacketHandler(PacketType.ObjectGroup, HandleObjectGroupRequest);
AddLocalPacketHandler(PacketType.GenericMessage, HandleGenericMessage);
AddLocalPacketHandler(PacketType.AvatarPropertiesRequest, HandleAvatarPropertiesRequest);
AddLocalPacketHandler(PacketType.ChatFromViewer, HandleChatFromViewer);
@@ -5518,81 +5538,84 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#region Scene/Avatar
- private bool HandleAgentUpdate(IClientAPI sener, Packet Pack)
+ private bool HandleAgentUpdate(IClientAPI sener, Packet packet)
{
if (OnAgentUpdate != null)
{
- bool update = false;
- AgentUpdatePacket agenUpdate = (AgentUpdatePacket)Pack;
+ AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet;
#region Packet Session and User Check
- if (agenUpdate.AgentData.SessionID != SessionId || agenUpdate.AgentData.AgentID != AgentId)
+ if (agentUpdate.AgentData.SessionID != SessionId || agentUpdate.AgentData.AgentID != AgentId)
+ {
+ PacketPool.Instance.ReturnPacket(packet);
return false;
+ }
#endregion
- AgentUpdatePacket.AgentDataBlock x = agenUpdate.AgentData;
+ bool update = false;
+ AgentUpdatePacket.AgentDataBlock x = agentUpdate.AgentData;
- // We can only check when we have something to check
- // against.
-
- if (lastarg != null)
+ if (m_lastAgentUpdateArgs != null)
{
+ // These should be ordered from most-likely to
+ // least likely to change. I've made an initial
+ // guess at that.
update =
(
- (x.BodyRotation != lastarg.BodyRotation) ||
- (x.CameraAtAxis != lastarg.CameraAtAxis) ||
- (x.CameraCenter != lastarg.CameraCenter) ||
- (x.CameraLeftAxis != lastarg.CameraLeftAxis) ||
- (x.CameraUpAxis != lastarg.CameraUpAxis) ||
- (x.ControlFlags != lastarg.ControlFlags) ||
+ (x.BodyRotation != m_lastAgentUpdateArgs.BodyRotation) ||
+ (x.CameraAtAxis != m_lastAgentUpdateArgs.CameraAtAxis) ||
+ (x.CameraCenter != m_lastAgentUpdateArgs.CameraCenter) ||
+ (x.CameraLeftAxis != m_lastAgentUpdateArgs.CameraLeftAxis) ||
+ (x.CameraUpAxis != m_lastAgentUpdateArgs.CameraUpAxis) ||
+ (x.ControlFlags != m_lastAgentUpdateArgs.ControlFlags) ||
(x.ControlFlags != 0) ||
- (x.Far != lastarg.Far) ||
- (x.Flags != lastarg.Flags) ||
- (x.State != lastarg.State) ||
- (x.HeadRotation != lastarg.HeadRotation) ||
- (x.SessionID != lastarg.SessionID) ||
- (x.AgentID != lastarg.AgentID)
+ (x.Far != m_lastAgentUpdateArgs.Far) ||
+ (x.Flags != m_lastAgentUpdateArgs.Flags) ||
+ (x.State != m_lastAgentUpdateArgs.State) ||
+ (x.HeadRotation != m_lastAgentUpdateArgs.HeadRotation) ||
+ (x.SessionID != m_lastAgentUpdateArgs.SessionID) ||
+ (x.AgentID != m_lastAgentUpdateArgs.AgentID)
);
}
else
{
+ m_lastAgentUpdateArgs = new AgentUpdateArgs();
update = true;
}
- // These should be ordered from most-likely to
- // least likely to change. I've made an initial
- // guess at that.
-
if (update)
{
// m_log.DebugFormat("[LLCLIENTVIEW]: Triggered AgentUpdate for {0}", sener.Name);
- AgentUpdateArgs arg = new AgentUpdateArgs();
- arg.AgentID = x.AgentID;
- arg.BodyRotation = x.BodyRotation;
- arg.CameraAtAxis = x.CameraAtAxis;
- arg.CameraCenter = x.CameraCenter;
- arg.CameraLeftAxis = x.CameraLeftAxis;
- arg.CameraUpAxis = x.CameraUpAxis;
- arg.ControlFlags = x.ControlFlags;
- arg.Far = x.Far;
- arg.Flags = x.Flags;
- arg.HeadRotation = x.HeadRotation;
- arg.SessionID = x.SessionID;
- arg.State = x.State;
+ m_lastAgentUpdateArgs.AgentID = x.AgentID;
+ m_lastAgentUpdateArgs.BodyRotation = x.BodyRotation;
+ m_lastAgentUpdateArgs.CameraAtAxis = x.CameraAtAxis;
+ m_lastAgentUpdateArgs.CameraCenter = x.CameraCenter;
+ m_lastAgentUpdateArgs.CameraLeftAxis = x.CameraLeftAxis;
+ m_lastAgentUpdateArgs.CameraUpAxis = x.CameraUpAxis;
+ m_lastAgentUpdateArgs.ControlFlags = x.ControlFlags;
+ m_lastAgentUpdateArgs.Far = x.Far;
+ m_lastAgentUpdateArgs.Flags = x.Flags;
+ m_lastAgentUpdateArgs.HeadRotation = x.HeadRotation;
+ m_lastAgentUpdateArgs.SessionID = x.SessionID;
+ m_lastAgentUpdateArgs.State = x.State;
+
UpdateAgent handlerAgentUpdate = OnAgentUpdate;
UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate;
- lastarg = arg; // save this set of arguments for nexttime
+
if (handlerPreAgentUpdate != null)
- OnPreAgentUpdate(this, arg);
+ OnPreAgentUpdate(this, m_lastAgentUpdateArgs);
+
if (handlerAgentUpdate != null)
- OnAgentUpdate(this, arg);
+ OnAgentUpdate(this, m_lastAgentUpdateArgs);
handlerAgentUpdate = null;
handlerPreAgentUpdate = null;
}
}
+ PacketPool.Instance.ReturnPacket(packet);
+
return true;
}
@@ -5964,7 +5987,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
msgpack.MessageBlock.ID,
msgpack.MessageBlock.Offline != 0 ? true : false,
msgpack.MessageBlock.Position,
- msgpack.MessageBlock.BinaryBucket);
+ msgpack.MessageBlock.BinaryBucket,
+ true);
handlerInstantMessage(this, im);
}
@@ -9251,7 +9275,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
#endregion
- switch (Utils.BytesToString(messagePacket.MethodData.Method))
+ string method = Utils.BytesToString(messagePacket.MethodData.Method);
+
+ switch (method)
{
case "getinfo":
if (((Scene)m_scene).Permissions.CanIssueEstateCommand(AgentId, false))
@@ -9567,7 +9593,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return true;
default:
- m_log.Error("EstateOwnerMessage: Unknown method requested\n" + messagePacket);
+ m_log.WarnFormat(
+ "[LLCLIENTVIEW]: EstateOwnerMessage: Unknown method {0} requested for {1} in {2}",
+ method, Name, Scene.Name);
+
+ for (int i = 0; i < messagePacket.ParamList.Length; i++)
+ {
+ EstateOwnerMessagePacket.ParamListBlock block = messagePacket.ParamList[i];
+ string data = (string)Utils.BytesToString(block.Parameter);
+ m_log.DebugFormat("[LLCLIENTVIEW]: Param {0}={1}", i, data);
+ }
+
return true;
}
@@ -11960,7 +11996,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
logPacket = false;
if (DebugPacketLevel <= 50
- & (packet.Type == PacketType.ImprovedTerseObjectUpdate || packet.Type == PacketType.ObjectUpdate))
+ && (packet.Type == PacketType.ImprovedTerseObjectUpdate || packet.Type == PacketType.ObjectUpdate))
logPacket = false;
if (DebugPacketLevel <= 25 && packet.Type == PacketType.ObjectPropertiesFamily)
@@ -12034,8 +12070,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (!ProcessPacketMethod(packet))
m_log.Warn("[CLIENT]: unhandled packet " + packet.Type);
-
- PacketPool.Instance.ReturnPacket(packet);
}
private static PrimitiveBaseShape GetShapeFromAddPacket(ObjectAddPacket addPacket)
@@ -12204,7 +12238,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
Kick(reason);
Thread.Sleep(1000);
- Close();
+ Disconnect();
}
public void Disconnect()
@@ -12492,7 +12526,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
ushort timeDilation = Utils.FloatToUInt16(TIME_DILATION, 0.0f, 1.0f);
- ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
+ ImprovedTerseObjectUpdatePacket packet
+ = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(
+ PacketType.ImprovedTerseObjectUpdate);
+
packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
packet.RegionData.TimeDilation = timeDilation;
packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1];
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
index d6513c5403..b8951d966c 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
@@ -37,6 +37,7 @@ using log4net;
using Nini.Config;
using OpenMetaverse.Packets;
using OpenSim.Framework;
+using OpenSim.Framework.Console;
using OpenSim.Framework.Monitoring;
using OpenSim.Region.Framework.Scenes;
using OpenMetaverse;
@@ -100,9 +101,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// The measured resolution of Environment.TickCount
public readonly float TickCountResolution;
+
/// Number of prim updates to put on the queue each time the
/// OnQueueEmpty event is triggered for updates
public readonly int PrimUpdatesPerCallback;
+
/// Number of texture packets to put on the queue each time the
/// OnQueueEmpty event is triggered for textures
public readonly int TextureSendLimit;
@@ -124,28 +127,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// Manages authentication for agent circuits
private AgentCircuitManager m_circuitManager;
+
/// Reference to the scene this UDP server is attached to
protected Scene m_scene;
+
/// The X/Y coordinates of the scene this UDP server is attached to
private Location m_location;
+
/// The size of the receive buffer for the UDP socket. This value
/// is passed up to the operating system and used in the system networking
/// stack. Use zero to leave this value as the default
private int m_recvBufferSize;
+
/// Flag to process packets asynchronously or synchronously
private bool m_asyncPacketHandling;
+
/// Tracks whether or not a packet was sent each round so we know
/// whether or not to sleep
private bool m_packetSent;
/// Environment.TickCount of the last time that packet stats were reported to the scene
private int m_elapsedMSSinceLastStatReport = 0;
+
/// Environment.TickCount of the last time the outgoing packet handler executed
private int m_tickLastOutgoingPacketHandler;
+
/// Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped
private int m_elapsedMSOutgoingPacketHandler;
+
/// Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed
private int m_elapsed100MSOutgoingPacketHandler;
+
/// Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed
private int m_elapsed500MSOutgoingPacketHandler;
@@ -159,6 +171,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
protected bool m_sendPing;
private ExpiringCache> m_pendingCache = new ExpiringCache>();
+ private Pool m_incomingPacketPool;
+
+ private Stat m_incomingPacketPoolStat;
private int m_defaultRTO = 0;
private int m_maxRTO = 0;
@@ -180,7 +195,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
///
private IClientAPI m_currentIncomingClient;
- public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
+ public LLUDPServer(
+ IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port,
+ IConfigSource configSource, AgentCircuitManager circuitManager)
: base(listenIP, (int)port)
{
#region Environment.TickCount Measurement
@@ -202,6 +219,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_circuitManager = circuitManager;
int sceneThrottleBps = 0;
+ bool usePools = false;
IConfig config = configSource.Configs["ClientStack.LindenUDP"];
if (config != null)
@@ -227,6 +245,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_pausedAckTimeout = 1000 * 300; // 5 minutes
}
+ // FIXME: This actually only needs to be done once since the PacketPool is shared across all servers.
+ // However, there is no harm in temporarily doing it multiple times.
+ IConfig packetConfig = configSource.Configs["PacketPool"];
+ if (packetConfig != null)
+ {
+ PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true);
+ PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true);
+ usePools = packetConfig.GetBoolean("RecycleBaseUDPPackets", usePools);
+ }
+
#region BinaryStats
config = configSource.Configs["Statistics.Binary"];
m_shouldCollectStats = false;
@@ -254,20 +282,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_throttle = new TokenBucket(null, sceneThrottleBps);
ThrottleRates = new ThrottleRates(configSource);
+
+ if (usePools)
+ EnablePools();
}
public void Start()
{
- if (m_scene == null)
- throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference");
+ StartInbound();
+ StartOutbound();
+ m_elapsedMSSinceLastStatReport = Environment.TickCount;
+ }
+
+ private void StartInbound()
+ {
m_log.InfoFormat(
- "[LLUDPSERVER]: Starting the LLUDP server in {0} mode",
- m_asyncPacketHandling ? "asynchronous" : "synchronous");
+ "[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}",
+ m_asyncPacketHandling ? "asynchronous" : "synchronous", UsePools);
- base.Start(m_recvBufferSize, m_asyncPacketHandling);
+ base.StartInbound(m_recvBufferSize, m_asyncPacketHandling);
- // Start the packet processing threads
+ // This thread will process the packets received that are placed on the packetInbox
Watchdog.StartThread(
IncomingPacketHandler,
string.Format("Incoming Packets ({0})", m_scene.RegionInfo.RegionName),
@@ -276,6 +312,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
true,
GetWatchdogIncomingAlarmData,
Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
+ }
+
+ private new void StartOutbound()
+ {
+ m_log.Info("[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server");
+
+ base.StartOutbound();
Watchdog.StartThread(
OutgoingPacketHandler,
@@ -285,8 +328,57 @@ namespace OpenSim.Region.ClientStack.LindenUDP
true,
GetWatchdogOutgoingAlarmData,
Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
+ }
- m_elapsedMSSinceLastStatReport = Environment.TickCount;
+ public void Stop()
+ {
+ m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
+ base.StopOutbound();
+ base.StopInbound();
+ }
+
+ protected override bool EnablePools()
+ {
+ if (!UsePools)
+ {
+ base.EnablePools();
+
+ m_incomingPacketPool = new Pool(() => new IncomingPacket(), 500);
+
+ m_incomingPacketPoolStat
+ = new Stat(
+ "IncomingPacketPoolCount",
+ "Objects within incoming packet pool",
+ "The number of objects currently stored within the incoming packet pool",
+ "",
+ "clientstack",
+ "packetpool",
+ StatType.Pull,
+ stat => stat.Value = m_incomingPacketPool.Count,
+ StatVerbosity.Debug);
+
+ StatsManager.RegisterStat(m_incomingPacketPoolStat);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ protected override bool DisablePools()
+ {
+ if (UsePools)
+ {
+ base.DisablePools();
+
+ StatsManager.DeregisterStat(m_incomingPacketPoolStat);
+
+ // We won't null out the pool to avoid a race condition with code that may be in the middle of using it.
+
+ return true;
+ }
+
+ return false;
}
///
@@ -311,12 +403,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_currentOutgoingClient != null ? m_currentOutgoingClient.Name : "none");
}
- public new void Stop()
- {
- m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
- base.Stop();
- }
-
public void AddScene(IScene scene)
{
if (m_scene != null)
@@ -333,6 +419,117 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_scene = (Scene)scene;
m_location = new Location(m_scene.RegionInfo.RegionHandle);
+
+ MainConsole.Instance.Commands.AddCommand(
+ "Debug",
+ false,
+ "debug lludp start",
+ "debug lludp start ",
+ "Control LLUDP packet processing.",
+ "No effect if packet processing has already started.\n"
+ + "in - start inbound processing.\n"
+ + "out - start outbound processing.\n"
+ + "all - start in and outbound processing.\n",
+ HandleStartCommand);
+
+ MainConsole.Instance.Commands.AddCommand(
+ "Debug",
+ false,
+ "debug lludp stop",
+ "debug lludp stop ",
+ "Stop LLUDP packet processing.",
+ "No effect if packet processing has already stopped.\n"
+ + "in - stop inbound processing.\n"
+ + "out - stop outbound processing.\n"
+ + "all - stop in and outbound processing.\n",
+ HandleStopCommand);
+
+ MainConsole.Instance.Commands.AddCommand(
+ "Debug",
+ false,
+ "debug lludp pool",
+ "debug lludp pool ",
+ "Turn object pooling within the lludp component on or off.",
+ HandlePoolCommand);
+
+ MainConsole.Instance.Commands.AddCommand(
+ "Debug",
+ false,
+ "debug lludp status",
+ "debug lludp status",
+ "Return status of LLUDP packet processing.",
+ HandleStatusCommand);
+ }
+
+ private void HandleStartCommand(string module, string[] args)
+ {
+ if (args.Length != 4)
+ {
+ MainConsole.Instance.Output("Usage: debug lludp start ");
+ return;
+ }
+
+ string subCommand = args[3];
+
+ if (subCommand == "in" || subCommand == "all")
+ StartInbound();
+
+ if (subCommand == "out" || subCommand == "all")
+ StartOutbound();
+ }
+
+ private void HandleStopCommand(string module, string[] args)
+ {
+ if (args.Length != 4)
+ {
+ MainConsole.Instance.Output("Usage: debug lludp stop ");
+ return;
+ }
+
+ string subCommand = args[3];
+
+ if (subCommand == "in" || subCommand == "all")
+ StopInbound();
+
+ if (subCommand == "out" || subCommand == "all")
+ StopOutbound();
+ }
+
+ private void HandlePoolCommand(string module, string[] args)
+ {
+ if (args.Length != 4)
+ {
+ MainConsole.Instance.Output("Usage: debug lludp pool ");
+ return;
+ }
+
+ string enabled = args[3];
+
+ if (enabled == "on")
+ {
+ if (EnablePools())
+ MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", m_scene.Name);
+ }
+ else if (enabled == "off")
+ {
+ if (DisablePools())
+ MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", m_scene.Name);
+ }
+ else
+ {
+ MainConsole.Instance.Output("Usage: debug lludp pool ");
+ }
+ }
+
+ private void HandleStatusCommand(string module, string[] args)
+ {
+ MainConsole.Instance.OutputFormat(
+ "IN LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningInbound ? "enabled" : "disabled");
+
+ MainConsole.Instance.OutputFormat(
+ "OUT LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningOutbound ? "enabled" : "disabled");
+
+ MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", m_scene.Name, UsePools ? "on" : "off");
}
public bool HandlesRegion(Location x)
@@ -416,6 +613,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
byte[] data = packet.ToBytes();
SendPacketData(udpClient, data, packet.Type, category, method);
}
+
+ PacketPool.Instance.ReturnPacket(packet);
}
///
@@ -700,7 +899,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
LLUDPClient udpClient = null;
Packet packet = null;
int packetEnd = buffer.DataLength - 1;
- IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint;
+ IPEndPoint endPoint = (IPEndPoint)buffer.RemoteEndPoint;
#region Decoding
@@ -710,7 +909,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}",
// buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
- return; // Drop undersizd packet
+ return; // Drop undersized packet
}
int headerLen = 7;
@@ -733,7 +932,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
try
{
- packet = Packet.BuildPacket(buffer.Data, ref packetEnd,
+// packet = Packet.BuildPacket(buffer.Data, ref packetEnd,
+// // Only allocate a buffer for zerodecoding if the packet is zerocoded
+// ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
+ // If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we
+ // assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all
+ // bytes are copied out).
+ packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd,
// Only allocate a buffer for zerodecoding if the packet is zerocoded
((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
}
@@ -748,11 +953,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return; // Drop short packet
}
- catch(Exception e)
+ catch (Exception e)
{
if (m_malformedCount < 100)
m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString());
+
m_malformedCount++;
+
if ((m_malformedCount % 100000) == 0)
m_log.DebugFormat("[LLUDPSERVER]: Received {0} malformed packets so far, probable network attack.", m_malformedCount);
}
@@ -772,7 +979,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// If there is already a client for this endpoint, don't process UseCircuitCode
IClientAPI client = null;
- if (!m_scene.TryGetClient(address, out client))
+ if (!m_scene.TryGetClient(endPoint, out client) || !(client is LLClientView))
{
// UseCircuitCode handling
if (packet.Type == PacketType.UseCircuitCode)
@@ -780,13 +987,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// And if there is a UseCircuitCode pending, also drop it
lock (m_pendingCache)
{
- if (m_pendingCache.Contains(address))
+ if (m_pendingCache.Contains(endPoint))
return;
- m_pendingCache.AddOrUpdate(address, new Queue(), 60);
+ m_pendingCache.AddOrUpdate(endPoint, new Queue(), 60);
}
- object[] array = new object[] { buffer, packet };
+ // We need to copy the endpoint so that it doesn't get changed when another thread reuses the
+ // buffer.
+ object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
Util.FireAndForget(HandleUseCircuitCode, array);
@@ -798,7 +1007,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
lock (m_pendingCache)
{
Queue queue;
- if (m_pendingCache.TryGetValue(address, out queue))
+ if (m_pendingCache.TryGetValue(endPoint, out queue))
{
//m_log.DebugFormat("[LLUDPSERVER]: Enqueued a {0} packet into the pending queue", packet.Type);
queue.Enqueue(buffer);
@@ -834,6 +1043,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Handle appended ACKs
if (packet.Header.AppendedAcks && packet.Header.AckList != null)
{
+// m_log.DebugFormat(
+// "[LLUDPSERVER]: Handling {0} appended acks from {1} in {2}",
+// packet.Header.AckList.Length, client.Name, m_scene.Name);
+
for (int i = 0; i < packet.Header.AckList.Length; i++)
udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent);
}
@@ -843,6 +1056,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
PacketAckPacket ackPacket = (PacketAckPacket)packet;
+// m_log.DebugFormat(
+// "[LLUDPSERVER]: Handling {0} packet acks for {1} in {2}",
+// ackPacket.Packets.Length, client.Name, m_scene.Name);
+
for (int i = 0; i < ackPacket.Packets.Length; i++)
udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent);
@@ -856,6 +1073,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (packet.Header.Reliable)
{
+// m_log.DebugFormat(
+// "[LLUDPSERVER]: Adding ack request for {0} {1} from {2} in {3}",
+// packet.Type, packet.Header.Sequence, client.Name, m_scene.Name);
+
udpClient.PendingAcks.Enqueue(packet.Header.Sequence);
// This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out,
@@ -902,6 +1123,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (packet.Type == PacketType.StartPingCheck)
{
+// m_log.DebugFormat("[LLUDPSERVER]: Handling ping from {0} in {1}", client.Name, m_scene.Name);
+
// We don't need to do anything else with ping checks
StartPingCheckPacket startPing = (StartPingCheckPacket)packet;
CompletePing(udpClient, startPing.PingID.PingID);
@@ -921,13 +1144,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#endregion Ping Check Handling
+ IncomingPacket incomingPacket;
+
// Inbox insertion
- if (packet.Type == PacketType.AgentUpdate ||
- packet.Type == PacketType.ChatFromViewer)
- packetInbox.EnqueueHigh(new IncomingPacket((LLClientView)client, packet));
+ if (UsePools)
+ {
+ incomingPacket = m_incomingPacketPool.GetObject();
+ incomingPacket.Client = (LLClientView)client;
+ incomingPacket.Packet = packet;
+ }
else
- packetInbox.EnqueueLow(new IncomingPacket((LLClientView)client, packet));
-// packetInbox.Enqueue(new IncomingPacket((LLClientView)client, packet));
+ {
+ incomingPacket = new IncomingPacket((LLClientView)client, packet);
+ }
+
+ if (incomingPacket.Packet.Type == PacketType.AgentUpdate ||
+ incomingPacket.Packet.Type == PacketType.ChatFromViewer)
+ packetInbox.EnqueueHigh(incomingPacket);
+ else
+ packetInbox.EnqueueLow(incomingPacket);
}
#region BinaryStats
@@ -1013,21 +1248,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private void HandleUseCircuitCode(object o)
{
- IPEndPoint remoteEndPoint = null;
+ IPEndPoint endPoint = null;
IClientAPI client = null;
try
{
// DateTime startTime = DateTime.Now;
object[] array = (object[])o;
- UDPPacketBuffer buffer = (UDPPacketBuffer)array[0];
+ endPoint = (IPEndPoint)array[0];
UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1];
m_log.DebugFormat(
"[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}",
- uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, buffer.RemoteEndPoint);
-
- remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint;
+ uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, endPoint);
AuthenticateResponse sessionInfo;
if (IsClientAuthorized(uccp, out sessionInfo))
@@ -1038,13 +1271,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
uccp.CircuitCode.Code,
uccp.CircuitCode.ID,
uccp.CircuitCode.SessionID,
- remoteEndPoint,
+ endPoint,
sessionInfo);
// Send ack straight away to let the viewer know that the connection is active.
// The client will be null if it already exists (e.g. if on a region crossing the client sends a use
// circuit code to the existing child agent. This is not particularly obvious.
- SendAckImmediate(remoteEndPoint, uccp.Header.Sequence);
+ SendAckImmediate(endPoint, uccp.Header.Sequence);
// We only want to send initial data to new clients, not ones which are being converted from child to root.
if (client != null)
@@ -1058,12 +1291,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
lock (m_pendingCache)
{
- if (!m_pendingCache.TryGetValue(remoteEndPoint, out queue))
+ if (!m_pendingCache.TryGetValue(endPoint, out queue))
{
m_log.DebugFormat("[LLUDPSERVER]: Client created but no pending queue present");
return;
}
- m_pendingCache.Remove(remoteEndPoint);
+ m_pendingCache.Remove(endPoint);
}
m_log.DebugFormat("[LLUDPSERVER]: Client created, processing pending queue, {0} entries", queue.Count);
@@ -1081,9 +1314,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Don't create clients for unauthorized requesters.
m_log.WarnFormat(
"[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}",
- uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, remoteEndPoint);
+ uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, endPoint);
lock (m_pendingCache)
- m_pendingCache.Remove(remoteEndPoint);
+ m_pendingCache.Remove(endPoint);
}
// m_log.DebugFormat(
@@ -1095,7 +1328,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
m_log.ErrorFormat(
"[LLUDPSERVER]: UseCircuitCode handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}",
- remoteEndPoint != null ? remoteEndPoint.ToString() : "n/a",
+ endPoint != null ? endPoint.ToString() : "n/a",
client != null ? client.Name : "unknown",
client != null ? client.AgentId.ToString() : "unknown",
e.Message,
@@ -1160,20 +1393,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
IClientAPI client = null;
- // In priciple there shouldn't be more than one thread here, ever.
- // But in case that happens, we need to synchronize this piece of code
- // because it's too important
- lock (this)
+ // We currently synchronize this code across the whole scene to avoid issues such as
+ // http://opensimulator.org/mantis/view.php?id=5365 However, once locking per agent circuit can be done
+ // consistently, this lock could probably be removed.
+ lock (this)
{
if (!m_scene.TryGetClient(agentID, out client))
{
LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO);
-
+
client = new LLClientView(m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode);
client.OnLogout += LogoutHandler;
-
+
((LLClientView)client).DisableFacelights = m_disableFacelights;
-
+
client.Start();
}
}
@@ -1212,7 +1445,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// on to en-US to avoid number parsing issues
Culture.SetCurrentCulture();
- while (base.IsRunning)
+ while (IsRunningInbound)
{
m_scene.ThreadAlive(1);
try
@@ -1228,7 +1461,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
if (packetInbox.Dequeue(100, ref incomingPacket))
+ {
ProcessInPacket(incomingPacket);//, incomingPacket); Util.FireAndForget(ProcessInPacket, incomingPacket);
+
+ if (UsePools)
+ m_incomingPacketPool.ReturnObject(incomingPacket);
+ }
}
catch (Exception ex)
{
@@ -1255,7 +1493,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Action generic every round
Action clientPacketHandler = ClientOutgoingPacketHandler;
- while (base.IsRunning)
+ while (base.IsRunningOutbound)
{
m_scene.ThreadAlive(2);
try
@@ -1523,7 +1761,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (!client.IsLoggingOut)
{
client.IsLoggingOut = true;
- client.Close(false);
+ client.Close(false, false);
}
}
}
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
index cfe7c9d09d..8bd346149e 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
@@ -30,6 +30,8 @@ using System.Net;
using System.Net.Sockets;
using System.Threading;
using log4net;
+using OpenSim.Framework;
+using OpenSim.Framework.Monitoring;
namespace OpenMetaverse
{
@@ -58,17 +60,31 @@ namespace OpenMetaverse
/// Flag to process packets asynchronously or synchronously
private bool m_asyncPacketHandling;
- /// The all important shutdown flag
- private volatile bool m_shutdownFlag = true;
+ ///
+ /// Pool to use for handling data. May be null if UsePools = false;
+ ///
+ protected OpenSim.Framework.Pool m_pool;
- /// Returns true if the server is currently listening, otherwise false
- public bool IsRunning { get { return !m_shutdownFlag; } }
+ ///
+ /// Are we to use object pool(s) to reduce memory churn when receiving data?
+ ///
+ public bool UsePools { get; protected set; }
+
+ /// Returns true if the server is currently listening for inbound packets, otherwise false
+ public bool IsRunningInbound { get; private set; }
+
+ /// Returns true if the server is currently sending outbound packets, otherwise false
+ /// If IsRunningOut = false, then any request to send a packet is simply dropped.
+ public bool IsRunningOutbound { get; private set; }
+
+ private Stat m_poolCountStat;
///
/// Default constructor
///
/// Local IP address to bind the server to
/// Port to listening for incoming UDP packets on
+ /// /// Are we to use an object pool to get objects for handing inbound data?
public OpenSimUDPBase(IPAddress bindAddress, int port)
{
m_localBindAddress = bindAddress;
@@ -76,7 +92,7 @@ namespace OpenMetaverse
}
///
- /// Start the UDP server
+ /// Start inbound UDP packet handling.
///
/// The size of the receive buffer for
/// the UDP socket. This value is passed up to the operating system
@@ -91,11 +107,11 @@ namespace OpenMetaverse
/// manner (not throwing an exception when the remote side resets the
/// connection). This call is ignored on Mono where the flag is not
/// necessary
- public void Start(int recvBufferSize, bool asyncPacketHandling)
+ public void StartInbound(int recvBufferSize, bool asyncPacketHandling)
{
m_asyncPacketHandling = asyncPacketHandling;
- if (m_shutdownFlag)
+ if (!IsRunningInbound)
{
const int SIO_UDP_CONNRESET = -1744830452;
@@ -123,8 +139,7 @@ namespace OpenMetaverse
m_udpSocket.Bind(ipep);
- // we're not shutting down, we're starting up
- m_shutdownFlag = false;
+ IsRunningInbound = true;
// kick off an async receive. The Start() method will return, the
// actual receives will occur asynchronously and will be caught in
@@ -134,28 +149,84 @@ namespace OpenMetaverse
}
///
- /// Stops the UDP server
+ /// Start outbound UDP packet handling.
///
- public void Stop()
+ public void StartOutbound()
{
- if (!m_shutdownFlag)
+ IsRunningOutbound = true;
+ }
+
+ public void StopInbound()
+ {
+ if (IsRunningInbound)
{
// wait indefinitely for a writer lock. Once this is called, the .NET runtime
// will deny any more reader locks, in effect blocking all other send/receive
- // threads. Once we have the lock, we set shutdownFlag to inform the other
+ // threads. Once we have the lock, we set IsRunningInbound = false to inform the other
// threads that the socket is closed.
- m_shutdownFlag = true;
+ IsRunningInbound = false;
m_udpSocket.Close();
}
}
+ public void StopOutbound()
+ {
+ IsRunningOutbound = false;
+ }
+
+ protected virtual bool EnablePools()
+ {
+ if (!UsePools)
+ {
+ m_pool = new Pool(() => new UDPPacketBuffer(), 500);
+
+ m_poolCountStat
+ = new Stat(
+ "UDPPacketBufferPoolCount",
+ "Objects within the UDPPacketBuffer pool",
+ "The number of objects currently stored within the UDPPacketBuffer pool",
+ "",
+ "clientstack",
+ "packetpool",
+ StatType.Pull,
+ stat => stat.Value = m_pool.Count,
+ StatVerbosity.Debug);
+
+ StatsManager.RegisterStat(m_poolCountStat);
+
+ UsePools = true;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ protected virtual bool DisablePools()
+ {
+ if (UsePools)
+ {
+ UsePools = false;
+ StatsManager.DeregisterStat(m_poolCountStat);
+
+ // We won't null out the pool to avoid a race condition with code that may be in the middle of using it.
+
+ return true;
+ }
+
+ return false;
+ }
+
private void AsyncBeginReceive()
{
- // allocate a packet buffer
- //WrappedObject wrappedBuffer = Pool.CheckOut();
- UDPPacketBuffer buf = new UDPPacketBuffer();
+ UDPPacketBuffer buf;
- if (!m_shutdownFlag)
+ if (UsePools)
+ buf = m_pool.GetObject();
+ else
+ buf = new UDPPacketBuffer();
+
+ if (IsRunningInbound)
{
try
{
@@ -208,7 +279,7 @@ namespace OpenMetaverse
{
// Asynchronous receive operations will complete here through the call
// to AsyncBeginReceive
- if (!m_shutdownFlag)
+ if (IsRunningInbound)
{
// Asynchronous mode will start another receive before the
// callback for this packet is even fired. Very parallel :-)
@@ -217,8 +288,6 @@ namespace OpenMetaverse
// get the buffer that was created in AsyncBeginReceive
// this is the received data
- //WrappedObject wrappedBuffer = (WrappedObject)iar.AsyncState;
- //UDPPacketBuffer buffer = wrappedBuffer.Instance;
UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
try
@@ -235,7 +304,8 @@ namespace OpenMetaverse
catch (ObjectDisposedException) { }
finally
{
- //wrappedBuffer.Dispose();
+ if (UsePools)
+ m_pool.ReturnObject(buffer);
// Synchronous mode waits until the packet callback completes
// before starting the receive to fetch another packet
@@ -248,7 +318,7 @@ namespace OpenMetaverse
public void AsyncBeginSend(UDPPacketBuffer buf)
{
- if (!m_shutdownFlag)
+ if (IsRunningOutbound)
{
try
{
diff --git a/OpenSim/Framework/PacketPool.cs b/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs
similarity index 71%
rename from OpenSim/Framework/PacketPool.cs
rename to OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs
index 41d17c54f2..9f22fb48ec 100644
--- a/OpenSim/Framework/PacketPool.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs
@@ -31,10 +31,10 @@ using System.Reflection;
using OpenMetaverse;
using OpenMetaverse.Packets;
using log4net;
+using OpenSim.Framework.Monitoring;
-namespace OpenSim.Framework
+namespace OpenSim.Region.ClientStack.LindenUDP
{
-
public sealed class PacketPool
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@@ -44,14 +44,32 @@ namespace OpenSim.Framework
private bool packetPoolEnabled = true;
private bool dataBlockPoolEnabled = true;
+ private PercentageStat m_packetsReusedStat = new PercentageStat(
+ "PacketsReused",
+ "Packets reused",
+ "Number of packets reused out of all requests to the packet pool",
+ "clientstack",
+ "packetpool",
+ StatType.Push,
+ null,
+ StatVerbosity.Debug);
+
+ private PercentageStat m_blocksReusedStat = new PercentageStat(
+ "PacketDataBlocksReused",
+ "Packet data blocks reused",
+ "Number of data blocks reused out of all requests to the packet pool",
+ "clientstack",
+ "packetpool",
+ StatType.Push,
+ null,
+ StatVerbosity.Debug);
+
+ ///
+ /// Pool of packets available for reuse.
+ ///
private readonly Dictionary> pool = new Dictionary>();
- private static Dictionary> DataBlocks =
- new Dictionary>();
-
- static PacketPool()
- {
- }
+ private static Dictionary> DataBlocks = new Dictionary>();
public static PacketPool Instance
{
@@ -70,8 +88,45 @@ namespace OpenSim.Framework
get { return dataBlockPoolEnabled; }
}
+ private PacketPool()
+ {
+ StatsManager.RegisterStat(m_packetsReusedStat);
+ StatsManager.RegisterStat(m_blocksReusedStat);
+
+ StatsManager.RegisterStat(
+ new Stat(
+ "PacketsPoolCount",
+ "Objects within the packet pool",
+ "The number of objects currently stored within the packet pool",
+ "",
+ "clientstack",
+ "packetpool",
+ StatType.Pull,
+ stat => { lock (pool) { stat.Value = pool.Count; } },
+ StatVerbosity.Debug));
+
+ StatsManager.RegisterStat(
+ new Stat(
+ "PacketDataBlocksPoolCount",
+ "Objects within the packet data block pool",
+ "The number of objects currently stored within the packet data block pool",
+ "",
+ "clientstack",
+ "packetpool",
+ StatType.Pull,
+ stat => { lock (DataBlocks) { stat.Value = DataBlocks.Count; } },
+ StatVerbosity.Debug));
+ }
+
+ ///
+ /// Gets a packet of the given type.
+ ///
+ ///
+ /// Guaranteed to always return a packet, whether from the pool or newly constructed.
public Packet GetPacket(PacketType type)
{
+ m_packetsReusedStat.Consequent++;
+
Packet packet;
if (!packetPoolEnabled)
@@ -81,13 +136,19 @@ namespace OpenSim.Framework
{
if (!pool.ContainsKey(type) || pool[type] == null || (pool[type]).Count == 0)
{
+// m_log.DebugFormat("[PACKETPOOL]: Building {0} packet", type);
+
// Creating a new packet if we cannot reuse an old package
packet = Packet.BuildPacket(type);
}
else
{
+// m_log.DebugFormat("[PACKETPOOL]: Pulling {0} packet", type);
+
// Recycle old packages
- packet = (pool[type]).Pop();
+ m_packetsReusedStat.Antecedent++;
+
+ packet = pool[type].Pop();
}
}
@@ -136,7 +197,7 @@ namespace OpenSim.Framework
{
PacketType type = GetType(bytes);
- Array.Clear(zeroBuffer, 0, zeroBuffer.Length);
+// Array.Clear(zeroBuffer, 0, zeroBuffer.Length);
int i = 0;
Packet packet = GetPacket(type);
@@ -183,6 +244,7 @@ namespace OpenSim.Framework
switch (packet.Type)
{
// List pooling packets here
+ case PacketType.AgentUpdate:
case PacketType.PacketAck:
case PacketType.ObjectUpdate:
case PacketType.ImprovedTerseObjectUpdate:
@@ -197,7 +259,9 @@ namespace OpenSim.Framework
if ((pool[type]).Count < 50)
{
- (pool[type]).Push(packet);
+// m_log.DebugFormat("[PACKETPOOL]: Pushing {0} packet", type);
+
+ pool[type].Push(packet);
}
}
break;
@@ -209,16 +273,21 @@ namespace OpenSim.Framework
}
}
- public static T GetDataBlock() where T: new()
+ public T GetDataBlock() where T: new()
{
lock (DataBlocks)
{
+ m_blocksReusedStat.Consequent++;
+
Stack
[TestFixture]
- public class BasicCircuitTests
+ public class BasicCircuitTests : OpenSimTestCase
{
private Scene m_scene;
private TestLLUDPServer m_udpServer;
@@ -65,8 +65,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
}
[SetUp]
- public void SetUp()
+ public override void SetUp()
{
+ base.SetUp();
m_scene = new SceneHelpers().SetupScene();
}
@@ -143,7 +144,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
public void TestAddClient()
{
TestHelpers.InMethod();
-// XmlConfigurator.Configure();
+// TestHelpers.EnableLogging();
AddUdpServer();
diff --git a/OpenSim/Region/ClientStack/RegionApplicationBase.cs b/OpenSim/Region/ClientStack/RegionApplicationBase.cs
index 4672f8aa8a..853b72d9e7 100644
--- a/OpenSim/Region/ClientStack/RegionApplicationBase.cs
+++ b/OpenSim/Region/ClientStack/RegionApplicationBase.cs
@@ -76,7 +76,7 @@ namespace OpenSim.Region.ClientStack
protected override void StartupSpecific()
{
- SceneManager = new SceneManager();
+ SceneManager = SceneManager.Instance;
m_clientStackManager = CreateClientStackManager();
Initialize();
diff --git a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs
index 8a4fd8f041..da1ff2ecbb 100644
--- a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs
+++ b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs
@@ -57,39 +57,36 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
}
///
- /// Return a xfer uploader if one does not already exist.
+ /// Return the xfer uploader for the given transaction.
///
+ ///
+ /// If an uploader does not already exist for this transaction then it is created, otherwise the existing
+ /// uploader is returned.
+ ///
///
- ///
- /// We must transfer the new asset ID into the uploader on creation, otherwise
- /// we can see race conditions with other threads which can retrieve an item before it is updated with the new
- /// asset id.
- ///
- ///
- /// The xfer uploader requested. Null if one is already in existence.
- /// FIXME: This is a bizarre thing to do, and is probably meant to signal an error condition if multiple
- /// transfers are made. Needs to be corrected.
- ///
- public AssetXferUploader RequestXferUploader(UUID transactionID, UUID assetID)
+ /// The asset xfer uploader
+ public AssetXferUploader RequestXferUploader(UUID transactionID)
{
+ AssetXferUploader uploader;
+
lock (XferUploaders)
{
if (!XferUploaders.ContainsKey(transactionID))
{
- AssetXferUploader uploader = new AssetXferUploader(this, m_Scene, assetID, m_dumpAssetsToFile);
+ uploader = new AssetXferUploader(this, m_Scene, transactionID, m_dumpAssetsToFile);
// m_log.DebugFormat(
// "[AGENT ASSETS TRANSACTIONS]: Adding asset xfer uploader {0} since it didn't previously exist", transactionID);
XferUploaders.Add(transactionID, uploader);
-
- return uploader;
+ }
+ else
+ {
+ uploader = XferUploaders[transactionID];
}
}
- m_log.WarnFormat("[AGENT ASSETS TRANSACTIONS]: Ignoring request for asset xfer uploader {0} since it already exists", transactionID);
-
- return null;
+ return uploader;
}
public void HandleXfer(ulong xferID, uint packetID, byte[] data)
@@ -151,117 +148,30 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
string description, string name, sbyte invType,
sbyte type, byte wearableType, uint nextOwnerMask)
{
- AssetXferUploader uploader = null;
+ AssetXferUploader uploader = RequestXferUploader(transactionID);
- lock (XferUploaders)
- {
- if (XferUploaders.ContainsKey(transactionID))
- uploader = XferUploaders[transactionID];
- }
+ uploader.RequestCreateInventoryItem(
+ remoteClient, folderID, callbackID,
+ description, name, invType, type, wearableType, nextOwnerMask);
- if (uploader != null)
- {
- uploader.RequestCreateInventoryItem(
- remoteClient, transactionID, folderID,
- callbackID, description, name, invType, type,
- wearableType, nextOwnerMask);
-
- return true;
- }
-
- return false;
- }
-
- ///
- /// Get an uploaded asset. If the data is successfully retrieved,
- /// the transaction will be removed.
- ///
- ///
- /// The asset if the upload has completed, null if it has not.
- private AssetBase GetTransactionAsset(UUID transactionID)
- {
- lock (XferUploaders)
- {
- if (XferUploaders.ContainsKey(transactionID))
- {
- AssetXferUploader uploader = XferUploaders[transactionID];
- AssetBase asset = uploader.GetAssetData();
- RemoveXferUploader(transactionID);
-
- return asset;
- }
- }
-
- return null;
+ return true;
}
public void RequestUpdateTaskInventoryItem(IClientAPI remoteClient,
SceneObjectPart part, UUID transactionID,
TaskInventoryItem item)
{
- AssetXferUploader uploader = null;
+ AssetXferUploader uploader = RequestXferUploader(transactionID);
- lock (XferUploaders)
- {
- if (XferUploaders.ContainsKey(transactionID))
- uploader = XferUploaders[transactionID];
- }
-
- if (uploader != null)
- {
- AssetBase asset = GetTransactionAsset(transactionID);
-
- // Only legacy viewers use this, and they prefer CAPS, which
- // we have, so this really never runs.
- // Allow it, but only for "safe" types.
- if ((InventoryType)item.InvType != InventoryType.Notecard &&
- (InventoryType)item.InvType != InventoryType.LSL)
- return;
-
- if (asset != null)
- {
-// m_log.DebugFormat(
-// "[AGENT ASSETS TRANSACTIONS]: Updating item {0} in {1} for transaction {2}",
-// item.Name, part.Name, transactionID);
-
- asset.FullID = UUID.Random();
- asset.Name = item.Name;
- asset.Description = item.Description;
- asset.Type = (sbyte)item.Type;
- item.AssetID = asset.FullID;
-
- m_Scene.AssetService.Store(asset);
- }
- }
- else
- {
- m_log.ErrorFormat(
- "[AGENT ASSET TRANSACTIONS]: Could not find uploader with transaction ID {0} when handling request to update task inventory item {1} in {2}",
- transactionID, item.Name, part.Name);
- }
+ uploader.RequestUpdateTaskInventoryItem(remoteClient, item);
}
public void RequestUpdateInventoryItem(IClientAPI remoteClient,
UUID transactionID, InventoryItemBase item)
{
- AssetXferUploader uploader = null;
+ AssetXferUploader uploader = RequestXferUploader(transactionID);
- lock (XferUploaders)
- {
- if (XferUploaders.ContainsKey(transactionID))
- uploader = XferUploaders[transactionID];
- }
-
- if (uploader != null)
- {
- uploader.RequestUpdateInventoryItem(remoteClient, transactionID, item);
- }
- else
- {
- m_log.ErrorFormat(
- "[AGENT ASSET TRANSACTIONS]: Could not find uploader with transaction ID {0} when handling request to update inventory item {1} for {2}",
- transactionID, item.Name, remoteClient.Name);
- }
+ uploader.RequestUpdateInventoryItem(remoteClient, item);
}
}
}
diff --git a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetTransactionModule.cs b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetTransactionModule.cs
index 441c4ff405..4bb8986e08 100644
--- a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetTransactionModule.cs
+++ b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetTransactionModule.cs
@@ -215,7 +215,7 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
IClientAPI remoteClient, SceneObjectPart part, UUID transactionID, TaskInventoryItem item)
{
m_log.DebugFormat(
- "[TRANSACTIONS MANAGER] Called HandleTaskItemUpdateFromTransaction with item {0} in {1} for {2} in {3}",
+ "[ASSET TRANSACTION MODULE] Called HandleTaskItemUpdateFromTransaction with item {0} in {1} for {2} in {3}",
item.Name, part.Name, remoteClient.Name, m_Scene.RegionInfo.RegionName);
AgentAssetTransactions transactions =
@@ -274,13 +274,8 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
}
AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId);
- AssetXferUploader uploader = transactions.RequestXferUploader(transaction, assetID);
-
- if (uploader != null)
- {
- uploader.Initialise(remoteClient, assetID, transaction, type,
- data, storeLocal, tempFile);
- }
+ AssetXferUploader uploader = transactions.RequestXferUploader(transaction);
+ uploader.StartUpload(remoteClient, assetID, transaction, type, data, storeLocal, tempFile);
}
///
diff --git a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs
index 4cedfe6154..f6dd5af2d3 100644
--- a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs
+++ b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs
@@ -48,40 +48,76 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
};
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+ ///
+ /// Upload state.
+ ///
+ ///
+ /// New -> Uploading -> Complete
+ ///
+ private enum UploadState
+ {
+ New,
+ Uploading,
+ Complete
+ }
+
///
/// Reference to the object that holds this uploader. Used to remove ourselves from it's list if we
/// are performing a delayed update.
///
AgentAssetTransactions m_transactions;
+ private UploadState m_uploadState = UploadState.New;
+
private AssetBase m_asset;
private UUID InventFolder = UUID.Zero;
private sbyte invType = 0;
- private bool m_createItem = false;
- private uint m_createItemCallback = 0;
- private bool m_updateItem = false;
+ private bool m_createItem;
+ private uint m_createItemCallback;
+
+ private bool m_updateItem;
private InventoryItemBase m_updateItemData;
+ private bool m_updateTaskItem;
+ private TaskInventoryItem m_updateTaskItemData;
+
private string m_description = String.Empty;
private bool m_dumpAssetToFile;
- private bool m_finished = false;
private string m_name = String.Empty;
- private bool m_storeLocal;
+// private bool m_storeLocal;
private uint nextPerm = 0;
private IClientAPI ourClient;
- private UUID TransactionID = UUID.Zero;
+
+ private UUID m_transactionID;
+
private sbyte type = 0;
private byte wearableType = 0;
private byte[] m_oldData = null;
public ulong XferID;
private Scene m_Scene;
- public AssetXferUploader(AgentAssetTransactions transactions, Scene scene, UUID assetID, bool dumpAssetToFile)
+ ///
+ /// AssetXferUploader constructor
+ ///
+ /// /param>
+ ///
+ ///
+ ///
+ /// If true then when the asset is uploaded it is dumped to a file with the format
+ /// String.Format("{6}_{7}_{0:d2}{1:d2}{2:d2}_{3:d2}{4:d2}{5:d2}.dat",
+ /// now.Year, now.Month, now.Day, now.Hour, now.Minute,
+ /// now.Second, m_asset.Name, m_asset.Type);
+ /// for debugging purposes.
+ ///
+ public AssetXferUploader(
+ AgentAssetTransactions transactions, Scene scene, UUID transactionID, bool dumpAssetToFile)
{
+ m_asset = new AssetBase();
+
m_transactions = transactions;
+ m_transactionID = transactionID;
m_Scene = scene;
- m_asset = new AssetBase() { FullID = assetID };
m_dumpAssetToFile = dumpAssetToFile;
}
@@ -127,30 +163,50 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
}
///
- /// Initialise asset transfer from the client
+ /// Start asset transfer from the client
///
- ///
- ///
- ///
- public void Initialise(IClientAPI remoteClient, UUID assetID,
- UUID transaction, sbyte type, byte[] data, bool storeLocal,
- bool tempFile)
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Optional data. If present then the asset is created immediately with this data
+ /// rather than requesting an upload from the client. The data must be longer than 2 bytes.
+ ///
+ ///
+ ///
+ public void StartUpload(
+ IClientAPI remoteClient, UUID assetID, UUID transaction, sbyte type, byte[] data, bool storeLocal,
+ bool tempFile)
{
// m_log.DebugFormat(
// "[ASSET XFER UPLOADER]: Initialised xfer from {0}, asset {1}, transaction {2}, type {3}, storeLocal {4}, tempFile {5}, already received data length {6}",
// remoteClient.Name, assetID, transaction, type, storeLocal, tempFile, data.Length);
+ lock (this)
+ {
+ if (m_uploadState != UploadState.New)
+ {
+ m_log.WarnFormat(
+ "[ASSET XFER UPLOADER]: Tried to start upload of asset {0}, transaction {1} for {2} but this is already in state {3}. Aborting.",
+ assetID, transaction, remoteClient.Name, m_uploadState);
+
+ return;
+ }
+
+ m_uploadState = UploadState.Uploading;
+ }
+
ourClient = remoteClient;
- m_asset.Name = "blank";
- m_asset.Description = "empty";
+
+ m_asset.FullID = assetID;
m_asset.Type = type;
m_asset.CreatorID = remoteClient.AgentId.ToString();
m_asset.Data = data;
m_asset.Local = storeLocal;
m_asset.Temporary = tempFile;
- TransactionID = transaction;
- m_storeLocal = storeLocal;
+// m_storeLocal = storeLocal;
if (m_asset.Data.Length > 2)
{
@@ -175,36 +231,35 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
protected void SendCompleteMessage()
{
- ourClient.SendAssetUploadCompleteMessage(m_asset.Type, true,
- m_asset.FullID);
-
// We must lock in order to avoid a race with a separate thread dealing with an inventory item or create
// message from other client UDP.
lock (this)
{
- m_finished = true;
+ m_uploadState = UploadState.Complete;
+
+ ourClient.SendAssetUploadCompleteMessage(m_asset.Type, true, m_asset.FullID);
+
if (m_createItem)
{
- DoCreateItem(m_createItemCallback);
+ CompleteCreateItem(m_createItemCallback);
}
else if (m_updateItem)
{
- StoreAssetForItemUpdate(m_updateItemData);
-
- // Remove ourselves from the list of transactions if completion was delayed until the transaction
- // was complete.
- // TODO: Should probably do the same for create item.
- m_transactions.RemoveXferUploader(TransactionID);
+ CompleteItemUpdate(m_updateItemData);
}
- else if (m_storeLocal)
+ else if (m_updateTaskItem)
{
- m_Scene.AssetService.Store(m_asset);
+ CompleteTaskItemUpdate(m_updateTaskItemData);
}
+// else if (m_storeLocal)
+// {
+// m_Scene.AssetService.Store(m_asset);
+// }
}
m_log.DebugFormat(
"[ASSET XFER UPLOADER]: Uploaded asset {0} for transaction {1}",
- m_asset.FullID, TransactionID);
+ m_asset.FullID, m_transactionID);
if (m_dumpAssetToFile)
{
@@ -232,40 +287,37 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
}
public void RequestCreateInventoryItem(IClientAPI remoteClient,
- UUID transactionID, UUID folderID, uint callbackID,
+ UUID folderID, uint callbackID,
string description, string name, sbyte invType,
sbyte type, byte wearableType, uint nextOwnerMask)
{
- if (TransactionID == transactionID)
- {
- InventFolder = folderID;
- m_name = name;
- m_description = description;
- this.type = type;
- this.invType = invType;
- this.wearableType = wearableType;
- nextPerm = nextOwnerMask;
- m_asset.Name = name;
- m_asset.Description = description;
- m_asset.Type = type;
+ InventFolder = folderID;
+ m_name = name;
+ m_description = description;
+ this.type = type;
+ this.invType = invType;
+ this.wearableType = wearableType;
+ nextPerm = nextOwnerMask;
+ m_asset.Name = name;
+ m_asset.Description = description;
+ m_asset.Type = type;
- // We must lock to avoid a race with a separate thread uploading the asset.
- lock (this)
+ // We must lock to avoid a race with a separate thread uploading the asset.
+ lock (this)
+ {
+ if (m_uploadState == UploadState.Complete)
{
- if (m_finished)
- {
- DoCreateItem(callbackID);
- }
- else
- {
- m_createItem = true; //set flag so the inventory item is created when upload is complete
- m_createItemCallback = callbackID;
- }
+ CompleteCreateItem(callbackID);
+ }
+ else
+ {
+ m_createItem = true; //set flag so the inventory item is created when upload is complete
+ m_createItemCallback = callbackID;
}
}
}
- public void RequestUpdateInventoryItem(IClientAPI remoteClient, UUID transactionID, InventoryItemBase item)
+ public void RequestUpdateInventoryItem(IClientAPI remoteClient, InventoryItemBase item)
{
// We must lock to avoid a race with a separate thread uploading the asset.
lock (this)
@@ -280,9 +332,9 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
item.AssetID = m_asset.FullID;
m_Scene.InventoryService.UpdateItem(item);
- if (m_finished)
+ if (m_uploadState == UploadState.Complete)
{
- StoreAssetForItemUpdate(item);
+ CompleteItemUpdate(item);
}
else
{
@@ -296,20 +348,59 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
}
}
+ public void RequestUpdateTaskInventoryItem(IClientAPI remoteClient, TaskInventoryItem taskItem)
+ {
+ // We must lock to avoid a race with a separate thread uploading the asset.
+ lock (this)
+ {
+ m_asset.Name = taskItem.Name;
+ m_asset.Description = taskItem.Description;
+ m_asset.Type = (sbyte)taskItem.Type;
+ taskItem.AssetID = m_asset.FullID;
+
+ if (m_uploadState == UploadState.Complete)
+ {
+ CompleteTaskItemUpdate(taskItem);
+ }
+ else
+ {
+ m_updateTaskItem = true;
+ m_updateTaskItemData = taskItem;
+ }
+ }
+ }
+
///
- /// Store the asset for the given item.
+ /// Store the asset for the given item when it has been uploaded.
///
///
- private void StoreAssetForItemUpdate(InventoryItemBase item)
+ private void CompleteItemUpdate(InventoryItemBase item)
{
// m_log.DebugFormat(
// "[ASSET XFER UPLOADER]: Storing asset {0} for earlier item update for {1} for {2}",
// m_asset.FullID, item.Name, ourClient.Name);
m_Scene.AssetService.Store(m_asset);
+
+ m_transactions.RemoveXferUploader(m_transactionID);
}
- private void DoCreateItem(uint callbackID)
+ ///
+ /// Store the asset for the given task item when it has been uploaded.
+ ///
+ ///
+ private void CompleteTaskItemUpdate(TaskInventoryItem taskItem)
+ {
+// m_log.DebugFormat(
+// "[ASSET XFER UPLOADER]: Storing asset {0} for earlier task item update for {1} for {2}",
+// m_asset.FullID, taskItem.Name, ourClient.Name);
+
+ m_Scene.AssetService.Store(m_asset);
+
+ m_transactions.RemoveXferUploader(m_transactionID);
+ }
+
+ private void CompleteCreateItem(uint callbackID)
{
ValidateAssets();
m_Scene.AssetService.Store(m_asset);
@@ -339,6 +430,8 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
ourClient.SendInventoryItemCreateUpdate(item, callbackID);
else
ourClient.SendAlertMessage("Unable to create inventory item");
+
+ m_transactions.RemoveXferUploader(m_transactionID);
}
private void ValidateAssets()
@@ -416,7 +509,7 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
/// null if the asset has not finished uploading
public AssetBase GetAssetData()
{
- if (m_finished)
+ if (m_uploadState == UploadState.Complete)
{
ValidateAssets();
return m_asset;
@@ -469,4 +562,3 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
}
}
}
-
diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
index 7d7176fc07..d1a563cdce 100644
--- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
+++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
@@ -52,7 +52,7 @@ using OpenSim.Services.Interfaces;
[assembly: Addin("FlotsamAssetCache", "1.1")]
[assembly: AddinDependency("OpenSim", "0.5")]
-namespace Flotsam.RegionModules.AssetCache
+namespace OpenSim.Region.CoreModules.Asset
{
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
public class FlotsamAssetCache : ISharedRegionModule, IImprovedAssetCache, IAssetService
@@ -107,8 +107,6 @@ namespace Flotsam.RegionModules.AssetCache
private IAssetService m_AssetService;
private List m_Scenes = new List();
- private bool m_DeepScanBeforePurge;
-
public FlotsamAssetCache()
{
m_InvalidChars.AddRange(Path.GetInvalidPathChars());
@@ -170,8 +168,6 @@ namespace Flotsam.RegionModules.AssetCache
m_CacheDirectoryTierLen = assetConfig.GetInt("CacheDirectoryTierLength", m_CacheDirectoryTierLen);
m_CacheWarnAt = assetConfig.GetInt("CacheWarnAt", m_CacheWarnAt);
-
- m_DeepScanBeforePurge = assetConfig.GetBoolean("DeepScanBeforePurge", m_DeepScanBeforePurge);
}
m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory);
@@ -519,13 +515,10 @@ namespace Flotsam.RegionModules.AssetCache
// Purge all files last accessed prior to this point
DateTime purgeLine = DateTime.Now - m_FileExpiration;
- // An optional deep scan at this point will ensure assets present in scenes,
- // or referenced by objects in the scene, but not recently accessed
- // are not purged.
- if (m_DeepScanBeforePurge)
- {
- CacheScenes();
- }
+ // An asset cache may contain local non-temporary assets that are not in the asset service. Therefore,
+ // before cleaning up expired files we must scan the objects in the scene to make sure that we retain
+ // such local assets if they have not been recently accessed.
+ TouchAllSceneAssets(false);
foreach (string dir in Directory.GetDirectories(m_CacheDirectory))
{
@@ -718,11 +711,14 @@ namespace Flotsam.RegionModules.AssetCache
///
/// Iterates through all Scenes, doing a deep scan through assets
- /// to cache all assets present in the scene or referenced by assets
- /// in the scene
+ /// to update the access time of all assets present in the scene or referenced by assets
+ /// in the scene.
///
- ///
- private int CacheScenes()
+ ///
+ /// If true, then assets scanned which are not found in cache are added to the cache.
+ ///
+ /// Number of distinct asset references found in the scene.
+ private int TouchAllSceneAssets(bool storeUncached)
{
UuidGatherer gatherer = new UuidGatherer(m_AssetService);
@@ -745,7 +741,7 @@ namespace Flotsam.RegionModules.AssetCache
{
File.SetLastAccessTime(filename, DateTime.Now);
}
- else
+ else if (storeUncached)
{
m_AssetService.Get(assetID.ToString());
}
@@ -873,13 +869,14 @@ namespace Flotsam.RegionModules.AssetCache
break;
-
case "assets":
- m_log.Info("[FLOTSAM ASSET CACHE]: Caching all assets, in all scenes.");
+ m_log.Info("[FLOTSAM ASSET CACHE]: Ensuring assets are cached for all scenes.");
Util.FireAndForget(delegate {
- int assetsCached = CacheScenes();
- m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Completed Scene Caching, {0} assets found.", assetsCached);
+ int assetReferenceTotal = TouchAllSceneAssets(true);
+ m_log.InfoFormat(
+ "[FLOTSAM ASSET CACHE]: Completed check with {0} assets.",
+ assetReferenceTotal);
});
break;
diff --git a/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs b/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs
index c91b25fac2..1c2bfd0dff 100644
--- a/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs
+++ b/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs
@@ -35,7 +35,6 @@ using Nini.Config;
using NUnit.Framework;
using OpenMetaverse;
using OpenMetaverse.Assets;
-using Flotsam.RegionModules.AssetCache;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
index 951afd740d..acd156e855 100644
--- a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
@@ -285,6 +285,20 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
}
public bool AttachObject(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent, bool useAttachData, bool temp)
+ {
+ if (!Enabled)
+ return false;
+
+ if (AttachObjectInternal(sp, group, attachmentPt, silent, useAttachData, temp))
+ {
+ m_scene.EventManager.TriggerOnAttach(group.LocalId, group.FromItemID, sp.UUID);
+ return true;
+ }
+
+ return false;
+ }
+
+ private bool AttachObjectInternal(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent, bool useAttachData, bool temp)
{
lock (sp.AttachmentsSyncLock)
{
@@ -460,6 +474,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
}
public void DetachSingleAttachmentToGround(IScenePresence sp, uint soLocalId)
+ {
+ DetachSingleAttachmentToGround(sp, soLocalId, sp.AbsolutePosition, Quaternion.Identity);
+ }
+
+ public void DetachSingleAttachmentToGround(IScenePresence sp, uint soLocalId, Vector3 absolutePos, Quaternion absoluteRot)
{
if (!Enabled)
return;
@@ -502,7 +521,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
so.FromItemID = UUID.Zero;
SceneObjectPart rootPart = so.RootPart;
- so.AbsolutePosition = sp.AbsolutePosition;
+ so.AbsolutePosition = absolutePos;
+ if (absoluteRot != Quaternion.Identity)
+ {
+ so.UpdateGroupRotationR(absoluteRot);
+ }
so.AttachedAvatar = UUID.Zero;
rootPart.SetParentLocalId(0);
so.ClearPartAttachmentData();
@@ -616,9 +639,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
if (grp.HasGroupChanged)
{
-// m_log.DebugFormat(
-// "[ATTACHMENTS MODULE]: Updating asset for attachment {0}, attachpoint {1}",
-// grp.UUID, grp.AttachmentPoint);
+ m_log.DebugFormat(
+ "[ATTACHMENTS MODULE]: Updating asset for attachment {0}, attachpoint {1}",
+ grp.UUID, grp.AttachmentPoint);
string sceneObjectXml = SceneObjectSerializer.ToOriginalXmlFormat(grp, scriptedState);
@@ -862,7 +885,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
// This will throw if the attachment fails
try
{
- AttachObject(sp, objatt, attachmentPt, false, false, false);
+ AttachObjectInternal(sp, objatt, attachmentPt, false, false, false);
}
catch (Exception e)
{
@@ -933,6 +956,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
InventoryItemBase item = new InventoryItemBase(itemID, sp.UUID);
item = m_scene.InventoryService.GetItem(item);
+ if (item == null)
+ return;
+
bool changed = sp.Appearance.SetAttachment((int)AttachmentPt, itemID, item.AssetID);
if (changed && m_scene.AvatarFactory != null)
{
diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs
index d9a619d15b..4e9d3f9e57 100644
--- a/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs
@@ -62,7 +62,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
public class AttachmentsModuleTests : OpenSimTestCase
{
private AutoResetEvent m_chatEvent = new AutoResetEvent(false);
- private OSChatMessage m_osChatMessageReceived;
+// private OSChatMessage m_osChatMessageReceived;
+
+ // Used to test whether the operations have fired the attach event. Must be reset after each test.
+ private int m_numberOfAttachEventsFired;
[TestFixtureSetUp]
public void FixtureInit()
@@ -83,7 +86,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
{
// Console.WriteLine("Got chat [{0}]", oscm.Message);
- m_osChatMessageReceived = oscm;
+// m_osChatMessageReceived = oscm;
m_chatEvent.Set();
}
@@ -99,6 +102,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
"attachments-test-scene", TestHelpers.ParseTail(999), 1000, 1000, config);
SceneHelpers.SetupSceneModules(scene, config, modules.ToArray());
+ scene.EventManager.OnAttach += (localID, itemID, avatarID) => m_numberOfAttachEventsFired++;
+
return scene;
}
@@ -181,6 +186,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
+ m_numberOfAttachEventsFired = 0;
+
Scene scene = CreateTestScene();
UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1);
ScenePresence sp = SceneHelpers.AddScenePresence(scene, ua1);
@@ -189,6 +196,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, attName, sp.UUID);
+ m_numberOfAttachEventsFired = 0;
scene.AttachmentsModule.AttachObject(sp, so, (uint)AttachmentPoint.Chest, false, false, false);
// Check status on scene presence
@@ -216,7 +224,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
-// TestHelpers.DisableLogging();
+ // Check events
+ Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
}
///
@@ -228,6 +237,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
+ m_numberOfAttachEventsFired = 0;
+
Scene scene = CreateTestScene();
UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1);
ScenePresence sp = SceneHelpers.AddScenePresence(scene, ua1);
@@ -247,6 +258,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(sp.HasAttachments(), Is.False);
Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
+
+ // Check events
+ Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(0));
}
[Test]
@@ -261,6 +275,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
InventoryItemBase attItem = CreateAttachmentItem(scene, ua1.PrincipalID, "att", 0x10, 0x20);
+ m_numberOfAttachEventsFired = 0;
scene.AttachmentsModule.RezSingleAttachmentFromInventory(
sp, attItem.ID, (uint)AttachmentPoint.Chest);
@@ -280,6 +295,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(sp.Appearance.GetAttachpoint(attItem.ID), Is.EqualTo((int)AttachmentPoint.Chest));
Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
+
+ // Check events
+ Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
}
///
@@ -338,6 +356,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
ISceneEntity so
= scene.AttachmentsModule.RezSingleAttachmentFromInventory(
sp, attItem.ID, (uint)AttachmentPoint.Chest);
+
+ m_numberOfAttachEventsFired = 0;
scene.AttachmentsModule.DetachSingleAttachmentToGround(sp, so.LocalId);
// Check scene presence status
@@ -353,6 +373,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
// Check object in scene
Assert.That(scene.GetSceneObjectGroup("att"), Is.Not.Null);
+
+ // Check events
+ Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
}
[Test]
@@ -369,6 +392,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
SceneObjectGroup so
= (SceneObjectGroup)scene.AttachmentsModule.RezSingleAttachmentFromInventory(
sp, attItem.ID, (uint)AttachmentPoint.Chest);
+
+ m_numberOfAttachEventsFired = 0;
scene.AttachmentsModule.DetachSingleAttachmentToInv(sp, so);
// Check status on scene presence
@@ -380,6 +405,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(sp.Appearance.GetAttachpoint(attItem.ID), Is.EqualTo(0));
Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(0));
+
+ // Check events
+ Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
}
///
@@ -461,10 +489,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
SceneObjectGroup rezzedAtt = presence.GetAttachments()[0];
- scene.IncomingCloseAgent(presence.UUID);
+ m_numberOfAttachEventsFired = 0;
+ scene.IncomingCloseAgent(presence.UUID, false);
// Check that we can't retrieve this attachment from the scene.
Assert.That(scene.GetSceneObjectGroup(rezzedAtt.UUID), Is.Null);
+
+ // Check events
+ Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(0));
}
[Test]
@@ -480,6 +512,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
AgentCircuitData acd = SceneHelpers.GenerateAgentData(ua1.PrincipalID);
acd.Appearance = new AvatarAppearance();
acd.Appearance.SetAttachment((int)AttachmentPoint.Chest, attItem.ID, attItem.AssetID);
+
+ m_numberOfAttachEventsFired = 0;
ScenePresence presence = SceneHelpers.AddScenePresence(scene, acd);
Assert.That(presence.HasAttachments(), Is.True);
@@ -502,6 +536,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(presence.Appearance.GetAttachpoint(attItem.ID), Is.EqualTo((int)AttachmentPoint.Chest));
Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
+
+ // Check events. We expect OnAttach to fire on login.
+ Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
}
[Test]
@@ -522,10 +559,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Vector3 newPosition = new Vector3(1, 2, 4);
+ m_numberOfAttachEventsFired = 0;
scene.SceneGraph.UpdatePrimGroupPosition(attSo.LocalId, newPosition, sp.ControllingClient);
Assert.That(attSo.AbsolutePosition, Is.EqualTo(sp.AbsolutePosition));
Assert.That(attSo.RootPart.AttachedPos, Is.EqualTo(newPosition));
+
+ // Check events
+ Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(0));
}
[Test]
@@ -574,6 +615,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Vector3 teleportPosition = new Vector3(10, 11, 12);
Vector3 teleportLookAt = new Vector3(20, 21, 22);
+ m_numberOfAttachEventsFired = 0;
sceneA.RequestTeleportLocation(
beforeTeleportSp.ControllingClient,
sceneB.RegionInfo.RegionHandle,
@@ -616,29 +658,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
Assert.That(actualSceneAAttachments.Count, Is.EqualTo(0));
Assert.That(sceneA.GetSceneObjectGroups().Count, Is.EqualTo(0));
- }
- // I'm commenting this test because scene setup NEEDS InventoryService to
- // be non-null
- //[Test]
-// public void T032_CrossAttachments()
-// {
-// TestHelpers.InMethod();
-//
-// ScenePresence presence = scene.GetScenePresence(agent1);
-// ScenePresence presence2 = scene2.GetScenePresence(agent1);
-// presence2.AddAttachment(sog1);
-// presence2.AddAttachment(sog2);
-//
-// ISharedRegionModule serialiser = new SerialiserModule();
-// SceneHelpers.SetupSceneModules(scene, new IniConfigSource(), serialiser);
-// SceneHelpers.SetupSceneModules(scene2, new IniConfigSource(), serialiser);
-//
-// Assert.That(presence.HasAttachments(), Is.False, "Presence has attachments before cross");
-//
-// //Assert.That(presence2.CrossAttachmentsIntoNewRegion(region1, true), Is.True, "Cross was not successful");
-// Assert.That(presence2.HasAttachments(), Is.False, "Presence2 objects were not deleted");
-// Assert.That(presence.HasAttachments(), Is.True, "Presence has not received new objects");
-// }
+ // Check events
+ Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(0));
+ }
}
}
diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs
index 4cb4370212..e3bf997a5d 100644
--- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs
@@ -533,6 +533,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
// Ignore ruth's assets
if (appearance.Wearables[i][j].ItemID == AvatarWearable.DefaultWearables[i][0].ItemID)
continue;
+
InventoryItemBase baseItem = new InventoryItemBase(appearance.Wearables[i][j].ItemID, userID);
baseItem = invService.GetItem(baseItem);
diff --git a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs
index dbbb0aec7d..4407e40392 100644
--- a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs
@@ -197,6 +197,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat
string fromName = c.From;
string fromNamePrefix = "";
UUID fromID = UUID.Zero;
+ UUID ownerID = UUID.Zero;
string message = c.Message;
IScene scene = c.Scene;
UUID destination = c.Destination;
@@ -224,11 +225,16 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat
fromNamePrefix = m_adminPrefix;
}
destination = UUID.Zero; // Avatars cant "SayTo"
+ ownerID = c.Sender.AgentId;
+
break;
case ChatSourceType.Object:
fromID = c.SenderUUID;
+ if (c.SenderObject != null && c.SenderObject is SceneObjectPart)
+ ownerID = ((SceneObjectPart)c.SenderObject).OwnerID;
+
break;
}
@@ -262,8 +268,16 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat
// objects on a parcel with access restrictions
if (c.Sender == null || Presencecheck.IsEitherBannedOrRestricted(c.Sender.AgentId) != true)
{
- if (TrySendChatMessage(presence, fromPos, regionPos, fromID, fromNamePrefix + fromName, c.Type, message, sourceType))
- receiverIDs.Add(presence.UUID);
+ if (destination != UUID.Zero)
+ {
+ if (TrySendChatMessage(presence, fromPos, regionPos, fromID, ownerID, fromNamePrefix + fromName, c.Type, message, sourceType, true))
+ receiverIDs.Add(presence.UUID);
+ }
+ else
+ {
+ if (TrySendChatMessage(presence, fromPos, regionPos, fromID, ownerID, fromNamePrefix + fromName, c.Type, message, sourceType, false))
+ receiverIDs.Add(presence.UUID);
+ }
}
}
}
@@ -324,7 +338,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat
(((SceneObjectPart)c.SenderObject).OwnerID != client.AgentId))
return;
- client.SendChatMessage(c.Message, (byte)cType, CenterOfRegion, fromName, fromID,
+ client.SendChatMessage(c.Message, (byte)cType, CenterOfRegion, fromName, fromID, fromID,
(byte)sourceType, (byte)ChatAudibleLevel.Fully);
receiverIDs.Add(client.AgentId);
}
@@ -341,15 +355,20 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat
///
/// /param>
///
+ ///
+ /// Owner of the message. For at least some messages from objects, this has to be correctly filled with the owner's UUID.
+ /// This is the case for script error messages in viewer 3 since LLViewer change EXT-7762
+ ///
///
///
///
///
/// true if the message was sent to the receiver, false if it was not sent due to failing a
/// precondition
- protected virtual bool TrySendChatMessage(ScenePresence presence, Vector3 fromPos, Vector3 regionPos,
- UUID fromAgentID, string fromName, ChatTypeEnum type,
- string message, ChatSourceType src)
+ protected virtual bool TrySendChatMessage(
+ ScenePresence presence, Vector3 fromPos, Vector3 regionPos,
+ UUID fromAgentID, UUID ownerID, string fromName, ChatTypeEnum type,
+ string message, ChatSourceType src, bool ignoreDistance)
{
// don't send chat to child agents
if (presence.IsChildAgent) return false;
@@ -369,8 +388,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat
}
// TODO: should change so the message is sent through the avatar rather than direct to the ClientView
- presence.ControllingClient.SendChatMessage(message, (byte) type, fromPos, fromName,
- fromAgentID, (byte)src, (byte)ChatAudibleLevel.Fully);
+ presence.ControllingClient.SendChatMessage(
+ message, (byte) type, fromPos, fromName,
+ fromAgentID, ownerID, (byte)src, (byte)ChatAudibleLevel.Fully);
return true;
}
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/CallingCardModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/CallingCardModule.cs
index d942e8742a..5ec0ea94b4 100644
--- a/OpenSim/Region/CoreModules/Avatar/Friends/CallingCardModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Friends/CallingCardModule.cs
@@ -141,7 +141,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
client.FirstName+" "+client.LastName,
destID, (byte)211, false,
String.Empty,
- transactionID, false, new Vector3(), new byte[0]),
+ transactionID, false, new Vector3(), new byte[0], true),
delegate(bool success) {} );
}
}
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
index 24ec435d17..f1903c3e27 100644
--- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
@@ -28,6 +28,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using System.Threading;
using log4net;
@@ -482,9 +483,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
Util.FireAndForget(
delegate
{
- m_log.DebugFormat(
- "[FRIENDS MODULE]: Notifying {0} friends of {1} of online status {2}",
- friendList.Count, agentID, online);
+// m_log.DebugFormat(
+// "[FRIENDS MODULE]: Notifying {0} friends of {1} of online status {2}",
+// friendList.Count, agentID, online);
// Notify about this user status
StatusNotify(friendList, agentID, online);
@@ -495,42 +496,36 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
protected virtual void StatusNotify(List friendList, UUID userID, bool online)
{
- foreach (FriendInfo friend in friendList)
+ List friendStringIds = friendList.ConvertAll(friend => friend.Friend);
+ List remoteFriendStringIds = new List();
+ foreach (string friendStringId in friendStringIds)
{
- UUID friendID;
- if (UUID.TryParse(friend.Friend, out friendID))
+ UUID friendUuid;
+ if (UUID.TryParse(friendStringId, out friendUuid))
{
- // Try local
- if (LocalStatusNotification(userID, friendID, online))
+ if (LocalStatusNotification(userID, friendUuid, online))
continue;
- // The friend is not here [as root]. Let's forward.
- PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
- if (friendSessions != null && friendSessions.Length > 0)
- {
- PresenceInfo friendSession = null;
- foreach (PresenceInfo pinfo in friendSessions)
- {
- if (pinfo.RegionID != UUID.Zero) // let's guard against sessions-gone-bad
- {
- friendSession = pinfo;
- break;
- }
- }
-
- if (friendSession != null)
- {
- GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
- //m_log.DebugFormat("[FRIENDS]: Remote Notify to region {0}", region.RegionName);
- m_FriendsSimConnector.StatusNotify(region, userID, friendID, online);
- }
- }
-
- // Friend is not online. Ignore.
+ remoteFriendStringIds.Add(friendStringId);
}
else
{
- m_log.WarnFormat("[FRIENDS]: Error parsing friend ID {0}", friend.Friend);
+ m_log.WarnFormat("[FRIENDS]: Error parsing friend ID {0}", friendStringId);
+ }
+ }
+
+ // We do this regrouping so that we can efficiently send a single request rather than one for each
+ // friend in what may be a very large friends list.
+ PresenceInfo[] friendSessions = PresenceService.GetAgents(remoteFriendStringIds.ToArray());
+
+ foreach (PresenceInfo friendSession in friendSessions)
+ {
+ // let's guard against sessions-gone-bad
+ if (friendSession.RegionID != UUID.Zero)
+ {
+ GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
+ //m_log.DebugFormat("[FRIENDS]: Remote Notify to region {0}", region.RegionName);
+ m_FriendsSimConnector.StatusNotify(region, userID, friendSession.UserID, online);
}
}
}
diff --git a/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs b/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs
index 716cc69736..82816d918f 100644
--- a/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs
@@ -206,7 +206,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Gods
transferModule.SendInstantMessage(new GridInstantMessage(
m_scene, godID, "God", agentID, (byte)250, false,
Utils.BytesToString(reason), UUID.Zero, true,
- new Vector3(), new byte[] {(byte)kickflags}),
+ new Vector3(), new byte[] {(byte)kickflags}, true),
delegate(bool success) {} );
}
return;
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs
index 6587eadf57..d0e88f697f 100644
--- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs
@@ -166,7 +166,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
if (options.ContainsKey("verbose"))
m_log.InfoFormat(
- "[INVENTORY ARCHIVER]: Saving item {0} {1} with asset {2}",
+ "[INVENTORY ARCHIVER]: Saving item {0} {1} (asset UUID {2})",
inventoryItem.ID, inventoryItem.Name, inventoryItem.AssetID);
string filename = path + CreateArchiveItemName(inventoryItem);
@@ -337,11 +337,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
{
m_log.DebugFormat("[INVENTORY ARCHIVER]: Saving {0} assets for items", m_assetUuids.Count);
- new AssetsRequest(
- new AssetsArchiver(m_archiveWriter),
- m_assetUuids, m_scene.AssetService,
- m_scene.UserAccountService, m_scene.RegionInfo.ScopeID,
- options, ReceivedAllAssets).Execute();
+ AssetsRequest ar
+ = new AssetsRequest(
+ new AssetsArchiver(m_archiveWriter),
+ m_assetUuids, m_scene.AssetService,
+ m_scene.UserAccountService, m_scene.RegionInfo.ScopeID,
+ options, ReceivedAllAssets);
+
+ Util.FireAndForget(o => ar.Execute());
}
else
{
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs
index 7d1fe68e8c..765b960c75 100644
--- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs
@@ -35,6 +35,7 @@ using Nini.Config;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.Communications;
+using OpenSim.Framework.Console;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
@@ -209,6 +210,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
Guid id, string firstName, string lastName, string invPath, string pass, string savePath,
Dictionary options)
{
+// if (!ConsoleUtil.CheckFileDoesNotExist(MainConsole.Instance, savePath))
+// return false;
+
if (m_scenes.Count > 0)
{
UserAccount userInfo = GetUserInfo(firstName, lastName, pass);
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs
index 1056865590..00727a45a6 100644
--- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs
@@ -82,7 +82,25 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
protected string m_item1Name = "Ray Gun Item";
protected string m_coaItemName = "Coalesced Item";
-
+
+ [TestFixtureSetUp]
+ public void FixtureSetup()
+ {
+ // Don't allow tests to be bamboozled by asynchronous events. Execute everything on the same thread.
+ Util.FireAndForgetMethod = FireAndForgetMethod.RegressionTest;
+
+ ConstructDefaultIarBytesForTestLoad();
+ }
+
+ [TestFixtureTearDown]
+ public void TearDown()
+ {
+ // We must set this back afterwards, otherwise later tests will fail since they're expecting multiple
+ // threads. Possibly, later tests should be rewritten so none of them require async stuff (which regression
+ // tests really shouldn't).
+ Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod;
+ }
+
[SetUp]
public override void SetUp()
{
@@ -90,12 +108,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
m_iarStream = new MemoryStream(m_iarStreamBytes);
}
- [TestFixtureSetUp]
- public void FixtureSetup()
- {
- ConstructDefaultIarBytesForTestLoad();
- }
-
protected void ConstructDefaultIarBytesForTestLoad()
{
// log4net.Config.XmlConfigurator.Configure();
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs
index b112b6db52..06f6e49ab6 100644
--- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs
@@ -49,7 +49,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
{
[TestFixture]
public class InventoryArchiverTests : InventoryArchiveTestCase
- {
+ {
protected TestScene m_scene;
protected InventoryArchiverModule m_archiverModule;
@@ -69,7 +69,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
public void TestLoadCoalesecedItem()
{
TestHelpers.InMethod();
-// log4net.Config.XmlConfigurator.Configure();
+// TestHelpers.EnableLogging();
UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaLL1, "password");
m_archiverModule.DearchiveInventory(m_uaLL1.FirstName, m_uaLL1.LastName, "/", "password", m_iarStream);
@@ -350,38 +350,38 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
Assert.That(sog1.RootPart.CreatorID, Is.EqualTo(m_uaLL1.PrincipalID));
}
- ///
- /// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where
- /// an account exists with the same name as the creator, though not the same id.
- ///
- [Test]
- public void TestLoadIarV0_1SameNameCreator()
- {
- TestHelpers.InMethod();
-// log4net.Config.XmlConfigurator.Configure();
-
- UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaMT, "meowfood");
- UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaLL2, "hampshire");
-
- m_archiverModule.DearchiveInventory(m_uaMT.FirstName, m_uaMT.LastName, "/", "meowfood", m_iarStream);
- InventoryItemBase foundItem1
- = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaMT.PrincipalID, m_item1Name);
-
- Assert.That(
- foundItem1.CreatorId, Is.EqualTo(m_uaLL2.PrincipalID.ToString()),
- "Loaded item non-uuid creator doesn't match original");
- Assert.That(
- foundItem1.CreatorIdAsUuid, Is.EqualTo(m_uaLL2.PrincipalID),
- "Loaded item uuid creator doesn't match original");
- Assert.That(foundItem1.Owner, Is.EqualTo(m_uaMT.PrincipalID),
- "Loaded item owner doesn't match inventory reciever");
-
- AssetBase asset1 = m_scene.AssetService.Get(foundItem1.AssetID.ToString());
- string xmlData = Utils.BytesToString(asset1.Data);
- SceneObjectGroup sog1 = SceneObjectSerializer.FromOriginalXmlFormat(xmlData);
-
- Assert.That(sog1.RootPart.CreatorID, Is.EqualTo(m_uaLL2.PrincipalID));
- }
+// ///
+// /// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where
+// /// an account exists with the same name as the creator, though not the same id.
+// ///
+// [Test]
+// public void TestLoadIarV0_1SameNameCreator()
+// {
+// TestHelpers.InMethod();
+// TestHelpers.EnableLogging();
+//
+// UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaMT, "meowfood");
+// UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaLL2, "hampshire");
+//
+// m_archiverModule.DearchiveInventory(m_uaMT.FirstName, m_uaMT.LastName, "/", "meowfood", m_iarStream);
+// InventoryItemBase foundItem1
+// = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaMT.PrincipalID, m_item1Name);
+//
+// Assert.That(
+// foundItem1.CreatorId, Is.EqualTo(m_uaLL2.PrincipalID.ToString()),
+// "Loaded item non-uuid creator doesn't match original");
+// Assert.That(
+// foundItem1.CreatorIdAsUuid, Is.EqualTo(m_uaLL2.PrincipalID),
+// "Loaded item uuid creator doesn't match original");
+// Assert.That(foundItem1.Owner, Is.EqualTo(m_uaMT.PrincipalID),
+// "Loaded item owner doesn't match inventory reciever");
+//
+// AssetBase asset1 = m_scene.AssetService.Get(foundItem1.AssetID.ToString());
+// string xmlData = Utils.BytesToString(asset1.Data);
+// SceneObjectGroup sog1 = SceneObjectSerializer.FromOriginalXmlFormat(xmlData);
+//
+// Assert.That(sog1.RootPart.CreatorID, Is.EqualTo(m_uaLL2.PrincipalID));
+// }
///
/// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs
index 8176989e0b..e26beec3e1 100644
--- a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs
@@ -38,15 +38,15 @@ using OpenSim.Services.Interfaces;
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
{
- public class InventoryTransferModule : IInventoryTransferModule, ISharedRegionModule
+ public class InventoryTransferModule : ISharedRegionModule
{
private static readonly ILog m_log
= LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
///
private List m_Scenelist = new List();
- private Dictionary m_AgentRegions =
- new Dictionary();
+// private Dictionary m_AgentRegions =
+// new Dictionary();
private IMessageTransferModule m_TransferModule = null;
private bool m_Enabled = true;
@@ -76,12 +76,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
m_Scenelist.Add(scene);
- scene.RegisterModuleInterface(this);
+// scene.RegisterModuleInterface(this);
scene.EventManager.OnNewClient += OnNewClient;
- scene.EventManager.OnClientClosed += ClientLoggedOut;
+// scene.EventManager.OnClientClosed += ClientLoggedOut;
scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
- scene.EventManager.OnSetRootAgentScene += OnSetRootAgentScene;
+// scene.EventManager.OnSetRootAgentScene += OnSetRootAgentScene;
}
public void RegionLoaded(Scene scene)
@@ -96,9 +96,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
m_Scenelist.Clear();
scene.EventManager.OnNewClient -= OnNewClient;
- scene.EventManager.OnClientClosed -= ClientLoggedOut;
+// scene.EventManager.OnClientClosed -= ClientLoggedOut;
scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
- scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene;
+// scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene;
}
}
}
@@ -106,9 +106,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
public void RemoveRegion(Scene scene)
{
scene.EventManager.OnNewClient -= OnNewClient;
- scene.EventManager.OnClientClosed -= ClientLoggedOut;
+// scene.EventManager.OnClientClosed -= ClientLoggedOut;
scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
- scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene;
+// scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene;
m_Scenelist.Remove(scene);
}
@@ -138,10 +138,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
client.OnInstantMessage += OnInstantMessage;
}
- protected void OnSetRootAgentScene(UUID id, Scene scene)
- {
- m_AgentRegions[id] = scene;
- }
+// protected void OnSetRootAgentScene(UUID id, Scene scene)
+// {
+// m_AgentRegions[id] = scene;
+// }
private Scene FindClientScene(UUID agentId)
{
@@ -313,6 +313,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
m_TransferModule.SendInstantMessage(im, delegate(bool success) {});
}
}
+
+ // XXX: This code was placed here to try and accomdate RLV which moves given folders named #RLV/~
+ // to a folder called name in #RLV. However, this approach may not be ultimately correct - from analysis
+ // of Firestorm 4.2.2 on sending an InventoryOffered instead of TaskInventoryOffered (as was previously
+ // done), the viewer itself would appear to move and rename the folder, rather than the simulator doing it here.
else if (im.dialog == (byte) InstantMessageDialog.TaskInventoryAccepted)
{
UUID destinationFolderID = UUID.Zero;
@@ -324,6 +329,16 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
if (destinationFolderID != UUID.Zero)
{
+ InventoryFolderBase destinationFolder = new InventoryFolderBase(destinationFolderID, client.AgentId);
+ if (destinationFolder == null)
+ {
+ m_log.WarnFormat(
+ "[INVENTORY TRANSFER]: TaskInventoryAccepted message from {0} in {1} specified folder {2} which does not exist",
+ client.Name, scene.Name, destinationFolderID);
+
+ return;
+ }
+
IInventoryService invService = scene.InventoryService;
UUID inventoryID = new UUID(im.imSessionID); // The inventory item/folder, back from it's trip
@@ -331,9 +346,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
InventoryItemBase item = new InventoryItemBase(inventoryID, client.AgentId);
item = invService.GetItem(item);
InventoryFolderBase folder = null;
+ UUID? previousParentFolderID = null;
if (item != null) // It's an item
{
+ previousParentFolderID = item.Folder;
item.Folder = destinationFolderID;
invService.DeleteItems(item.Owner, new List() { item.ID });
@@ -346,10 +363,22 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
if (folder != null) // It's a folder
{
+ previousParentFolderID = folder.ParentID;
folder.ParentID = destinationFolderID;
invService.MoveFolder(folder);
}
}
+
+ // Tell client about updates to original parent and new parent (this should probably be factored with existing move item/folder code).
+ if (previousParentFolderID != null)
+ {
+ InventoryFolderBase previousParentFolder
+ = new InventoryFolderBase((UUID)previousParentFolderID, client.AgentId);
+ previousParentFolder = invService.GetFolder(previousParentFolder);
+ scene.SendInventoryUpdate(client, previousParentFolder, true, true);
+
+ scene.SendInventoryUpdate(client, destinationFolder, true, true);
+ }
}
}
else if (
@@ -370,9 +399,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
InventoryItemBase item = new InventoryItemBase(inventoryID, client.AgentId);
item = invService.GetItem(item);
InventoryFolderBase folder = null;
+ UUID? previousParentFolderID = null;
if (item != null && trashFolder != null)
{
+ previousParentFolderID = item.Folder;
item.Folder = trashFolder.ID;
// Diva comment: can't we just update this item???
@@ -388,6 +419,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
if (folder != null & trashFolder != null)
{
+ previousParentFolderID = folder.ParentID;
folder.ParentID = trashFolder.ID;
invService.MoveFolder(folder);
client.SendBulkUpdateInventory(folder);
@@ -408,6 +440,16 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
client.SendAgentAlertMessage("Unable to delete "+
"received inventory" + reason, false);
}
+ // Tell client about updates to original parent and new parent (this should probably be factored with existing move item/folder code).
+ else if (previousParentFolderID != null)
+ {
+ InventoryFolderBase previousParentFolder
+ = new InventoryFolderBase((UUID)previousParentFolderID, client.AgentId);
+ previousParentFolder = invService.GetFolder(previousParentFolder);
+ scene.SendInventoryUpdate(client, previousParentFolder, true, true);
+
+ scene.SendInventoryUpdate(client, trashFolder, true, true);
+ }
if (im.dialog == (byte)InstantMessageDialog.InventoryDeclined)
{
@@ -426,69 +468,69 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
}
}
- public bool NeedSceneCacheClear(UUID agentID, Scene scene)
- {
- if (!m_AgentRegions.ContainsKey(agentID))
- {
- // Since we can get here two ways, we need to scan
- // the scenes here. This is somewhat more expensive
- // but helps avoid a nasty bug
- //
-
- foreach (Scene s in m_Scenelist)
- {
- ScenePresence presence;
-
- if (s.TryGetScenePresence(agentID, out presence))
- {
- // If the agent is in this scene, then we
- // are being called twice in a single
- // teleport. This is wasteful of cycles
- // but harmless due to this 2nd level check
- //
- // If the agent is found in another scene
- // then the list wasn't current
- //
- // If the agent is totally unknown, then what
- // are we even doing here??
- //
- if (s == scene)
- {
- //m_log.Debug("[INVTRANSFERMOD]: s == scene. Returning true in " + scene.RegionInfo.RegionName);
- return true;
- }
- else
- {
- //m_log.Debug("[INVTRANSFERMOD]: s != scene. Returning false in " + scene.RegionInfo.RegionName);
- return false;
- }
- }
- }
- //m_log.Debug("[INVTRANSFERMOD]: agent not in scene. Returning true in " + scene.RegionInfo.RegionName);
- return true;
- }
-
- // The agent is left in current Scene, so we must be
- // going to another instance
- //
- if (m_AgentRegions[agentID] == scene)
- {
- //m_log.Debug("[INVTRANSFERMOD]: m_AgentRegions[agentID] == scene. Returning true in " + scene.RegionInfo.RegionName);
- m_AgentRegions.Remove(agentID);
- return true;
- }
-
- // Another region has claimed the agent
- //
- //m_log.Debug("[INVTRANSFERMOD]: last resort. Returning false in " + scene.RegionInfo.RegionName);
- return false;
- }
-
- public void ClientLoggedOut(UUID agentID, Scene scene)
- {
- if (m_AgentRegions.ContainsKey(agentID))
- m_AgentRegions.Remove(agentID);
- }
+// public bool NeedSceneCacheClear(UUID agentID, Scene scene)
+// {
+// if (!m_AgentRegions.ContainsKey(agentID))
+// {
+// // Since we can get here two ways, we need to scan
+// // the scenes here. This is somewhat more expensive
+// // but helps avoid a nasty bug
+// //
+//
+// foreach (Scene s in m_Scenelist)
+// {
+// ScenePresence presence;
+//
+// if (s.TryGetScenePresence(agentID, out presence))
+// {
+// // If the agent is in this scene, then we
+// // are being called twice in a single
+// // teleport. This is wasteful of cycles
+// // but harmless due to this 2nd level check
+// //
+// // If the agent is found in another scene
+// // then the list wasn't current
+// //
+// // If the agent is totally unknown, then what
+// // are we even doing here??
+// //
+// if (s == scene)
+// {
+// //m_log.Debug("[INVTRANSFERMOD]: s == scene. Returning true in " + scene.RegionInfo.RegionName);
+// return true;
+// }
+// else
+// {
+// //m_log.Debug("[INVTRANSFERMOD]: s != scene. Returning false in " + scene.RegionInfo.RegionName);
+// return false;
+// }
+// }
+// }
+// //m_log.Debug("[INVTRANSFERMOD]: agent not in scene. Returning true in " + scene.RegionInfo.RegionName);
+// return true;
+// }
+//
+// // The agent is left in current Scene, so we must be
+// // going to another instance
+// //
+// if (m_AgentRegions[agentID] == scene)
+// {
+// //m_log.Debug("[INVTRANSFERMOD]: m_AgentRegions[agentID] == scene. Returning true in " + scene.RegionInfo.RegionName);
+// m_AgentRegions.Remove(agentID);
+// return true;
+// }
+//
+// // Another region has claimed the agent
+// //
+// //m_log.Debug("[INVTRANSFERMOD]: last resort. Returning false in " + scene.RegionInfo.RegionName);
+// return false;
+// }
+//
+// public void ClientLoggedOut(UUID agentID, Scene scene)
+// {
+// if (m_AgentRegions.ContainsKey(agentID))
+// m_AgentRegions.Remove(agentID);
+// }
///
///
diff --git a/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs b/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs
index 92cf9d1616..9c369f6060 100644
--- a/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs
@@ -186,7 +186,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Lure
client.FirstName+" "+client.LastName, targetid,
(byte)InstantMessageDialog.RequestTeleport, false,
message, sessionID, false, presence.AbsolutePosition,
- new Byte[0]);
+ new Byte[0], true);
m.RegionID = client.Scene.RegionInfo.RegionID.Guid;
m_log.DebugFormat("[HG LURE MODULE]: RequestTeleport sessionID={0}, regionID={1}, message={2}", m.imSessionID, m.RegionID, m.message);
diff --git a/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs b/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs
index a889984daf..1949459981 100644
--- a/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs
@@ -173,7 +173,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Lure
client.FirstName+" "+client.LastName, targetid,
(byte)InstantMessageDialog.GodLikeRequestTeleport, false,
message, dest, false, presence.AbsolutePosition,
- new Byte[0]);
+ new Byte[0], true);
}
else
{
@@ -181,7 +181,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Lure
client.FirstName+" "+client.LastName, targetid,
(byte)InstantMessageDialog.RequestTeleport, false,
message, dest, false, presence.AbsolutePosition,
- new Byte[0]);
+ new Byte[0], true);
}
if (m_TransferModule != null)
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
index 880b2cc2b2..31e6ce9719 100644
--- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
@@ -327,6 +327,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return;
}
+ // Validate assorted conditions
+ string reason = string.Empty;
+ if (!ValidateGenericConditions(sp, reg, finalDestination, teleportFlags, out reason))
+ {
+ sp.ControllingClient.SendTeleportFailed(reason);
+ return;
+ }
+
//
// This is it
//
@@ -358,6 +366,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
}
}
+ // Nothing to validate here
+ protected virtual bool ValidateGenericConditions(ScenePresence sp, GridRegion reg, GridRegion finalDestination, uint teleportFlags, out string reason)
+ {
+ reason = String.Empty;
+ return true;
+ }
+
///
/// Determines whether this instance is within the max transfer distance.
///
@@ -473,10 +488,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// both regions
if (sp.ParentID != (uint)0)
sp.StandUp();
-
else if (sp.Flying)
teleportFlags |= (uint)TeleportFlags.IsFlying;
+ // At least on LL 3.3.4, this is not strictly necessary - a teleport will succeed without sending this to
+ // the viewer. However, it might mean that the viewer does not see the black teleport screen (untested).
sp.ControllingClient.SendTeleportStart(teleportFlags);
// the avatar.Close below will clear the child region list. We need this below for (possibly)
@@ -552,8 +568,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// So let's wait
Thread.Sleep(200);
+ // At least on LL 3.3.4 for teleports between different regions on the same simulator this appears
+ // unnecessary - teleport will succeed and SEED caps will be requested without it (though possibly
+ // only on TeleportFinish). This is untested for region teleport between different simulators
+ // though this probably also works.
m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath);
-
}
else
{
@@ -574,7 +593,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
//sp.ControllingClient.SendTeleportProgress(teleportFlags, "Updating agent...");
- if (!UpdateAgent(reg, finalDestination, agent))
+ if (!UpdateAgent(reg, finalDestination, agent, sp))
{
// Region doesn't take it
m_log.WarnFormat(
@@ -650,7 +669,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// an agent cannot teleport back to this region if it has teleported away.
Thread.Sleep(3000);
- sp.Scene.IncomingCloseAgent(sp.UUID);
+ sp.Scene.IncomingCloseAgent(sp.UUID, false);
}
else
{
@@ -658,13 +677,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
sp.Reset();
}
- // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE!
- if (sp.Scene.NeedSceneCacheClear(sp.UUID))
- {
- m_log.DebugFormat(
- "[ENTITY TRANSFER MODULE]: User {0} is going to another region, profile cache removed",
- sp.UUID);
- }
+ // Commented pending deletion since this method no longer appears to do anything at all
+// // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE!
+// if (sp.Scene.NeedSceneCacheClear(sp.UUID))
+// {
+// m_log.DebugFormat(
+// "[ENTITY TRANSFER MODULE]: User {0} is going to another region, profile cache removed",
+// sp.UUID);
+// }
m_entityTransferStateMachine.ResetFromTransit(sp.UUID);
}
@@ -701,7 +721,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return success;
}
- protected virtual bool UpdateAgent(GridRegion reg, GridRegion finalDestination, AgentData agent)
+ protected virtual bool UpdateAgent(GridRegion reg, GridRegion finalDestination, AgentData agent, ScenePresence sp)
{
return Scene.SimulationService.UpdateAgent(finalDestination, agent);
}
@@ -1013,6 +1033,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
Scene initiatingScene)
{
Thread.Sleep(10000);
+
IMessageTransferModule im = initiatingScene.RequestModuleInterface();
if (im != null)
{
@@ -1025,11 +1046,22 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
(uint)(int)position.X,
(uint)(int)position.Y,
(uint)(int)position.Z);
- GridInstantMessage m = new GridInstantMessage(initiatingScene, UUID.Zero,
- "Region", agent.UUID,
- (byte)InstantMessageDialog.GodLikeRequestTeleport, false,
- "", gotoLocation, false, new Vector3(127, 0, 0),
- new Byte[0]);
+
+ GridInstantMessage m
+ = new GridInstantMessage(
+ initiatingScene,
+ UUID.Zero,
+ "Region",
+ agent.UUID,
+ (byte)InstantMessageDialog.GodLikeRequestTeleport,
+ false,
+ "",
+ gotoLocation,
+ false,
+ new Vector3(127, 0, 0),
+ new Byte[0],
+ false);
+
im.SendInstantMessage(m, delegate(bool success)
{
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Client Initiating Teleport sending IM success = {0}", success);
@@ -1191,11 +1223,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// the user may change their profile information in other region,
// so the userinfo in UserProfileCache is not reliable any more, delete it
// REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE!
- if (agent.Scene.NeedSceneCacheClear(agent.UUID))
- {
- m_log.DebugFormat(
- "[ENTITY TRANSFER MODULE]: User {0} is going to another region", agent.UUID);
- }
+// if (agent.Scene.NeedSceneCacheClear(agent.UUID))
+// {
+// m_log.DebugFormat(
+// "[ENTITY TRANSFER MODULE]: User {0} is going to another region", agent.UUID);
+// }
//m_log.Debug("AFTER CROSS");
//Scene.DumpChildrenSeeds(UUID);
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs
index 3010b59f13..b16c37ae5a 100644
--- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs
@@ -54,6 +54,59 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
private GatekeeperServiceConnector m_GatekeeperConnector;
+ protected bool m_RestrictAppearanceAbroad;
+ protected string m_AccountName;
+ protected List m_ExportedAppearances;
+ protected List m_Attachs;
+
+ protected List ExportedAppearance
+ {
+ get
+ {
+ if (m_ExportedAppearances != null)
+ return m_ExportedAppearances;
+
+ m_ExportedAppearances = new List();
+ m_Attachs = new List();
+
+ string[] names = m_AccountName.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+
+ foreach (string name in names)
+ {
+ string[] parts = name.Trim().Split();
+ if (parts.Length != 2)
+ {
+ m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: Wrong user account name format {0}. Specify 'First Last'", name);
+ return null;
+ }
+ UserAccount account = Scene.UserAccountService.GetUserAccount(UUID.Zero, parts[0], parts[1]);
+ if (account == null)
+ {
+ m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: Unknown account {0}", m_AccountName);
+ return null;
+ }
+ AvatarAppearance a = Scene.AvatarService.GetAppearance(account.PrincipalID);
+ if (a != null)
+ m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Successfully retrieved appearance for {0}", name);
+
+ foreach (AvatarAttachment att in a.GetAttachments())
+ {
+ InventoryItemBase item = new InventoryItemBase(att.ItemID, account.PrincipalID);
+ item = Scene.InventoryService.GetItem(item);
+ if (item != null)
+ a.SetAttachment(att.AttachPoint, att.ItemID, item.AssetID);
+ else
+ m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: Unable to retrieve item {0} from inventory {1}", att.ItemID, name);
+ }
+
+ m_ExportedAppearances.Add(a);
+ m_Attachs.AddRange(a.GetAttachments());
+ }
+
+ return m_ExportedAppearances;
+ }
+ }
+
#region ISharedRegionModule
public override string Name
@@ -72,8 +125,18 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
{
IConfig transferConfig = source.Configs["EntityTransfer"];
if (transferConfig != null)
+ {
m_levelHGTeleport = transferConfig.GetInt("LevelHGTeleport", 0);
+ m_RestrictAppearanceAbroad = transferConfig.GetBoolean("RestrictAppearanceAbroad", false);
+ if (m_RestrictAppearanceAbroad)
+ {
+ m_AccountName = transferConfig.GetString("AccountForAppearance", string.Empty);
+ if (m_AccountName == string.Empty)
+ m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: RestrictAppearanceAbroad is on, but no account has been given for avatar appearance!");
+ }
+ }
+
InitialiseCommon(source);
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: {0} enabled.", Name);
}
@@ -85,7 +148,36 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
base.AddRegion(scene);
if (m_Enabled)
+ {
scene.RegisterModuleInterface(this);
+ scene.EventManager.OnIncomingSceneObject += OnIncomingSceneObject;
+ }
+ }
+
+ void OnIncomingSceneObject(SceneObjectGroup so)
+ {
+ if (!so.IsAttachment)
+ return;
+
+ if (so.Scene.UserManagementModule.IsLocalGridUser(so.AttachedAvatar))
+ return;
+
+ // foreign user
+ AgentCircuitData aCircuit = so.Scene.AuthenticateHandler.GetAgentCircuitData(so.AttachedAvatar);
+ if (aCircuit != null && (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0)
+ {
+ if (aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("AssetServerURI"))
+ {
+ string url = aCircuit.ServiceURLs["AssetServerURI"].ToString();
+ m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Incoming attachement {0} for HG user {1} with asset server {2}", so.Name, so.AttachedAvatar, url);
+ Dictionary ids = new Dictionary();
+ HGUuidGatherer uuidGatherer = new HGUuidGatherer(so.Scene.AssetService, url);
+ uuidGatherer.GatherAssetUuids(so, ids);
+
+ foreach (KeyValuePair kvp in ids)
+ uuidGatherer.FetchAsset(kvp.Key);
+ }
+ }
}
protected override void OnNewClient(IClientAPI client)
@@ -120,7 +212,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, region.RegionID);
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: region {0} flags: {1}", region.RegionID, flags);
- if ((flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0)
+ if ((flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0)
{
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Destination region {0} is hyperlink", region.RegionID);
GridRegion real_destination = m_GatekeeperConnector.GetHyperlinkRegion(region, region.RegionID);
@@ -140,7 +232,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return true;
int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, reg.RegionID);
- if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0)
+ if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0)
return true;
return false;
@@ -153,6 +245,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
{
// Log them out of this grid
Scene.PresenceService.LogoutAgent(sp.ControllingClient.SessionId);
+ string userId = Scene.UserManagementModule.GetUserUUI(sp.UUID);
+ Scene.GridUserService.LoggedOut(userId, UUID.Zero, Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat);
}
}
@@ -162,11 +256,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
reason = string.Empty;
logout = false;
int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, reg.RegionID);
- if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0)
+ if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0)
{
// this user is going to another grid
- // check if HyperGrid teleport is allowed, based on user level
- if (sp.UserLevel < m_levelHGTeleport)
+ // for local users, check if HyperGrid teleport is allowed, based on user level
+ if (Scene.UserManagementModule.IsLocalGridUser(sp.UUID) && sp.UserLevel < m_levelHGTeleport)
{
m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: Unable to HG teleport agent due to insufficient UserLevel.");
reason = "Hypergrid teleport not allowed";
@@ -200,6 +294,124 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
TeleportHome(id, client);
}
+ protected override bool ValidateGenericConditions(ScenePresence sp, GridRegion reg, GridRegion finalDestination, uint teleportFlags, out string reason)
+ {
+ reason = "Please wear your grid's allowed appearance before teleporting to another grid";
+ if (!m_RestrictAppearanceAbroad)
+ return true;
+
+ // The rest is only needed for controlling appearance
+
+ int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, reg.RegionID);
+ if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0)
+ {
+ // this user is going to another grid
+ if (Scene.UserManagementModule.IsLocalGridUser(sp.UUID))
+ {
+ m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: RestrictAppearanceAbroad is ON. Checking generic appearance");
+
+ // Check wearables
+ for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++)
+ {
+ for (int j = 0; j < sp.Appearance.Wearables[i].Count; j++)
+ {
+ if (sp.Appearance.Wearables[i] == null)
+ continue;
+
+ bool found = false;
+ foreach (AvatarAppearance a in ExportedAppearance)
+ if (a.Wearables[i] != null)
+ {
+ found = true;
+ break;
+ }
+
+ if (!found)
+ {
+ m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Wearable not allowed to go outside {0}", i);
+ return false;
+ }
+
+ found = false;
+ foreach (AvatarAppearance a in ExportedAppearance)
+ if (sp.Appearance.Wearables[i][j].AssetID == a.Wearables[i][j].AssetID)
+ {
+ found = true;
+ break;
+ }
+
+ if (!found)
+ {
+ m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Wearable not allowed to go outside {0}", i);
+ return false;
+ }
+ }
+ }
+
+ // Check attachments
+ foreach (AvatarAttachment att in sp.Appearance.GetAttachments())
+ {
+ bool found = false;
+ foreach (AvatarAttachment att2 in m_Attachs)
+ {
+ if (att2.AssetID == att.AssetID)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Attachment not allowed to go outside {0}", att.AttachPoint);
+ return false;
+ }
+ }
+ }
+ }
+
+ reason = string.Empty;
+ return true;
+ }
+
+
+ //protected override bool UpdateAgent(GridRegion reg, GridRegion finalDestination, AgentData agentData, ScenePresence sp)
+ //{
+ // int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, reg.RegionID);
+ // if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0)
+ // {
+ // // this user is going to another grid
+ // if (m_RestrictAppearanceAbroad && Scene.UserManagementModule.IsLocalGridUser(agentData.AgentID))
+ // {
+ // // We need to strip the agent off its appearance
+ // m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: RestrictAppearanceAbroad is ON. Sending generic appearance");
+
+ // // Delete existing npc attachments
+ // Scene.AttachmentsModule.DeleteAttachmentsFromScene(sp, false);
+
+ // // XXX: We can't just use IAvatarFactoryModule.SetAppearance() yet since it doesn't transfer attachments
+ // AvatarAppearance newAppearance = new AvatarAppearance(ExportedAppearance, true);
+ // sp.Appearance = newAppearance;
+
+ // // Rez needed npc attachments
+ // Scene.AttachmentsModule.RezAttachments(sp);
+
+
+ // IAvatarFactoryModule module = Scene.RequestModuleInterface();
+ // //module.SendAppearance(sp.UUID);
+ // module.RequestRebake(sp, false);
+
+ // Scene.AttachmentsModule.CopyAttachments(sp, agentData);
+ // agentData.Appearance = sp.Appearance;
+ // }
+ // }
+
+ // foreach (AvatarAttachment a in agentData.Appearance.GetAttachments())
+ // m_log.DebugFormat("[XXX]: {0}-{1}", a.ItemID, a.AssetID);
+
+
+ // return base.UpdateAgent(reg, finalDestination, agentData, sp);
+ //}
+
public override bool TeleportHome(UUID id, IClientAPI client)
{
m_log.DebugFormat(
@@ -375,4 +587,4 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return region;
}
}
-}
\ No newline at end of file
+}
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs
index eaadc1b8be..f8ec6de85d 100644
--- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs
+++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs
@@ -71,19 +71,19 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
#region Internal functions
- public AssetBase FetchAsset(string url, UUID assetID)
+ public AssetMetadata FetchMetadata(string url, UUID assetID)
{
if (!url.EndsWith("/") && !url.EndsWith("="))
url = url + "/";
- AssetBase asset = m_scene.AssetService.Get(url + assetID.ToString());
+ AssetMetadata meta = m_scene.AssetService.GetMetadata(url + assetID.ToString());
- if (asset != null)
- {
- m_log.DebugFormat("[HG ASSET MAPPER]: Copied asset {0} from {1} to local asset server. ", asset.ID, url);
- return asset;
- }
- return null;
+ if (meta != null)
+ m_log.DebugFormat("[HG ASSET MAPPER]: Fetched metadata for asset {0} of type {1} from {2} ", assetID, meta.Type, url);
+ else
+ m_log.DebugFormat("[HG ASSET MAPPER]: Unable to fetched metadata for asset {0} from {1} ", assetID, url);
+
+ return meta;
}
public bool PostAsset(string url, AssetBase asset)
@@ -93,6 +93,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
if (!url.EndsWith("/") && !url.EndsWith("="))
url = url + "/";
+ bool success = true;
// See long comment in AssetCache.AddAsset
if (!asset.Temporary || asset.Local)
{
@@ -103,14 +104,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
// not having a global naming infrastructure
AssetBase asset1 = new AssetBase(asset.FullID, asset.Name, asset.Type, asset.Metadata.CreatorID);
Copy(asset, asset1);
- try
- {
- asset1.ID = url + asset.ID;
- }
- catch
- {
- m_log.Warn("[HG ASSET MAPPER]: Oops.");
- }
+ asset1.ID = url + asset.ID;
AdjustIdentifiers(asset1.Metadata);
if (asset1.Metadata.Type == (sbyte)AssetType.Object)
@@ -118,11 +112,17 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
else
asset1.Data = asset.Data;
- m_scene.AssetService.Store(asset1);
- m_log.DebugFormat("[HG ASSET MAPPER]: Posted copy of asset {0} from local asset server to {1}", asset1.ID, url);
+ string id = m_scene.AssetService.Store(asset1);
+ if (id == string.Empty)
+ {
+ m_log.DebugFormat("[HG ASSET MAPPER]: Asset server {0} did not accept {1}", url, asset.ID);
+ success = false;
+ }
+ else
+ m_log.DebugFormat("[HG ASSET MAPPER]: Posted copy of asset {0} from local asset server to {1}", asset1.ID, url);
}
- return true;
- }
+ return success;
+ }
else
m_log.Warn("[HG ASSET MAPPER]: Tried to post asset to remote server, but asset not in local cache.");
@@ -222,28 +222,17 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
public void Get(UUID assetID, UUID ownerID, string userAssetURL)
{
- // Get the item from the remote asset server onto the local AssetCache
- // and place an entry in m_assetMap
+ // Get the item from the remote asset server onto the local AssetService
- m_log.Debug("[HG ASSET MAPPER]: Fetching object " + assetID + " from asset server " + userAssetURL);
- AssetBase asset = FetchAsset(userAssetURL, assetID);
+ AssetMetadata meta = FetchMetadata(userAssetURL, assetID);
+ if (meta == null)
+ return;
- if (asset != null)
- {
- // OK, now fetch the inside.
- Dictionary ids = new Dictionary();
- HGUuidGatherer uuidGatherer = new HGUuidGatherer(this, m_scene.AssetService, userAssetURL);
- uuidGatherer.GatherAssetUuids(asset.FullID, (AssetType)asset.Type, ids);
- if (ids.ContainsKey(assetID))
- ids.Remove(assetID);
- foreach (UUID uuid in ids.Keys)
- FetchAsset(userAssetURL, uuid);
+ // The act of gathering UUIDs downloads the assets from the remote server
+ Dictionary ids = new Dictionary();
+ HGUuidGatherer uuidGatherer = new HGUuidGatherer(m_scene.AssetService, userAssetURL);
+ uuidGatherer.GatherAssetUuids(assetID, (AssetType)meta.Type, ids);
- m_log.DebugFormat("[HG ASSET MAPPER]: Successfully fetched asset {0} from asset server {1}", asset.ID, userAssetURL);
-
- }
- else
- m_log.Warn("[HG ASSET MAPPER]: Could not fetch asset from remote asset server " + userAssetURL);
}
@@ -257,19 +246,23 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
if (asset != null)
{
Dictionary ids = new Dictionary();
- HGUuidGatherer uuidGatherer = new HGUuidGatherer(this, m_scene.AssetService, string.Empty);
+ HGUuidGatherer uuidGatherer = new HGUuidGatherer(m_scene.AssetService, string.Empty);
uuidGatherer.GatherAssetUuids(asset.FullID, (AssetType)asset.Type, ids);
+ bool success = false;
foreach (UUID uuid in ids.Keys)
{
asset = m_scene.AssetService.Get(uuid.ToString());
if (asset == null)
m_log.DebugFormat("[HG ASSET MAPPER]: Could not find asset {0}", uuid);
else
- PostAsset(userAssetURL, asset);
+ success = PostAsset(userAssetURL, asset);
}
- // maybe all pieces got there...
- m_log.DebugFormat("[HG ASSET MAPPER]: Successfully posted item {0} to asset server {1}", assetID, userAssetURL);
+ // maybe all pieces got there...
+ if (!success)
+ m_log.DebugFormat("[HG ASSET MAPPER]: Problems posting item {0} to asset server {1}", assetID, userAssetURL);
+ else
+ m_log.DebugFormat("[HG ASSET MAPPER]: Successfully posted item {0} to asset server {1}", assetID, userAssetURL);
}
else
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs
index cf72b58832..6bb758ea07 100644
--- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs
@@ -92,7 +92,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
m_HomeURI = thisModuleConfig.GetString("HomeURI", m_HomeURI);
m_OutboundPermission = thisModuleConfig.GetBoolean("OutboundPermission", true);
m_ThisGatekeeper = thisModuleConfig.GetString("Gatekeeper", string.Empty);
- m_RestrictInventoryAccessAbroad = thisModuleConfig.GetBoolean("RestrictInventoryAccessAbroad", false);
+ m_RestrictInventoryAccessAbroad = thisModuleConfig.GetBoolean("RestrictInventoryAccessAbroad", true);
}
else
m_log.Warn("[HG INVENTORY ACCESS MODULE]: HGInventoryAccessModule configs not found. ProfileServerURI not set!");
@@ -263,8 +263,13 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
//}
// OK, we're done fetching. Pass it up to the default RezObject
- return base.RezObject(remoteClient, itemID, RayEnd, RayStart, RayTargetID, BypassRayCast, RayEndIsIntersection,
- RezSelected, RemoveItem, fromTaskID, attachment);
+ SceneObjectGroup sog = base.RezObject(remoteClient, itemID, RayEnd, RayStart, RayTargetID, BypassRayCast, RayEndIsIntersection,
+ RezSelected, RemoveItem, fromTaskID, attachment);
+
+ if (sog == null)
+ remoteClient.SendAgentAlertMessage("Unable to rez: problem accessing inventory or locating assets", false);
+
+ return sog;
}
@@ -308,6 +313,8 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
protected override InventoryItemBase GetItem(UUID agentID, UUID itemID)
{
InventoryItemBase item = base.GetItem(agentID, itemID);
+ if (item == null)
+ return null;
string userAssetServer = string.Empty;
if (IsForeignUser(agentID, out userAssetServer))
@@ -344,6 +351,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
private void ProcessInventoryForArriving(IClientAPI client)
{
+ // No-op for now, but we may need to do something for freign users inventory
}
//
@@ -390,6 +398,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
private void ProcessInventoryForLeaving(IClientAPI client)
{
+ // No-op for now
}
#endregion
diff --git a/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs b/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs
index e135c21b79..e4115851af 100644
--- a/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs
@@ -95,14 +95,14 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring
{
foreach (IMonitor monitor in m_staticMonitors)
{
- m_log.InfoFormat(
+ MainConsole.Instance.OutputFormat(
"[MONITOR MODULE]: {0} reports {1} = {2}",
m_scene.RegionInfo.RegionName, monitor.GetFriendlyName(), monitor.GetFriendlyValue());
}
foreach (KeyValuePair tuple in m_scene.StatsReporter.GetExtraSimStats())
{
- m_log.InfoFormat(
+ MainConsole.Instance.OutputFormat(
"[MONITOR MODULE]: {0} reports {1} = {2}",
m_scene.RegionInfo.RegionName, tuple.Key, tuple.Value);
}
diff --git a/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs b/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs
index 65e4c90ba5..fd8d5e32bd 100755
--- a/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs
+++ b/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs
@@ -1,161 +1,170 @@
-/*
- * 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.IO;
-using System.Text;
-using log4net;
-
-namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging
-{
- ///
- /// Class for writing a high performance, high volume log file.
- /// Sometimes, to debug, one has a high volume logging to do and the regular
- /// log file output is not appropriate.
- /// Create a new instance with the parameters needed and
- /// call Write() to output a line. Call Close() when finished.
- /// If created with no parameters, it will not log anything.
- ///
- public class LogWriter : IDisposable
- {
- public bool Enabled { get; private set; }
-
- private string m_logDirectory = ".";
- private int m_logMaxFileTimeMin = 5; // 5 minutes
- public String LogFileHeader { get; set; }
-
- private StreamWriter m_logFile = null;
- private TimeSpan m_logFileLife;
- private DateTime m_logFileEndTime;
- private Object m_logFileWriteLock = new Object();
-
- // set externally when debugging. If let 'null', this does not write any error messages.
- public ILog ErrorLogger = null;
- private string LogHeader = "[LOG WRITER]";
-
- ///
- /// Create a log writer that will not write anything. Good for when not enabled
- /// but the write statements are still in the code.
- ///
- public LogWriter()
- {
- Enabled = false;
- m_logFile = null;
- }
-
- ///
- /// Create a log writer instance.
- ///
- /// The directory to create the log file in. May be 'null' for default.
- /// The characters that begin the log file name. May be 'null' for default.
- /// Maximum age of a log file in minutes. If zero, will set default.
- public LogWriter(string dir, string headr, int maxFileTime)
- {
- m_logDirectory = dir == null ? "." : dir;
-
- LogFileHeader = headr == null ? "log-" : headr;
-
- m_logMaxFileTimeMin = maxFileTime;
- if (m_logMaxFileTimeMin < 1)
- m_logMaxFileTimeMin = 5;
-
- m_logFileLife = new TimeSpan(0, m_logMaxFileTimeMin, 0);
- m_logFileEndTime = DateTime.Now + m_logFileLife;
-
- Enabled = true;
- }
-
- public void Dispose()
- {
- this.Close();
- }
-
- public void Close()
- {
- Enabled = false;
- if (m_logFile != null)
- {
- m_logFile.Close();
- m_logFile.Dispose();
- m_logFile = null;
- }
- }
-
- public void Write(string line, params object[] args)
- {
- if (!Enabled) return;
- Write(String.Format(line, args));
- }
-
- public void Write(string line)
- {
- if (!Enabled) return;
- try
- {
- lock (m_logFileWriteLock)
- {
- DateTime now = DateTime.Now;
- if (m_logFile == null || now > m_logFileEndTime)
- {
- if (m_logFile != null)
- {
- m_logFile.Close();
- m_logFile.Dispose();
- m_logFile = null;
- }
-
- // First log file or time has expired, start writing to a new log file
- m_logFileEndTime = now + m_logFileLife;
- string path = (m_logDirectory.Length > 0 ? m_logDirectory
- + System.IO.Path.DirectorySeparatorChar.ToString() : "")
- + String.Format("{0}{1}.log", LogFileHeader, now.ToString("yyyyMMddHHmmss"));
- m_logFile = new StreamWriter(File.Open(path, FileMode.Append, FileAccess.Write));
- }
- if (m_logFile != null)
- {
- StringBuilder buff = new StringBuilder(line.Length + 25);
- buff.Append(now.ToString("yyyyMMddHHmmssfff"));
- // buff.Append(now.ToString("yyyyMMddHHmmss"));
- buff.Append(",");
- buff.Append(line);
- buff.Append("\r\n");
- m_logFile.Write(buff.ToString());
- }
- }
- }
- catch (Exception e)
- {
- if (ErrorLogger != null)
- {
- ErrorLogger.ErrorFormat("{0}: FAILURE WRITING TO LOGFILE: {1}", LogHeader, e);
- }
- Enabled = false;
- }
- return;
- }
- }
-}
\ No newline at end of file
+/*
+ * 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.IO;
+using System.Text;
+using log4net;
+
+namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging
+{
+ ///
+ /// Class for writing a high performance, high volume log file.
+ /// Sometimes, to debug, one has a high volume logging to do and the regular
+ /// log file output is not appropriate.
+ /// Create a new instance with the parameters needed and
+ /// call Write() to output a line. Call Close() when finished.
+ /// If created with no parameters, it will not log anything.
+ ///
+ public class LogWriter : IDisposable
+ {
+ public bool Enabled { get; private set; }
+
+ private string m_logDirectory = ".";
+ private int m_logMaxFileTimeMin = 5; // 5 minutes
+ public String LogFileHeader { get; set; }
+
+ private StreamWriter m_logFile = null;
+ private TimeSpan m_logFileLife;
+ private DateTime m_logFileEndTime;
+ private Object m_logFileWriteLock = new Object();
+
+ // set externally when debugging. If let 'null', this does not write any error messages.
+ public ILog ErrorLogger = null;
+ private string LogHeader = "[LOG WRITER]";
+
+ ///
+ /// Create a log writer that will not write anything. Good for when not enabled
+ /// but the write statements are still in the code.
+ ///
+ public LogWriter()
+ {
+ Enabled = false;
+ m_logFile = null;
+ }
+
+ ///
+ /// Create a log writer instance.
+ ///
+ /// The directory to create the log file in. May be 'null' for default.
+ /// The characters that begin the log file name. May be 'null' for default.
+ /// Maximum age of a log file in minutes. If zero, will set default.
+ public LogWriter(string dir, string headr, int maxFileTime)
+ {
+ m_logDirectory = dir == null ? "." : dir;
+
+ LogFileHeader = headr == null ? "log-" : headr;
+
+ m_logMaxFileTimeMin = maxFileTime;
+ if (m_logMaxFileTimeMin < 1)
+ m_logMaxFileTimeMin = 5;
+
+ m_logFileLife = new TimeSpan(0, m_logMaxFileTimeMin, 0);
+ m_logFileEndTime = DateTime.Now + m_logFileLife;
+
+ Enabled = true;
+ }
+
+ public void Dispose()
+ {
+ this.Close();
+ }
+
+ public void Close()
+ {
+ Enabled = false;
+ if (m_logFile != null)
+ {
+ m_logFile.Close();
+ m_logFile.Dispose();
+ m_logFile = null;
+ }
+ }
+
+ public void Write(string line, params object[] args)
+ {
+ if (!Enabled) return;
+ Write(String.Format(line, args));
+ }
+
+ public void Flush()
+ {
+ if (!Enabled) return;
+ if (m_logFile != null)
+ {
+ m_logFile.Flush();
+ }
+ }
+
+ public void Write(string line)
+ {
+ if (!Enabled) return;
+ try
+ {
+ lock (m_logFileWriteLock)
+ {
+ DateTime now = DateTime.Now;
+ if (m_logFile == null || now > m_logFileEndTime)
+ {
+ if (m_logFile != null)
+ {
+ m_logFile.Close();
+ m_logFile.Dispose();
+ m_logFile = null;
+ }
+
+ // First log file or time has expired, start writing to a new log file
+ m_logFileEndTime = now + m_logFileLife;
+ string path = (m_logDirectory.Length > 0 ? m_logDirectory
+ + System.IO.Path.DirectorySeparatorChar.ToString() : "")
+ + String.Format("{0}{1}.log", LogFileHeader, now.ToString("yyyyMMddHHmmss"));
+ m_logFile = new StreamWriter(File.Open(path, FileMode.Append, FileAccess.Write));
+ }
+ if (m_logFile != null)
+ {
+ StringBuilder buff = new StringBuilder(line.Length + 25);
+ buff.Append(now.ToString("yyyyMMddHHmmssfff"));
+ // buff.Append(now.ToString("yyyyMMddHHmmss"));
+ buff.Append(",");
+ buff.Append(line);
+ buff.Append("\r\n");
+ m_logFile.Write(buff.ToString());
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ if (ErrorLogger != null)
+ {
+ ErrorLogger.ErrorFormat("{0}: FAILURE WRITING TO LOGFILE: {1}", LogHeader, e);
+ }
+ Enabled = false;
+ }
+ return;
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/Framework/UserManagement/HGUserManagementModule.cs b/OpenSim/Region/CoreModules/Framework/UserManagement/HGUserManagementModule.cs
index 4eecaa2080..acefc97276 100644
--- a/OpenSim/Region/CoreModules/Framework/UserManagement/HGUserManagementModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/UserManagement/HGUserManagementModule.cs
@@ -137,6 +137,9 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
ud.FirstName = words[0];
ud.LastName = "@" + words[1];
users.Add(ud);
+ // WARNING! that uriStr is not quite right... it may be missing the / at the end,
+ // which will cause trouble (duplicate entries on some tables). We should
+ // get the UUI instead from the UAS. TO BE FIXED.
AddUser(userID, names[0], names[1], uriStr);
m_log.DebugFormat("[USER MANAGEMENT MODULE]: User {0}@{1} found", words[0], words[1]);
}
diff --git a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs
index f4ed67b864..b4811dadce 100644
--- a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs
@@ -31,6 +31,7 @@ using System.Reflection;
using OpenSim.Framework;
using OpenSim.Framework.Console;
+using OpenSim.Region.ClientStack.LindenUDP;
using OpenSim.Region.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
@@ -429,8 +430,7 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
public void AddUser(UUID uuid, string first, string last, string homeURL)
{
- // m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, first {1}, last {2}, url {3}", uuid, first, last, homeURL);
-
+ //m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, first {1}, last {2}, url {3}", uuid, first, last, homeURL);
AddUser(uuid, homeURL + ";" + first + " " + last);
}
@@ -553,8 +553,8 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
MainConsole.Instance.Output("-----------------------------------------------------------------------------");
foreach (KeyValuePair kvp in m_UserCache)
{
- MainConsole.Instance.Output(String.Format("{0} {1} {2}",
- kvp.Key, kvp.Value.FirstName, kvp.Value.LastName));
+ MainConsole.Instance.Output(String.Format("{0} {1} {2} ({3})",
+ kvp.Key, kvp.Value.FirstName, kvp.Value.LastName, kvp.Value.HomeURL));
}
return;
diff --git a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml
index 424e0ab384..6c73d911e0 100644
--- a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml
+++ b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml
@@ -15,6 +15,7 @@
+
diff --git a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTexture.cs b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTexture.cs
new file mode 100644
index 0000000000..fce9490da6
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTexture.cs
@@ -0,0 +1,61 @@
+/*
+ * 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.Drawing;
+using OpenSim.Region.Framework.Interfaces;
+
+namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
+{
+ public class DynamicTexture : IDynamicTexture
+ {
+ public string InputCommands { get; private set; }
+ public Uri InputUri { get; private set; }
+ public string InputParams { get; private set; }
+ public byte[] Data { get; private set; }
+ public Size Size { get; private set; }
+ public bool IsReuseable { get; private set; }
+
+ public DynamicTexture(string inputCommands, string inputParams, byte[] data, Size size, bool isReuseable)
+ {
+ InputCommands = inputCommands;
+ InputParams = inputParams;
+ Data = data;
+ Size = size;
+ IsReuseable = isReuseable;
+ }
+
+ public DynamicTexture(Uri inputUri, string inputParams, byte[] data, Size size, bool isReuseable)
+ {
+ InputUri = inputUri;
+ InputParams = inputParams;
+ Data = data;
+ Size = size;
+ IsReuseable = isReuseable;
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
index 18bd0186b8..93a045eff7 100644
--- a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
@@ -42,13 +42,29 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
{
public class DynamicTextureModule : IRegionModule, IDynamicTextureManager
{
- //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private const int ALL_SIDES = -1;
public const int DISP_EXPIRE = 1;
public const int DISP_TEMP = 2;
+ ///
+ /// If true then where possible dynamic textures are reused.
+ ///
+ public bool ReuseTextures { get; set; }
+
+ ///
+ /// If false, then textures which have a low data size are not reused when ReuseTextures = true.
+ ///
+ ///
+ /// LL viewers 3.3.4 and before appear to not fully render textures pulled from the viewer cache if those
+ /// textures have a relatively high pixel surface but a small data size. Typically, this appears to happen
+ /// if the data size is smaller than the viewer's discard level 2 size estimate. So if this is setting is
+ /// false, textures smaller than the calculation in IsSizeReuseable are always regenerated rather than reused
+ /// to work around this problem.
+ public bool ReuseLowDataTextures { get; set; }
+
private Dictionary RegisteredScenes = new Dictionary();
private Dictionary RenderPlugins =
@@ -56,6 +72,15 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
private Dictionary Updaters = new Dictionary();
+ ///
+ /// Record dynamic textures that we can reuse for a given data and parameter combination rather than
+ /// regenerate.
+ ///
+ ///
+ /// Key is string.Format("{0}{1}", data
+ ///
+ private Cache m_reuseableDynamicTextures;
+
#region IDynamicTextureManager Members
public void RegisterRender(string handleType, IDynamicTextureRender render)
@@ -69,17 +94,17 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
///
/// Called by code which actually renders the dynamic texture to supply texture data.
///
- ///
- ///
- public void ReturnData(UUID id, byte[] data)
+ ///
+ ///
+ public void ReturnData(UUID updaterId, IDynamicTexture texture)
{
DynamicTextureUpdater updater = null;
lock (Updaters)
{
- if (Updaters.ContainsKey(id))
+ if (Updaters.ContainsKey(updaterId))
{
- updater = Updaters[id];
+ updater = Updaters[updaterId];
}
}
@@ -88,7 +113,16 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
if (RegisteredScenes.ContainsKey(updater.SimUUID))
{
Scene scene = RegisteredScenes[updater.SimUUID];
- updater.DataReceived(data, scene);
+ UUID newTextureID = updater.DataReceived(texture.Data, scene);
+
+ if (ReuseTextures
+ && !updater.BlendWithOldTexture
+ && texture.IsReuseable
+ && (ReuseLowDataTextures || IsDataSizeReuseable(texture)))
+ {
+ m_reuseableDynamicTextures.Store(
+ GenerateReusableTextureKey(texture.InputCommands, texture.InputParams), newTextureID);
+ }
}
}
@@ -104,6 +138,27 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
}
}
+ ///
+ /// Determines whether the texture is reuseable based on its data size.
+ ///
+ ///
+ /// This is a workaround for a viewer bug where very small data size textures relative to their pixel size
+ /// are not redisplayed properly when pulled from cache. The calculation here is based on the typical discard
+ /// level of 2, a 'rate' of 0.125 and 4 components (which makes for a factor of 0.5).
+ ///
+ ///
+ private bool IsDataSizeReuseable(IDynamicTexture texture)
+ {
+// Console.WriteLine("{0} {1}", texture.Size.Width, texture.Size.Height);
+ int discardLevel2DataThreshold = (int)Math.Ceiling((texture.Size.Width >> 2) * (texture.Size.Height >> 2) * 0.5);
+
+// m_log.DebugFormat(
+// "[DYNAMIC TEXTURE MODULE]: Discard level 2 threshold {0}, texture data length {1}",
+// discardLevel2DataThreshold, texture.Data.Length);
+
+ return discardLevel2DataThreshold < texture.Data.Length;
+ }
+
public UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url,
string extraParams, int updateTimer)
{
@@ -167,22 +222,61 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
public UUID AddDynamicTextureData(UUID simID, UUID primID, string contentType, string data,
string extraParams, int updateTimer, bool SetBlending, int disp, byte AlphaValue, int face)
{
- if (RenderPlugins.ContainsKey(contentType))
- {
- DynamicTextureUpdater updater = new DynamicTextureUpdater();
- updater.SimUUID = simID;
- updater.PrimID = primID;
- updater.ContentType = contentType;
- updater.BodyData = data;
- updater.UpdateTimer = updateTimer;
- updater.UpdaterID = UUID.Random();
- updater.Params = extraParams;
- updater.BlendWithOldTexture = SetBlending;
- updater.FrontAlpha = AlphaValue;
- updater.Face = face;
- updater.Url = "Local image";
- updater.Disp = disp;
+ if (!RenderPlugins.ContainsKey(contentType))
+ return UUID.Zero;
+ Scene scene;
+ RegisteredScenes.TryGetValue(simID, out scene);
+
+ if (scene == null)
+ return UUID.Zero;
+
+ SceneObjectPart part = scene.GetSceneObjectPart(primID);
+
+ if (part == null)
+ return UUID.Zero;
+
+ // If we want to reuse dynamic textures then we have to ignore any request from the caller to expire
+ // them.
+ if (ReuseTextures)
+ disp = disp & ~DISP_EXPIRE;
+
+ DynamicTextureUpdater updater = new DynamicTextureUpdater();
+ updater.SimUUID = simID;
+ updater.PrimID = primID;
+ updater.ContentType = contentType;
+ updater.BodyData = data;
+ updater.UpdateTimer = updateTimer;
+ updater.UpdaterID = UUID.Random();
+ updater.Params = extraParams;
+ updater.BlendWithOldTexture = SetBlending;
+ updater.FrontAlpha = AlphaValue;
+ updater.Face = face;
+ updater.Url = "Local image";
+ updater.Disp = disp;
+
+ object objReusableTextureUUID = null;
+
+ if (ReuseTextures && !updater.BlendWithOldTexture)
+ {
+ string reuseableTextureKey = GenerateReusableTextureKey(data, extraParams);
+ objReusableTextureUUID = m_reuseableDynamicTextures.Get(reuseableTextureKey);
+
+ if (objReusableTextureUUID != null)
+ {
+ // If something else has removed this temporary asset from the cache, detect and invalidate
+ // our cached uuid.
+ if (scene.AssetService.GetMetadata(objReusableTextureUUID.ToString()) == null)
+ {
+ m_reuseableDynamicTextures.Invalidate(reuseableTextureKey);
+ objReusableTextureUUID = null;
+ }
+ }
+ }
+
+ // We cannot reuse a dynamic texture if the data is going to be blended with something already there.
+ if (objReusableTextureUUID == null)
+ {
lock (Updaters)
{
if (!Updaters.ContainsKey(updater.UpdaterID))
@@ -191,11 +285,29 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
}
}
+// m_log.DebugFormat(
+// "[DYNAMIC TEXTURE MODULE]: Requesting generation of new dynamic texture for {0} in {1}",
+// part.Name, part.ParentGroup.Scene.Name);
+
RenderPlugins[contentType].AsyncConvertData(updater.UpdaterID, data, extraParams);
- return updater.UpdaterID;
}
-
- return UUID.Zero;
+ else
+ {
+// m_log.DebugFormat(
+// "[DYNAMIC TEXTURE MODULE]: Reusing cached texture {0} for {1} in {2}",
+// objReusableTextureUUID, part.Name, part.ParentGroup.Scene.Name);
+
+ // No need to add to updaters as the texture is always the same. Not that this functionality
+ // apppears to be implemented anyway.
+ updater.UpdatePart(part, (UUID)objReusableTextureUUID);
+ }
+
+ return updater.UpdaterID;
+ }
+
+ private string GenerateReusableTextureKey(string data, string extraParams)
+ {
+ return string.Format("{0}{1}", data, extraParams);
}
public void GetDrawStringSize(string contentType, string text, string fontName, int fontSize,
@@ -215,6 +327,13 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
public void Initialise(Scene scene, IConfigSource config)
{
+ IConfig texturesConfig = config.Configs["Textures"];
+ if (texturesConfig != null)
+ {
+ ReuseTextures = texturesConfig.GetBoolean("ReuseDynamicTextures", false);
+ ReuseLowDataTextures = texturesConfig.GetBoolean("ReuseDynamicLowDataTextures", false);
+ }
+
if (!RegisteredScenes.ContainsKey(scene.RegionInfo.RegionID))
{
RegisteredScenes.Add(scene.RegionInfo.RegionID, scene);
@@ -224,6 +343,11 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
public void PostInitialise()
{
+ if (ReuseTextures)
+ {
+ m_reuseableDynamicTextures = new Cache(CacheMedium.Memory, CacheStrategy.Conservative);
+ m_reuseableDynamicTextures.DefaultTTL = new TimeSpan(24, 0, 0);
+ }
}
public void Close()
@@ -268,10 +392,61 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
BodyData = null;
}
+ ///
+ /// Update the given part with the new texture.
+ ///
+ ///
+ /// The old texture UUID.
+ ///
+ public UUID UpdatePart(SceneObjectPart part, UUID textureID)
+ {
+ UUID oldID;
+
+ lock (part)
+ {
+ // mostly keep the values from before
+ Primitive.TextureEntry tmptex = part.Shape.Textures;
+
+ // FIXME: Need to return the appropriate ID if only a single face is replaced.
+ oldID = tmptex.DefaultTexture.TextureID;
+
+ if (Face == ALL_SIDES)
+ {
+ oldID = tmptex.DefaultTexture.TextureID;
+ tmptex.DefaultTexture.TextureID = textureID;
+ }
+ else
+ {
+ try
+ {
+ Primitive.TextureEntryFace texface = tmptex.CreateFace((uint)Face);
+ texface.TextureID = textureID;
+ tmptex.FaceTextures[Face] = texface;
+ }
+ catch (Exception)
+ {
+ tmptex.DefaultTexture.TextureID = textureID;
+ }
+ }
+
+ // I'm pretty sure we always want to force this to true
+ // I'm pretty sure noone whats to set fullbright true if it wasn't true before.
+ // tmptex.DefaultTexture.Fullbright = true;
+
+ part.UpdateTextureEntry(tmptex.GetBytes());
+ }
+
+ return oldID;
+ }
+
///
/// Called once new texture data has been received for this updater.
///
- public void DataReceived(byte[] data, Scene scene)
+ ///
+ ///
+ /// True if the data given is reuseable.
+ /// The asset UUID given to the incoming data.
+ public UUID DataReceived(byte[] data, Scene scene)
{
SceneObjectPart part = scene.GetSceneObjectPart(PrimID);
@@ -281,7 +456,8 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
String.Format("DynamicTextureModule: Error preparing image using URL {0}", Url);
scene.SimChat(Utils.StringToBytes(msg), ChatTypeEnum.Say,
0, part.ParentGroup.RootPart.AbsolutePosition, part.Name, part.UUID, false);
- return;
+
+ return UUID.Zero;
}
byte[] assetData = null;
@@ -319,56 +495,29 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface();
if (cacheLayerDecode != null)
{
- cacheLayerDecode.Decode(asset.FullID, asset.Data);
- cacheLayerDecode = null;
+ if (!cacheLayerDecode.Decode(asset.FullID, asset.Data))
+ m_log.WarnFormat(
+ "[DYNAMIC TEXTURE MODULE]: Decoding of dynamically generated asset {0} for {1} in {2} failed",
+ asset.ID, part.Name, part.ParentGroup.Scene.Name);
}
- UUID oldID = UUID.Zero;
-
- lock (part)
- {
- // mostly keep the values from before
- Primitive.TextureEntry tmptex = part.Shape.Textures;
-
- // remove the old asset from the cache
- oldID = tmptex.DefaultTexture.TextureID;
-
- if (Face == ALL_SIDES)
- {
- tmptex.DefaultTexture.TextureID = asset.FullID;
- }
- else
- {
- try
- {
- Primitive.TextureEntryFace texface = tmptex.CreateFace((uint)Face);
- texface.TextureID = asset.FullID;
- tmptex.FaceTextures[Face] = texface;
- }
- catch (Exception)
- {
- tmptex.DefaultTexture.TextureID = asset.FullID;
- }
- }
-
- // I'm pretty sure we always want to force this to true
- // I'm pretty sure noone whats to set fullbright true if it wasn't true before.
- // tmptex.DefaultTexture.Fullbright = true;
-
- part.UpdateTextureEntry(tmptex.GetBytes());
- }
+ UUID oldID = UpdatePart(part, asset.FullID);
if (oldID != UUID.Zero && ((Disp & DISP_EXPIRE) != 0))
{
- if (oldAsset == null) oldAsset = scene.AssetService.Get(oldID.ToString());
+ if (oldAsset == null)
+ oldAsset = scene.AssetService.Get(oldID.ToString());
+
if (oldAsset != null)
{
- if (oldAsset.Temporary == true)
+ if (oldAsset.Temporary)
{
scene.AssetService.Delete(oldID.ToString());
}
}
}
+
+ return asset.FullID;
}
private byte[] BlendTextures(byte[] frontImage, byte[] backImage, bool setNewAlpha, byte newAlpha)
diff --git a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs
index 56221aa9ce..0b9174f799 100644
--- a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs
@@ -58,6 +58,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
public string body;
public int responseCode;
public string responseBody;
+ public string responseType = "text/plain";
//public ManualResetEvent ev;
public bool requestDone;
public int startTime;
@@ -270,6 +271,22 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
}
}
+ public void HttpContentType(UUID request, string type)
+ {
+ lock (m_UrlMap)
+ {
+ if (m_RequestMap.ContainsKey(request))
+ {
+ UrlData urlData = m_RequestMap[request];
+ urlData.requests[request].responseType = type;
+ }
+ else
+ {
+ m_log.Info("[HttpRequestHandler] There is no http-in request with id " + request.ToString());
+ }
+ }
+ }
+
public void HttpResponse(UUID request, int status, string body)
{
lock (m_RequestMap)
diff --git a/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs b/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs
index 6f839485ec..45e652788a 100644
--- a/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs
@@ -32,6 +32,7 @@ using System.Net;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.Imaging;
+using OpenSim.Region.CoreModules.Scripting.DynamicTexture;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using log4net;
@@ -67,12 +68,18 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
return true;
}
- public byte[] ConvertUrl(string url, string extraParams)
+// public bool AlwaysIdenticalConversion(string bodyData, string extraParams)
+// {
+// // We don't support conversion of body data.
+// return false;
+// }
+
+ public IDynamicTexture ConvertUrl(string url, string extraParams)
{
return null;
}
- public byte[] ConvertStream(Stream data, string extraParams)
+ public IDynamicTexture ConvertData(string bodyData, string extraParams)
{
return null;
}
@@ -165,11 +172,11 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
private void HttpRequestReturn(IAsyncResult result)
{
-
RequestState state = (RequestState) result.AsyncState;
WebRequest request = (WebRequest) state.Request;
Stream stream = null;
byte[] imageJ2000 = new byte[0];
+ Size newSize = new Size(0, 0);
try
{
@@ -182,37 +189,43 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
try
{
Bitmap image = new Bitmap(stream);
- Size newsize;
// TODO: make this a bit less hard coded
if ((image.Height < 64) && (image.Width < 64))
{
- newsize = new Size(32, 32);
+ newSize.Width = 32;
+ newSize.Height = 32;
}
else if ((image.Height < 128) && (image.Width < 128))
{
- newsize = new Size(64, 64);
+ newSize.Width = 64;
+ newSize.Height = 64;
}
else if ((image.Height < 256) && (image.Width < 256))
{
- newsize = new Size(128, 128);
+ newSize.Width = 128;
+ newSize.Height = 128;
}
else if ((image.Height < 512 && image.Width < 512))
{
- newsize = new Size(256, 256);
+ newSize.Width = 256;
+ newSize.Height = 256;
}
else if ((image.Height < 1024 && image.Width < 1024))
{
- newsize = new Size(512, 512);
+ newSize.Width = 512;
+ newSize.Height = 512;
}
else
{
- newsize = new Size(1024, 1024);
+ newSize.Width = 1024;
+ newSize.Height = 1024;
}
- Bitmap resize = new Bitmap(image, newsize);
-
- imageJ2000 = OpenJPEG.EncodeFromImage(resize, true);
+ using (Bitmap resize = new Bitmap(image, newSize))
+ {
+ imageJ2000 = OpenJPEG.EncodeFromImage(resize, true);
+ }
}
catch (Exception)
{
@@ -227,7 +240,6 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
}
catch (WebException)
{
-
}
finally
{
@@ -236,9 +248,14 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
stream.Close();
}
}
- m_log.DebugFormat("[LOADIMAGEURLMODULE] Returning {0} bytes of image data for request {1}",
+
+ m_log.DebugFormat("[LOADIMAGEURLMODULE]: Returning {0} bytes of image data for request {1}",
imageJ2000.Length, state.RequestID);
- m_textureManager.ReturnData(state.RequestID, imageJ2000);
+
+ m_textureManager.ReturnData(
+ state.RequestID,
+ new OpenSim.Region.CoreModules.Scripting.DynamicTexture.DynamicTexture(
+ request.RequestUri, null, imageJ2000, newSize, false));
}
#region Nested type: RequestState
diff --git a/OpenSim/Region/OptionalModules/Scripting/ScriptModuleComms/ScriptModuleCommsModule.cs b/OpenSim/Region/CoreModules/Scripting/ScriptModuleComms/ScriptModuleCommsModule.cs
similarity index 79%
rename from OpenSim/Region/OptionalModules/Scripting/ScriptModuleComms/ScriptModuleCommsModule.cs
rename to OpenSim/Region/CoreModules/Scripting/ScriptModuleComms/ScriptModuleCommsModule.cs
index c5c96a979f..dc54c3f821 100644
--- a/OpenSim/Region/OptionalModules/Scripting/ScriptModuleComms/ScriptModuleCommsModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/ScriptModuleComms/ScriptModuleCommsModule.cs
@@ -130,13 +130,25 @@ namespace OpenSim.Region.OptionalModules.Scripting.ScriptModuleComms
m_scriptModule.PostScriptEvent(script, "link_message", args);
}
+ private static MethodInfo GetMethodInfoFromType(Type target, string meth, bool searchInstanceMethods)
+ {
+ BindingFlags getMethodFlags =
+ BindingFlags.NonPublic | BindingFlags.Public;
+
+ if (searchInstanceMethods)
+ getMethodFlags |= BindingFlags.Instance;
+ else
+ getMethodFlags |= BindingFlags.Static;
+
+ return target.GetMethod(meth, getMethodFlags);
+ }
+
public void RegisterScriptInvocation(object target, string meth)
{
- MethodInfo mi = target.GetType().GetMethod(meth,
- BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
+ MethodInfo mi = GetMethodInfoFromType(target.GetType(), meth, true);
if (mi == null)
{
- m_log.WarnFormat("[MODULE COMMANDS] Failed to register method {0}",meth);
+ m_log.WarnFormat("[MODULE COMMANDS] Failed to register method {0}", meth);
return;
}
@@ -151,38 +163,71 @@ namespace OpenSim.Region.OptionalModules.Scripting.ScriptModuleComms
public void RegisterScriptInvocation(object target, MethodInfo mi)
{
- m_log.DebugFormat("[MODULE COMMANDS] Register method {0} from type {1}", mi.Name, target.GetType().Name);
+ m_log.DebugFormat("[MODULE COMMANDS] Register method {0} from type {1}", mi.Name, (target is Type) ? ((Type)target).Name : target.GetType().Name);
Type delegateType;
- var typeArgs = mi.GetParameters()
+ List typeArgs = mi.GetParameters()
.Select(p => p.ParameterType)
.ToList();
if (mi.ReturnType == typeof(void))
{
- delegateType = Expression.GetActionType(typeArgs.ToArray());
+ delegateType = Expression.GetActionType(typeArgs.ToArray());
}
else
{
- typeArgs.Add(mi.ReturnType);
- delegateType = Expression.GetFuncType(typeArgs.ToArray());
+ typeArgs.Add(mi.ReturnType);
+ delegateType = Expression.GetFuncType(typeArgs.ToArray());
}
- Delegate fcall = Delegate.CreateDelegate(delegateType, target, mi);
+ Delegate fcall;
+ if (!(target is Type))
+ fcall = Delegate.CreateDelegate(delegateType, target, mi);
+ else
+ fcall = Delegate.CreateDelegate(delegateType, (Type)target, mi.Name);
lock (m_scriptInvocation)
{
- ParameterInfo[] parameters = fcall.Method.GetParameters ();
+ ParameterInfo[] parameters = fcall.Method.GetParameters();
if (parameters.Length < 2) // Must have two UUID params
return;
// Hide the first two parameters
Type[] parmTypes = new Type[parameters.Length - 2];
- for (int i = 2 ; i < parameters.Length ; i++)
+ for (int i = 2; i < parameters.Length; i++)
parmTypes[i - 2] = parameters[i].ParameterType;
m_scriptInvocation[fcall.Method.Name] = new ScriptInvocationData(fcall.Method.Name, fcall, parmTypes, fcall.Method.ReturnType);
}
}
+
+ public void RegisterScriptInvocation(Type target, string[] methods)
+ {
+ foreach (string method in methods)
+ {
+ MethodInfo mi = GetMethodInfoFromType(target, method, false);
+ if (mi == null)
+ m_log.WarnFormat("[MODULE COMMANDS] Failed to register method {0}", method);
+ else
+ RegisterScriptInvocation(target, mi);
+ }
+ }
+
+ public void RegisterScriptInvocations(IRegionModuleBase target)
+ {
+ foreach(MethodInfo method in target.GetType().GetMethods(
+ BindingFlags.Public | BindingFlags.Instance |
+ BindingFlags.Static))
+ {
+ if(method.GetCustomAttributes(
+ typeof(ScriptInvocationAttribute), true).Any())
+ {
+ if(method.IsStatic)
+ RegisterScriptInvocation(target.GetType(), method);
+ else
+ RegisterScriptInvocation(target, method);
+ }
+ }
+ }
public Delegate[] GetScriptInvocationList()
{
@@ -285,6 +330,20 @@ namespace OpenSim.Region.OptionalModules.Scripting.ScriptModuleComms
}
}
+ public void RegisterConstants(IRegionModuleBase target)
+ {
+ foreach (FieldInfo field in target.GetType().GetFields(
+ BindingFlags.Public | BindingFlags.Static |
+ BindingFlags.Instance))
+ {
+ if (field.GetCustomAttributes(
+ typeof(ScriptConstantAttribute), true).Any())
+ {
+ RegisterConstant(field.Name, field.GetValue(target));
+ }
+ }
+ }
+
///
/// Operation to check for a registered constant
///
diff --git a/OpenSim/Region/CoreModules/Scripting/VectorRender/Tests/VectorRenderModuleTests.cs b/OpenSim/Region/CoreModules/Scripting/VectorRender/Tests/VectorRenderModuleTests.cs
index 9787c8c173..41bacccf9e 100644
--- a/OpenSim/Region/CoreModules/Scripting/VectorRender/Tests/VectorRenderModuleTests.cs
+++ b/OpenSim/Region/CoreModules/Scripting/VectorRender/Tests/VectorRenderModuleTests.cs
@@ -45,31 +45,292 @@ using OpenSim.Tests.Common.Mock;
namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
{
[TestFixture]
- public class VectorRenderModuleTests
+ public class VectorRenderModuleTests : OpenSimTestCase
{
+ Scene m_scene;
+ DynamicTextureModule m_dtm;
+ VectorRenderModule m_vrm;
+
+ private void SetupScene(bool reuseTextures)
+ {
+ m_scene = new SceneHelpers().SetupScene();
+
+ m_dtm = new DynamicTextureModule();
+ m_dtm.ReuseTextures = reuseTextures;
+// m_dtm.ReuseLowDataTextures = reuseTextures;
+
+ m_vrm = new VectorRenderModule();
+
+ SceneHelpers.SetupSceneModules(m_scene, m_dtm, m_vrm);
+ }
+
[Test]
public void TestDraw()
{
TestHelpers.InMethod();
- Scene scene = new SceneHelpers().SetupScene();
- DynamicTextureModule dtm = new DynamicTextureModule();
- VectorRenderModule vrm = new VectorRenderModule();
- SceneHelpers.SetupSceneModules(scene, dtm, vrm);
-
- SceneObjectGroup so = SceneHelpers.AddSceneObject(scene);
+ SetupScene(false);
+ SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
UUID originalTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
- dtm.AddDynamicTextureData(
- scene.RegionInfo.RegionID,
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
so.UUID,
- vrm.GetContentType(),
+ m_vrm.GetContentType(),
"PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;",
"",
0);
+ Assert.That(originalTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
+ }
+
+ [Test]
+ public void TestRepeatSameDraw()
+ {
+ TestHelpers.InMethod();
+
+ string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
+
+ SetupScene(false);
+ SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
+ }
+
+ [Test]
+ public void TestRepeatSameDrawDifferentExtraParams()
+ {
+ TestHelpers.InMethod();
+
+ string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
+
+ SetupScene(false);
+ SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "alpha:250",
+ 0);
+
+ Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
+ }
+
+ [Test]
+ public void TestRepeatSameDrawContainingImage()
+ {
+ TestHelpers.InMethod();
+
+ string dtText
+ = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World; Image http://localhost/shouldnotexist.png";
+
+ SetupScene(false);
+ SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
+ }
+
+ [Test]
+ public void TestDrawReusingTexture()
+ {
+ TestHelpers.InMethod();
+
+ SetupScene(true);
+ SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
+ UUID originalTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;",
+ "",
+ 0);
Assert.That(originalTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
}
+
+ [Test]
+ public void TestRepeatSameDrawReusingTexture()
+ {
+ TestHelpers.InMethod();
+// TestHelpers.EnableLogging();
+
+ string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
+
+ SetupScene(true);
+ SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ Assert.That(firstDynamicTextureID, Is.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
+ }
+
+ ///
+ /// Test a low data dynamically generated texture such that it is treated as a low data texture that causes
+ /// problems for current viewers.
+ ///
+ ///
+ /// As we do not set DynamicTextureModule.ReuseLowDataTextures = true in this test, it should not reuse the
+ /// texture
+ ///
+ [Test]
+ public void TestRepeatSameDrawLowDataTexture()
+ {
+ TestHelpers.InMethod();
+// TestHelpers.EnableLogging();
+
+ string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
+
+ SetupScene(true);
+ SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "1024",
+ 0);
+
+ UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "1024",
+ 0);
+
+ Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
+ }
+
+ [Test]
+ public void TestRepeatSameDrawDifferentExtraParamsReusingTexture()
+ {
+ TestHelpers.InMethod();
+
+ string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
+
+ SetupScene(true);
+ SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "alpha:250",
+ 0);
+
+ Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
+ }
+
+ [Test]
+ public void TestRepeatSameDrawContainingImageReusingTexture()
+ {
+ TestHelpers.InMethod();
+
+ string dtText
+ = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World; Image http://localhost/shouldnotexist.png";
+
+ SetupScene(true);
+ SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
+ }
}
}
\ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs b/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs
index 8b2f2f84e9..673c2d1725 100644
--- a/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs
@@ -30,10 +30,12 @@ using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
+using System.Linq;
using System.Net;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.Imaging;
+using OpenSim.Region.CoreModules.Scripting.DynamicTexture;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using log4net;
@@ -45,9 +47,13 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
{
public class VectorRenderModule : IRegionModule, IDynamicTextureRender
{
+ // These fields exist for testing purposes, please do not remove.
+// private static bool s_flipper;
+// private static byte[] s_asset1Data;
+// private static byte[] s_asset2Data;
+
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
- private string m_name = "VectorRenderModule";
private Scene m_scene;
private IDynamicTextureManager m_textureManager;
private Graphics m_graph;
@@ -61,12 +67,12 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
public string GetContentType()
{
- return ("vector");
+ return "vector";
}
public string GetName()
{
- return m_name;
+ return Name;
}
public bool SupportsAsynchronous()
@@ -74,14 +80,20 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
return true;
}
- public byte[] ConvertUrl(string url, string extraParams)
+// public bool AlwaysIdenticalConversion(string bodyData, string extraParams)
+// {
+// string[] lines = GetLines(bodyData);
+// return lines.Any((str, r) => str.StartsWith("Image"));
+// }
+
+ public IDynamicTexture ConvertUrl(string url, string extraParams)
{
return null;
}
- public byte[] ConvertStream(Stream data, string extraParams)
+ public IDynamicTexture ConvertData(string bodyData, string extraParams)
{
- return null;
+ return Draw(bodyData, extraParams);
}
public bool AsyncConvertUrl(UUID id, string url, string extraParams)
@@ -91,21 +103,28 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
public bool AsyncConvertData(UUID id, string bodyData, string extraParams)
{
- Draw(bodyData, id, extraParams);
+ // XXX: This isn't actually being done asynchronously!
+ m_textureManager.ReturnData(id, ConvertData(bodyData, extraParams));
+
return true;
}
public void GetDrawStringSize(string text, string fontName, int fontSize,
out double xSize, out double ySize)
{
- using (Font myFont = new Font(fontName, fontSize))
+ lock (this)
{
- SizeF stringSize = new SizeF();
- lock (m_graph)
+ using (Font myFont = new Font(fontName, fontSize))
{
- stringSize = m_graph.MeasureString(text, myFont);
- xSize = stringSize.Width;
- ySize = stringSize.Height;
+ SizeF stringSize = new SizeF();
+
+ // XXX: This lock may be unnecessary.
+ lock (m_graph)
+ {
+ stringSize = m_graph.MeasureString(text, myFont);
+ xSize = stringSize.Width;
+ ySize = stringSize.Height;
+ }
}
}
}
@@ -144,6 +163,13 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
{
m_textureManager.RegisterRender(GetContentType(), this);
}
+
+ // This code exists for testing purposes, please do not remove.
+// s_asset1Data = m_scene.AssetService.Get("00000000-0000-1111-9999-000000000001").Data;
+// s_asset1Data = m_scene.AssetService.Get("9f4acf0d-1841-4e15-bdb8-3a12efc9dd8f").Data;
+
+ // Terrain dirt - smallest bin/assets file (6004 bytes)
+// s_asset2Data = m_scene.AssetService.Get("b8d3965a-ad78-bf43-699b-bff8eca6c975").Data;
}
public void Close()
@@ -152,7 +178,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
public string Name
{
- get { return m_name; }
+ get { return "VectorRenderModule"; }
}
public bool IsSharedModule
@@ -162,7 +188,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
#endregion
- private void Draw(string data, UUID id, string extraParams)
+ private IDynamicTexture Draw(string data, string extraParams)
{
// We need to cater for old scripts that didnt use extraParams neatly, they use either an integer size which represents both width and height, or setalpha
// we will now support multiple comma seperated params in the form width:256,height:512,alpha:255
@@ -305,40 +331,57 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
Bitmap bitmap = null;
Graphics graph = null;
+ bool reuseable = false;
try
{
- if (alpha == 256)
- bitmap = new Bitmap(width, height, PixelFormat.Format32bppRgb);
- else
- bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
-
- graph = Graphics.FromImage(bitmap);
-
- // this is really just to save people filling the
- // background color in their scripts, only do when fully opaque
- if (alpha >= 255)
+ // XXX: In testing, it appears that if multiple threads dispose of separate GDI+ objects simultaneously,
+ // the native malloc heap can become corrupted, possibly due to a double free(). This may be due to
+ // bugs in the underlying libcairo used by mono's libgdiplus.dll on Linux/OSX. These problems were
+ // seen with both libcario 1.10.2-6.1ubuntu3 and 1.8.10-2ubuntu1. They go away if disposal is perfomed
+ // under lock.
+ lock (this)
{
- using (SolidBrush bgFillBrush = new SolidBrush(bgColor))
- {
- graph.FillRectangle(bgFillBrush, 0, 0, width, height);
- }
- }
+ if (alpha == 256)
+ bitmap = new Bitmap(width, height, PixelFormat.Format32bppRgb);
+ else
+ bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
- for (int w = 0; w < bitmap.Width; w++)
- {
- if (alpha <= 255)
+ graph = Graphics.FromImage(bitmap);
+
+ // this is really just to save people filling the
+ // background color in their scripts, only do when fully opaque
+ if (alpha >= 255)
{
- for (int h = 0; h < bitmap.Height; h++)
+ using (SolidBrush bgFillBrush = new SolidBrush(bgColor))
{
- bitmap.SetPixel(w, h, Color.FromArgb(alpha, bitmap.GetPixel(w, h)));
+ graph.FillRectangle(bgFillBrush, 0, 0, width, height);
}
}
+
+ for (int w = 0; w < bitmap.Width; w++)
+ {
+ if (alpha <= 255)
+ {
+ for (int h = 0; h < bitmap.Height; h++)
+ {
+ bitmap.SetPixel(w, h, Color.FromArgb(alpha, bitmap.GetPixel(w, h)));
+ }
+ }
+ }
+
+ GDIDraw(data, graph, altDataDelim, out reuseable);
}
- GDIDraw(data, graph, altDataDelim);
-
byte[] imageJ2000 = new byte[0];
+
+ // This code exists for testing purposes, please do not remove.
+// if (s_flipper)
+// imageJ2000 = s_asset1Data;
+// else
+// imageJ2000 = s_asset2Data;
+//
+// s_flipper = !s_flipper;
try
{
@@ -351,15 +394,24 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
e.Message, e.StackTrace);
}
- m_textureManager.ReturnData(id, imageJ2000);
+ return new OpenSim.Region.CoreModules.Scripting.DynamicTexture.DynamicTexture(
+ data, extraParams, imageJ2000, new Size(width, height), reuseable);
}
finally
{
- if (graph != null)
- graph.Dispose();
-
- if (bitmap != null)
- bitmap.Dispose();
+ // XXX: In testing, it appears that if multiple threads dispose of separate GDI+ objects simultaneously,
+ // the native malloc heap can become corrupted, possibly due to a double free(). This may be due to
+ // bugs in the underlying libcairo used by mono's libgdiplus.dll on Linux/OSX. These problems were
+ // seen with both libcario 1.10.2-6.1ubuntu3 and 1.8.10-2ubuntu1. They go away if disposal is perfomed
+ // under lock.
+ lock (this)
+ {
+ if (graph != null)
+ graph.Dispose();
+
+ if (bitmap != null)
+ bitmap.Dispose();
+ }
}
}
@@ -418,8 +470,21 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
}
*/
- private void GDIDraw(string data, Graphics graph, char dataDelim)
+ ///
+ /// Split input data into discrete command lines.
+ ///
+ ///
+ ///
+ ///
+ private string[] GetLines(string data, char dataDelim)
{
+ char[] lineDelimiter = { dataDelim };
+ return data.Split(lineDelimiter);
+ }
+
+ private void GDIDraw(string data, Graphics graph, char dataDelim, out bool reuseable)
+ {
+ reuseable = true;
Point startPoint = new Point(0, 0);
Point endPoint = new Point(0, 0);
Pen drawPen = null;
@@ -434,11 +499,9 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
myFont = new Font(fontName, fontSize);
myBrush = new SolidBrush(Color.Black);
- char[] lineDelimiter = {dataDelim};
char[] partsDelimiter = {','};
- string[] lines = data.Split(lineDelimiter);
- foreach (string line in lines)
+ foreach (string line in GetLines(data, dataDelim))
{
string nextLine = line.Trim();
//replace with switch, or even better, do some proper parsing
@@ -469,6 +532,10 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
}
else if (nextLine.StartsWith("Image"))
{
+ // We cannot reuse any generated texture involving fetching an image via HTTP since that image
+ // can change.
+ reuseable = false;
+
float x = 0;
float y = 0;
GetParams(partsDelimiter, ref nextLine, 5, ref x, ref y);
diff --git a/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs b/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs
index 07bb291241..e167e312da 100644
--- a/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs
@@ -28,6 +28,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Text.RegularExpressions;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Framework;
@@ -172,12 +173,42 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
/// UUID of the SceneObjectPart
/// channel to listen on
/// name to filter on
- /// key to filter on (user given, could be totally faked)
+ ///
+ /// key to filter on (user given, could be totally faked)
+ ///
/// msg to filter on
/// number of the scripts handle
- public int Listen(uint localID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg)
+ public int Listen(uint localID, UUID itemID, UUID hostID, int channel,
+ string name, UUID id, string msg)
{
- return m_listenerManager.AddListener(localID, itemID, hostID, channel, name, id, msg);
+ return m_listenerManager.AddListener(localID, itemID, hostID,
+ channel, name, id, msg);
+ }
+
+ ///
+ /// Create a listen event callback with the specified filters.
+ /// The parameters localID,itemID are needed to uniquely identify
+ /// the script during 'peek' time. Parameter hostID is needed to
+ /// determine the position of the script.
+ ///
+ /// localID of the script engine
+ /// UUID of the script engine
+ /// UUID of the SceneObjectPart
+ /// channel to listen on
+ /// name to filter on
+ ///
+ /// key to filter on (user given, could be totally faked)
+ ///
+ /// msg to filter on
+ ///
+ /// Bitfield indicating which strings should be processed as regex.
+ ///
+ /// number of the scripts handle
+ public int Listen(uint localID, UUID itemID, UUID hostID, int channel,
+ string name, UUID id, string msg, int regexBitfield)
+ {
+ return m_listenerManager.AddListener(localID, itemID, hostID,
+ channel, name, id, msg, regexBitfield);
}
///
@@ -326,7 +357,7 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
if (channel == 0)
{
// Channel 0 goes to viewer ONLY
- m_scene.SimChat(Utils.StringToBytes(msg), ChatTypeEnum.Broadcast, 0, pos, name, id, false, false, target);
+ m_scene.SimChat(Utils.StringToBytes(msg), ChatTypeEnum.Broadcast, 0, pos, name, id, target, false, false);
return true;
}
@@ -470,15 +501,25 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
m_curlisteners = 0;
}
- public int AddListener(uint localID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg)
+ public int AddListener(uint localID, UUID itemID, UUID hostID,
+ int channel, string name, UUID id, string msg)
+ {
+ return AddListener(localID, itemID, hostID, channel, name, id,
+ msg, 0);
+ }
+
+ public int AddListener(uint localID, UUID itemID, UUID hostID,
+ int channel, string name, UUID id, string msg,
+ int regexBitfield)
{
// do we already have a match on this particular filter event?
- List coll = GetListeners(itemID, channel, name, id, msg);
+ List coll = GetListeners(itemID, channel, name, id,
+ msg);
if (coll.Count > 0)
{
- // special case, called with same filter settings, return same handle
- // (2008-05-02, tested on 1.21.1 server, still holds)
+ // special case, called with same filter settings, return same
+ // handle (2008-05-02, tested on 1.21.1 server, still holds)
return coll[0].GetHandle();
}
@@ -490,7 +531,9 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
if (newHandle > 0)
{
- ListenerInfo li = new ListenerInfo(newHandle, localID, itemID, hostID, channel, name, id, msg);
+ ListenerInfo li = new ListenerInfo(newHandle, localID,
+ itemID, hostID, channel, name, id, msg,
+ regexBitfield);
List listeners;
if (!m_listeners.TryGetValue(channel,out listeners))
@@ -631,6 +674,22 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
return -1;
}
+ /// These are duplicated from ScriptBaseClass
+ /// http://opensimulator.org/mantis/view.php?id=6106#c21945
+ #region Constants for the bitfield parameter of osListenRegex
+
+ ///
+ /// process name parameter as regex
+ ///
+ public const int OS_LISTEN_REGEX_NAME = 0x1;
+
+ ///
+ /// process message parameter as regex
+ ///
+ public const int OS_LISTEN_REGEX_MESSAGE = 0x2;
+
+ #endregion
+
// Theres probably a more clever and efficient way to
// do this, maybe with regex.
// PM2008: Ha, one could even be smart and define a specialized Enumerator.
@@ -656,7 +715,10 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
{
continue;
}
- if (li.GetName().Length > 0 && !li.GetName().Equals(name))
+ if (li.GetName().Length > 0 && (
+ ((li.RegexBitfield & OS_LISTEN_REGEX_NAME) != OS_LISTEN_REGEX_NAME && !li.GetName().Equals(name)) ||
+ ((li.RegexBitfield & OS_LISTEN_REGEX_NAME) == OS_LISTEN_REGEX_NAME && !Regex.IsMatch(name, li.GetName()))
+ ))
{
continue;
}
@@ -664,7 +726,10 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
{
continue;
}
- if (li.GetMessage().Length > 0 && !li.GetMessage().Equals(msg))
+ if (li.GetMessage().Length > 0 && (
+ ((li.RegexBitfield & OS_LISTEN_REGEX_MESSAGE) != OS_LISTEN_REGEX_MESSAGE && !li.GetMessage().Equals(msg)) ||
+ ((li.RegexBitfield & OS_LISTEN_REGEX_MESSAGE) == OS_LISTEN_REGEX_MESSAGE && !Regex.IsMatch(msg, li.GetMessage()))
+ ))
{
continue;
}
@@ -697,10 +762,13 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
{
int idx = 0;
Object[] item = new Object[6];
+ int dataItemLength = 6;
while (idx < data.Length)
{
- Array.Copy(data, idx, item, 0, 6);
+ dataItemLength = (idx + 7 == data.Length || (idx + 7 < data.Length && data[idx + 7] is bool)) ? 7 : 6;
+ item = new Object[dataItemLength];
+ Array.Copy(data, idx, item, 0, dataItemLength);
ListenerInfo info =
ListenerInfo.FromData(localID, itemID, hostID, item);
@@ -712,12 +780,12 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
m_listeners[(int)item[2]].Add(info);
}
- idx+=6;
+ idx+=dataItemLength;
}
}
}
- public class ListenerInfo: IWorldCommListenerInfo
+ public class ListenerInfo : IWorldCommListenerInfo
{
private bool m_active; // Listener is active or not
private int m_handle; // Assigned handle of this listener
@@ -731,16 +799,29 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
public ListenerInfo(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, UUID id, string message)
{
- Initialise(handle, localID, ItemID, hostID, channel, name, id, message);
+ Initialise(handle, localID, ItemID, hostID, channel, name, id,
+ message, 0);
+ }
+
+ public ListenerInfo(int handle, uint localID, UUID ItemID,
+ UUID hostID, int channel, string name, UUID id,
+ string message, int regexBitfield)
+ {
+ Initialise(handle, localID, ItemID, hostID, channel, name, id,
+ message, regexBitfield);
}
public ListenerInfo(ListenerInfo li, string name, UUID id, string message)
{
- Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message);
+ Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message, 0);
}
- private void Initialise(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name,
- UUID id, string message)
+ public ListenerInfo(ListenerInfo li, string name, UUID id, string message, int regexBitfield)
+ {
+ Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message, regexBitfield);
+ }
+
+ private void Initialise(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, UUID id, string message, int regexBitfield)
{
m_active = true;
m_handle = handle;
@@ -751,11 +832,12 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
m_name = name;
m_id = id;
m_message = message;
+ RegexBitfield = regexBitfield;
}
public Object[] GetSerializationData()
{
- Object[] data = new Object[6];
+ Object[] data = new Object[7];
data[0] = m_active;
data[1] = m_handle;
@@ -763,16 +845,19 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
data[3] = m_name;
data[4] = m_id;
data[5] = m_message;
+ data[6] = RegexBitfield;
return data;
}
public static ListenerInfo FromData(uint localID, UUID ItemID, UUID hostID, Object[] data)
{
- ListenerInfo linfo = new ListenerInfo((int)data[1], localID,
- ItemID, hostID, (int)data[2], (string)data[3],
- (UUID)data[4], (string)data[5]);
- linfo.m_active=(bool)data[0];
+ ListenerInfo linfo = new ListenerInfo((int)data[1], localID, ItemID, hostID, (int)data[2], (string)data[3], (UUID)data[4], (string)data[5]);
+ linfo.m_active = (bool)data[0];
+ if (data.Length >= 7)
+ {
+ linfo.RegexBitfield = (int)data[6];
+ }
return linfo;
}
@@ -831,5 +916,7 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm
{
return m_id;
}
+
+ public int RegexBitfield { get; private set; }
}
}
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs
index 008465fcc9..1e1c7d00e0 100644
--- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs
+++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs
@@ -56,6 +56,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
private bool m_Enabled = false;
+ private AssetPermissions m_AssetPerms;
+
public Type ReplaceableInterface
{
get { return null; }
@@ -128,6 +130,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
if (m_LocalAssetServiceURI != string.Empty)
m_LocalAssetServiceURI = m_LocalAssetServiceURI.Trim('/');
+ IConfig hgConfig = source.Configs["HGAssetService"];
+ m_AssetPerms = new AssetPermissions(hgConfig); // it's ok if arg is null
+
m_Enabled = true;
m_log.Info("[HG ASSET CONNECTOR]: HG asset broker enabled");
}
@@ -206,14 +211,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
asset = m_HGService.Get(id);
if (asset != null)
{
- // Now store it locally
- // For now, let me just do it for textures and scripts
- if (((AssetType)asset.Type == AssetType.Texture) ||
- ((AssetType)asset.Type == AssetType.LSLBytecode) ||
- ((AssetType)asset.Type == AssetType.LSLText))
- {
+ // Now store it locally, if allowed
+ if (m_AssetPerms.AllowedImport(asset.Type))
m_GridService.Store(asset);
- }
+ else
+ return null;
}
}
else
@@ -328,7 +330,12 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
string id = string.Empty;
if (IsHG(asset.ID))
- id = m_HGService.Store(asset);
+ {
+ if (m_AssetPerms.AllowedExport(asset.Type))
+ id = m_HGService.Store(asset);
+ else
+ return String.Empty;
+ }
else
id = m_GridService.Store(asset);
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs
index c78915f970..449c1f1108 100644
--- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs
+++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs
@@ -204,8 +204,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
public byte[] GetData(string id)
{
// m_log.DebugFormat("[LOCAL ASSET SERVICES CONNECTOR]: Requesting data for asset {0}", id);
-
- AssetBase asset = m_Cache.Get(id);
+
+ AssetBase asset = null;
+
+ if (m_Cache != null)
+ asset = m_Cache.Get(id);
if (asset != null)
return asset.Data;
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/Tests/AssetConnectorTests.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/Tests/AssetConnectorTests.cs
new file mode 100644
index 0000000000..198247317b
--- /dev/null
+++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/Tests/AssetConnectorTests.cs
@@ -0,0 +1,136 @@
+/*
+ * 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.IO;
+using System.Reflection;
+using System.Threading;
+using log4net.Config;
+using Nini.Config;
+using NUnit.Framework;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset;
+using OpenSim.Tests.Common;
+
+namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset.Tests
+{
+ [TestFixture]
+ public class AssetConnectorsTests : OpenSimTestCase
+ {
+ [Test]
+ public void TestAddAsset()
+ {
+ TestHelpers.InMethod();
+// TestHelpers.EnableLogging();
+
+ IConfigSource config = new IniConfigSource();
+ config.AddConfig("Modules");
+ config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
+ config.AddConfig("AssetService");
+ config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
+ config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
+
+ LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
+ lasc.Initialise(config);
+
+ AssetBase a1 = AssetHelpers.CreateNotecardAsset();
+ lasc.Store(a1);
+
+ AssetBase retreivedA1 = lasc.Get(a1.ID);
+ Assert.That(retreivedA1.ID, Is.EqualTo(a1.ID));
+ Assert.That(retreivedA1.Metadata.ID, Is.EqualTo(a1.Metadata.ID));
+ Assert.That(retreivedA1.Data.Length, Is.EqualTo(a1.Data.Length));
+
+ AssetMetadata retrievedA1Metadata = lasc.GetMetadata(a1.ID);
+ Assert.That(retrievedA1Metadata.ID, Is.EqualTo(a1.ID));
+
+ byte[] retrievedA1Data = lasc.GetData(a1.ID);
+ Assert.That(retrievedA1Data.Length, Is.EqualTo(a1.Data.Length));
+
+ // TODO: Add cache and check that this does receive a copy of the asset
+ }
+
+ [Test]
+ public void TestAddTemporaryAsset()
+ {
+ TestHelpers.InMethod();
+// TestHelpers.EnableLogging();
+
+ IConfigSource config = new IniConfigSource();
+ config.AddConfig("Modules");
+ config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
+ config.AddConfig("AssetService");
+ config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
+ config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
+
+ LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
+ lasc.Initialise(config);
+
+ AssetBase a1 = AssetHelpers.CreateNotecardAsset();
+ a1.Temporary = true;
+
+ lasc.Store(a1);
+
+ Assert.That(lasc.Get(a1.ID), Is.Null);
+ Assert.That(lasc.GetData(a1.ID), Is.Null);
+ Assert.That(lasc.GetMetadata(a1.ID), Is.Null);
+
+ // TODO: Add cache and check that this does receive a copy of the asset
+ }
+
+ [Test]
+ public void TestAddLocalAsset()
+ {
+ TestHelpers.InMethod();
+// TestHelpers.EnableLogging();
+
+ IConfigSource config = new IniConfigSource();
+ config.AddConfig("Modules");
+ config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
+ config.AddConfig("AssetService");
+ config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
+ config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
+
+ LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
+ lasc.Initialise(config);
+
+ AssetBase a1 = AssetHelpers.CreateNotecardAsset();
+ a1.Local = true;
+
+ lasc.Store(a1);
+
+ Assert.That(lasc.Get(a1.ID), Is.Null);
+ Assert.That(lasc.GetData(a1.ID), Is.Null);
+ Assert.That(lasc.GetMetadata(a1.ID), Is.Null);
+
+ // TODO: Add cache and check that this does receive a copy of the asset
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/Tests/GridConnectorsTests.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/Tests/GridConnectorsTests.cs
index b286d172e4..43381337c7 100644
--- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/Tests/GridConnectorsTests.cs
+++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/Tests/GridConnectorsTests.cs
@@ -43,11 +43,15 @@ using OpenSim.Tests.Common;
namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid.Tests
{
[TestFixture]
- public class GridConnectorsTests
+ public class GridConnectorsTests : OpenSimTestCase
{
LocalGridServicesConnector m_LocalConnector;
- private void SetUp()
+
+ [SetUp]
+ public override void SetUp()
{
+ base.SetUp();
+
IConfigSource config = new IniConfigSource();
config.AddConfig("Modules");
config.AddConfig("GridService");
@@ -71,8 +75,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid.Tests
TestHelpers.InMethod();
// log4net.Config.XmlConfigurator.Configure();
- SetUp();
-
// Create 4 regions
GridRegion r1 = new GridRegion();
r1.RegionName = "Test Region 1";
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs
index b0edce7a91..221f81584d 100644
--- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs
+++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs
@@ -65,11 +65,13 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser
public void OnMakeRootAgent(ScenePresence sp)
{
-// m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected root presence {0} in {1}", sp.UUID, sp.Scene.RegionInfo.RegionName);
-
if (sp.PresenceType != PresenceType.Npc)
+ {
+ string userid = sp.Scene.UserManagementModule.GetUserUUI(sp.UUID);
+ //m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected root presence {0} in {1}", userid, sp.Scene.RegionInfo.RegionName);
m_GridUserService.SetLastPosition(
- sp.UUID.ToString(), UUID.Zero, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat);
+ userid, UUID.Zero, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat);
+ }
}
public void OnNewClient(IClientAPI client)
@@ -82,9 +84,16 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser
if (client.SceneAgent.IsChildAgent)
return;
-// m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName);
+ string userId = client.AgentId.ToString();
+ if (client.Scene is Scene)
+ {
+ Scene s = (Scene)client.Scene;
+ userId = s.UserManagementModule.GetUserUUI(client.AgentId);
+ }
+ //m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected client logout {0} in {1}", userId, client.Scene.RegionInfo.RegionName);
+
m_GridUserService.LoggedOut(
- client.AgentId.ToString(), client.SessionId, client.Scene.RegionInfo.RegionID,
+ userId, client.SessionId, client.Scene.RegionInfo.RegionID,
client.SceneAgent.AbsolutePosition, client.SceneAgent.Lookat);
}
}
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs
index badb5527fa..04acf6732f 100644
--- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs
+++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs
@@ -44,6 +44,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+ private const int KEEPTIME = 30; // 30 secs
+ private ExpiringCache m_Infos = new ExpiringCache();
+
#region ISharedRegionModule
private bool m_Enabled = false;
@@ -128,23 +131,60 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser
public bool LoggedOut(string userID, UUID sessionID, UUID region, Vector3 position, Vector3 lookat)
{
+ if (m_Infos.Contains(userID))
+ m_Infos.Remove(userID);
+
return m_RemoteConnector.LoggedOut(userID, sessionID, region, position, lookat);
}
public bool SetHome(string userID, UUID regionID, Vector3 position, Vector3 lookAt)
{
- return m_RemoteConnector.SetHome(userID, regionID, position, lookAt);
+ if (m_RemoteConnector.SetHome(userID, regionID, position, lookAt))
+ {
+ // Update the cache too
+ GridUserInfo info = null;
+ if (m_Infos.TryGetValue(userID, out info))
+ {
+ info.HomeRegionID = regionID;
+ info.HomePosition = position;
+ info.HomeLookAt = lookAt;
+ }
+ return true;
+ }
+
+ return false;
}
public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt)
{
- return m_RemoteConnector.SetLastPosition(userID, sessionID, regionID, position, lookAt);
+ if (m_RemoteConnector.SetLastPosition(userID, sessionID, regionID, position, lookAt))
+ {
+ // Update the cache too
+ GridUserInfo info = null;
+ if (m_Infos.TryGetValue(userID, out info))
+ {
+ info.LastRegionID = regionID;
+ info.LastPosition = position;
+ info.LastLookAt = lookAt;
+ }
+ return true;
+ }
+
+ return false;
}
public GridUserInfo GetGridUserInfo(string userID)
{
- return m_RemoteConnector.GetGridUserInfo(userID);
+ GridUserInfo info = null;
+ if (m_Infos.TryGetValue(userID, out info))
+ return info;
+
+ info = m_RemoteConnector.GetGridUserInfo(userID);
+
+ m_Infos.AddOrUpdate(userID, info, KEEPTIME);
+
+ return info;
}
public GridUserInfo[] GetGridUserInfo(string[] userID)
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs
index 6eb99ea710..8ed1833aca 100644
--- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs
+++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs
@@ -313,7 +313,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation
if (m_scenes.ContainsKey(destination.RegionID))
{
- Util.FireAndForget(delegate { m_scenes[destination.RegionID].IncomingCloseAgent(id); });
+// m_log.DebugFormat(
+// "[LOCAL SIMULATION CONNECTOR]: Found region {0} {1} to send AgentUpdate",
+// s.RegionInfo.RegionName, destination.RegionHandle);
+
+ Util.FireAndForget(delegate { m_scenes[destination.RegionID].IncomingCloseAgent(id, false); });
return true;
}
//m_log.Debug("[LOCAL COMMS]: region not found in SendCloseAgent");
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
index 619550c1c5..ade5e76b99 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
@@ -43,6 +43,7 @@ using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Services.Interfaces;
+using System.Threading;
namespace OpenSim.Region.CoreModules.World.Archiver
{
@@ -52,7 +53,30 @@ namespace OpenSim.Region.CoreModules.World.Archiver
public class ArchiveReadRequest
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ ///
+ /// Contains data used while dearchiving a single scene.
+ ///
+ private class DearchiveContext
+ {
+ public Scene Scene { get; set; }
+
+ public List SerialisedSceneObjects { get; set; }
+
+ public List SerialisedParcels { get; set; }
+
+ public List SceneObjects { get; set; }
+
+ public DearchiveContext(Scene scene)
+ {
+ Scene = scene;
+ SerialisedSceneObjects = new List();
+ SerialisedParcels = new List();
+ SceneObjects = new List();
+ }
+ }
+
///
/// The maximum major version of OAR that we can read. Minor versions shouldn't need a max number since version
/// bumps here should be compatible.
@@ -62,9 +86,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver
///
/// Has the control file been loaded for this archive?
///
- public bool ControlFileLoaded { get; private set; }
+ public bool ControlFileLoaded { get; private set; }
- protected Scene m_scene;
+ protected string m_loadPath;
+ protected Scene m_rootScene;
protected Stream m_loadStream;
protected Guid m_requestId;
protected string m_errorMessage;
@@ -91,16 +116,27 @@ namespace OpenSim.Region.CoreModules.World.Archiver
{
if (m_UserMan == null)
{
- m_UserMan = m_scene.RequestModuleInterface();
+ m_UserMan = m_rootScene.RequestModuleInterface();
}
return m_UserMan;
}
}
+ ///
+ /// Used to cache lookups for valid groups.
+ ///
+ private IDictionary m_validGroupUuids = new Dictionary();
+
+ private IGroupsModule m_groupsModule;
+
+ private IAssetService m_assetService = null;
+
+
public ArchiveReadRequest(Scene scene, string loadPath, bool merge, bool skipAssets, Guid requestId)
{
- m_scene = scene;
+ m_rootScene = scene;
+ m_loadPath = loadPath;
try
{
m_loadStream = new GZipStream(ArchiveHelpers.GetStream(loadPath), CompressionMode.Decompress);
@@ -120,11 +156,15 @@ namespace OpenSim.Region.CoreModules.World.Archiver
// Zero can never be a valid user id
m_validUserUuids[UUID.Zero] = false;
+
+ m_groupsModule = m_rootScene.RequestModuleInterface();
+ m_assetService = m_rootScene.AssetService;
}
public ArchiveReadRequest(Scene scene, Stream loadStream, bool merge, bool skipAssets, Guid requestId)
{
- m_scene = scene;
+ m_rootScene = scene;
+ m_loadPath = null;
m_loadStream = loadStream;
m_merge = merge;
m_skipAssets = skipAssets;
@@ -132,32 +172,35 @@ namespace OpenSim.Region.CoreModules.World.Archiver
// Zero can never be a valid user id
m_validUserUuids[UUID.Zero] = false;
+
+ m_groupsModule = m_rootScene.RequestModuleInterface();
+ m_assetService = m_rootScene.AssetService;
}
///
/// Dearchive the region embodied in this request.
///
public void DearchiveRegion()
- {
- // The same code can handle dearchiving 0.1 and 0.2 OpenSim Archive versions
- DearchiveRegion0DotStar();
- }
-
- private void DearchiveRegion0DotStar()
{
int successfulAssetRestores = 0;
int failedAssetRestores = 0;
- List serialisedSceneObjects = new List();
- List serialisedParcels = new List();
- string filePath = "NONE";
- TarArchiveReader archive = new TarArchiveReader(m_loadStream);
+ DearchiveScenesInfo dearchivedScenes;
+
+ // We dearchive all the scenes at once, because the files in the TAR archive might be mixed.
+ // Therefore, we have to keep track of the dearchive context of all the scenes.
+ Dictionary sceneContexts = new Dictionary();
+
+ string fullPath = "NONE";
+ TarArchiveReader archive = null;
byte[] data;
TarArchiveReader.TarEntryType entryType;
-
+
try
{
- while ((data = archive.ReadEntry(out filePath, out entryType)) != null)
+ FindAndLoadControlFile(out archive, out dearchivedScenes);
+
+ while ((data = archive.ReadEntry(out fullPath, out entryType)) != null)
{
//m_log.DebugFormat(
// "[ARCHIVER]: Successfully read {0} ({1} bytes)", filePath, data.Length);
@@ -165,9 +208,30 @@ namespace OpenSim.Region.CoreModules.World.Archiver
if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType)
continue;
+
+ // Find the scene that this file belongs to
+
+ Scene scene;
+ string filePath;
+ if (!dearchivedScenes.GetRegionFromPath(fullPath, out scene, out filePath))
+ continue; // this file belongs to a region that we're not loading
+
+ DearchiveContext sceneContext = null;
+ if (scene != null)
+ {
+ if (!sceneContexts.TryGetValue(scene.RegionInfo.RegionID, out sceneContext))
+ {
+ sceneContext = new DearchiveContext(scene);
+ sceneContexts.Add(scene.RegionInfo.RegionID, sceneContext);
+ }
+ }
+
+
+ // Process the file
+
if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH))
{
- serialisedSceneObjects.Add(Encoding.UTF8.GetString(data));
+ sceneContext.SerialisedSceneObjects.Add(Encoding.UTF8.GetString(data));
}
else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH) && !m_skipAssets)
{
@@ -181,19 +245,19 @@ namespace OpenSim.Region.CoreModules.World.Archiver
}
else if (!m_merge && filePath.StartsWith(ArchiveConstants.TERRAINS_PATH))
{
- LoadTerrain(filePath, data);
+ LoadTerrain(scene, filePath, data);
}
else if (!m_merge && filePath.StartsWith(ArchiveConstants.SETTINGS_PATH))
{
- LoadRegionSettings(filePath, data);
+ LoadRegionSettings(scene, filePath, data, dearchivedScenes);
}
else if (!m_merge && filePath.StartsWith(ArchiveConstants.LANDDATA_PATH))
{
- serialisedParcels.Add(Encoding.UTF8.GetString(data));
+ sceneContext.SerialisedParcels.Add(Encoding.UTF8.GetString(data));
}
else if (filePath == ArchiveConstants.CONTROL_FILE_PATH)
{
- LoadControlFile(filePath, data);
+ // Ignore, because we already read the control file
}
}
@@ -201,15 +265,16 @@ namespace OpenSim.Region.CoreModules.World.Archiver
}
catch (Exception e)
{
- m_log.ErrorFormat(
- "[ARCHIVER]: Aborting load with error in archive file {0}. {1}", filePath, e);
+ m_log.Error(
+ String.Format("[ARCHIVER]: Aborting load with error in archive file {0} ", fullPath), e);
m_errorMessage += e.ToString();
- m_scene.EventManager.TriggerOarFileLoaded(m_requestId, m_errorMessage);
+ m_rootScene.EventManager.TriggerOarFileLoaded(m_requestId, new List(), m_errorMessage);
return;
}
finally
{
- archive.Close();
+ if (archive != null)
+ archive.Close();
}
if (!m_skipAssets)
@@ -223,32 +288,143 @@ namespace OpenSim.Region.CoreModules.World.Archiver
}
}
- if (!m_merge)
+ foreach (DearchiveContext sceneContext in sceneContexts.Values)
{
- m_log.Info("[ARCHIVER]: Clearing all existing scene objects");
- m_scene.DeleteAllSceneObjects();
+ m_log.InfoFormat("[ARCHIVER]: Loading region {0}", sceneContext.Scene.RegionInfo.RegionName);
+
+ if (!m_merge)
+ {
+ m_log.Info("[ARCHIVER]: Clearing all existing scene objects");
+ sceneContext.Scene.DeleteAllSceneObjects();
+ }
+
+ try
+ {
+ LoadParcels(sceneContext.Scene, sceneContext.SerialisedParcels);
+ LoadObjects(sceneContext.Scene, sceneContext.SerialisedSceneObjects, sceneContext.SceneObjects);
+
+ // Inform any interested parties that the region has changed. We waited until now so that all
+ // of the region's objects will be loaded when we send this notification.
+ IEstateModule estateModule = sceneContext.Scene.RequestModuleInterface();
+ if (estateModule != null)
+ estateModule.TriggerRegionInfoChange();
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[ARCHIVER]: Error loading parcels or objects ", e);
+ m_errorMessage += e.ToString();
+ m_rootScene.EventManager.TriggerOarFileLoaded(m_requestId, new List(), m_errorMessage);
+ return;
+ }
}
- LoadParcels(serialisedParcels);
- LoadObjects(serialisedSceneObjects);
+ // Start the scripts. We delayed this because we want the OAR to finish loading ASAP, so
+ // that users can enter the scene. If we allow the scripts to start in the loop above
+ // then they significantly increase the time until the OAR finishes loading.
+ Util.FireAndForget(delegate(object o)
+ {
+ Thread.Sleep(15000);
+ m_log.Info("[ARCHIVER]: Starting scripts in scene objects");
+
+ foreach (DearchiveContext sceneContext in sceneContexts.Values)
+ {
+ foreach (SceneObjectGroup sceneObject in sceneContext.SceneObjects)
+ {
+ sceneObject.CreateScriptInstances(0, false, sceneContext.Scene.DefaultScriptEngine, 0); // StateSource.RegionStart
+ sceneObject.ResumeScripts();
+ }
+
+ sceneContext.SceneObjects.Clear();
+ }
+ });
m_log.InfoFormat("[ARCHIVER]: Successfully loaded archive");
- m_scene.EventManager.TriggerOarFileLoaded(m_requestId, m_errorMessage);
+ m_rootScene.EventManager.TriggerOarFileLoaded(m_requestId, dearchivedScenes.GetLoadedScenes(), m_errorMessage);
+ }
+
+ ///
+ /// Searches through the files in the archive for the control file, and reads it.
+ /// We must read the control file first, in order to know which regions are available.
+ ///
+ ///
+ /// In most cases the control file *is* first, since that's how we create archives. However,
+ /// it's possible that someone rewrote the archive externally so we can't rely on this fact.
+ ///
+ ///
+ ///
+ private void FindAndLoadControlFile(out TarArchiveReader archive, out DearchiveScenesInfo dearchivedScenes)
+ {
+ archive = new TarArchiveReader(m_loadStream);
+ dearchivedScenes = new DearchiveScenesInfo();
+
+ string filePath;
+ byte[] data;
+ TarArchiveReader.TarEntryType entryType;
+ bool firstFile = true;
+
+ while ((data = archive.ReadEntry(out filePath, out entryType)) != null)
+ {
+ if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType)
+ continue;
+
+ if (filePath == ArchiveConstants.CONTROL_FILE_PATH)
+ {
+ LoadControlFile(filePath, data, dearchivedScenes);
+
+ // Find which scenes are available in the simulator
+ ArchiveScenesGroup simulatorScenes = new ArchiveScenesGroup();
+ SceneManager.Instance.ForEachScene(delegate(Scene scene2)
+ {
+ simulatorScenes.AddScene(scene2);
+ });
+ simulatorScenes.CalcSceneLocations();
+ dearchivedScenes.SetSimulatorScenes(m_rootScene, simulatorScenes);
+
+ // If the control file wasn't the first file then reset the read pointer
+ if (!firstFile)
+ {
+ m_log.Warn("Control file wasn't the first file in the archive");
+ if (m_loadStream.CanSeek)
+ {
+ m_loadStream.Seek(0, SeekOrigin.Begin);
+ }
+ else if (m_loadPath != null)
+ {
+ archive.Close();
+ archive = null;
+ m_loadStream.Close();
+ m_loadStream = null;
+ m_loadStream = new GZipStream(ArchiveHelpers.GetStream(m_loadPath), CompressionMode.Decompress);
+ archive = new TarArchiveReader(m_loadStream);
+ }
+ else
+ {
+ // There isn't currently a scenario where this happens, but it's best to add a check just in case
+ throw new Exception("Error reading archive: control file wasn't the first file, and the input stream doesn't allow seeking");
+ }
+ }
+
+ return;
+ }
+
+ firstFile = false;
+ }
+
+ throw new Exception("Control file not found");
}
///
/// Load serialized scene objects.
///
- ///
- protected void LoadObjects(List serialisedSceneObjects)
+ protected void LoadObjects(Scene scene, List serialisedSceneObjects, List sceneObjects)
{
// Reload serialized prims
m_log.InfoFormat("[ARCHIVER]: Loading {0} scene objects. Please wait.", serialisedSceneObjects.Count);
- UUID oldTelehubUUID = m_scene.RegionInfo.RegionSettings.TelehubObject;
+ UUID oldTelehubUUID = scene.RegionInfo.RegionSettings.TelehubObject;
- IRegionSerialiserModule serialiser = m_scene.RequestModuleInterface();
+ IRegionSerialiserModule serialiser = scene.RequestModuleInterface();
int sceneObjectsLoadedCount = 0;
foreach (string serialisedSceneObject in serialisedSceneObjects)
@@ -269,7 +445,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
SceneObjectGroup sceneObject = serialiser.DeserializeGroupFromXml2(serialisedSceneObject);
- bool isTelehub = (sceneObject.UUID == oldTelehubUUID);
+ bool isTelehub = (sceneObject.UUID == oldTelehubUUID) && (oldTelehubUUID != UUID.Zero);
// For now, give all incoming scene objects new uuids. This will allow scenes to be cloned
// on the same region server and multiple examples a single object archive to be imported
@@ -279,8 +455,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver
if (isTelehub)
{
// Change the Telehub Object to the new UUID
- m_scene.RegionInfo.RegionSettings.TelehubObject = sceneObject.UUID;
- m_scene.RegionInfo.RegionSettings.Save();
+ scene.RegionInfo.RegionSettings.TelehubObject = sceneObject.UUID;
+ scene.RegionInfo.RegionSettings.Save();
oldTelehubUUID = UUID.Zero;
}
@@ -290,17 +466,20 @@ namespace OpenSim.Region.CoreModules.World.Archiver
{
if (part.CreatorData == null || part.CreatorData == string.Empty)
{
- if (!ResolveUserUuid(part.CreatorID))
- part.CreatorID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, part.CreatorID))
+ part.CreatorID = scene.RegionInfo.EstateSettings.EstateOwner;
}
if (UserManager != null)
UserManager.AddUser(part.CreatorID, part.CreatorData);
- if (!ResolveUserUuid(part.OwnerID))
- part.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, part.OwnerID))
+ part.OwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
- if (!ResolveUserUuid(part.LastOwnerID))
- part.LastOwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, part.LastOwnerID))
+ part.LastOwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
+
+ if (!ResolveGroupUuid(part.GroupID))
+ part.GroupID = UUID.Zero;
// And zap any troublesome sit target information
// part.SitTargetOrientation = new Quaternion(0, 0, 0, 1);
@@ -311,14 +490,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
// being no copy/no mod for everyone
lock (part.TaskInventory)
{
- if (!ResolveUserUuid(part.CreatorID))
- part.CreatorID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, part.CreatorID))
+ part.CreatorID = scene.RegionInfo.EstateSettings.EstateOwner;
- if (!ResolveUserUuid(part.OwnerID))
- part.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, part.OwnerID))
+ part.OwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
- if (!ResolveUserUuid(part.LastOwnerID))
- part.LastOwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, part.LastOwnerID))
+ part.LastOwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
// And zap any troublesome sit target information
part.SitTargetOrientation = new Quaternion(0, 0, 0, 1);
@@ -331,26 +510,31 @@ namespace OpenSim.Region.CoreModules.World.Archiver
TaskInventoryDictionary inv = part.TaskInventory;
foreach (KeyValuePair kvp in inv)
{
- if (!ResolveUserUuid(kvp.Value.OwnerID))
+ if (!ResolveUserUuid(scene, kvp.Value.OwnerID))
{
- kvp.Value.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ kvp.Value.OwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
}
+
if (kvp.Value.CreatorData == null || kvp.Value.CreatorData == string.Empty)
{
- if (!ResolveUserUuid(kvp.Value.CreatorID))
- kvp.Value.CreatorID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, kvp.Value.CreatorID))
+ kvp.Value.CreatorID = scene.RegionInfo.EstateSettings.EstateOwner;
}
+
if (UserManager != null)
UserManager.AddUser(kvp.Value.CreatorID, kvp.Value.CreatorData);
+
+ if (!ResolveGroupUuid(kvp.Value.GroupID))
+ kvp.Value.GroupID = UUID.Zero;
}
part.TaskInventory.LockItemsForRead(false);
}
}
- if (m_scene.AddRestoredSceneObject(sceneObject, true, false))
+ if (scene.AddRestoredSceneObject(sceneObject, true, false))
{
sceneObjectsLoadedCount++;
- sceneObject.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, 0);
+ sceneObject.CreateScriptInstances(0, false, scene.DefaultScriptEngine, 0);
sceneObject.ResumeScripts();
}
}
@@ -365,16 +549,17 @@ namespace OpenSim.Region.CoreModules.World.Archiver
if (oldTelehubUUID != UUID.Zero)
{
m_log.WarnFormat("Telehub object not found: {0}", oldTelehubUUID);
- m_scene.RegionInfo.RegionSettings.TelehubObject = UUID.Zero;
- m_scene.RegionInfo.RegionSettings.ClearSpawnPoints();
+ scene.RegionInfo.RegionSettings.TelehubObject = UUID.Zero;
+ scene.RegionInfo.RegionSettings.ClearSpawnPoints();
}
}
///
/// Load serialized parcels.
///
+ ///
///
- protected void LoadParcels(List serialisedParcels)
+ protected void LoadParcels(Scene scene, List serialisedParcels)
{
// Reload serialized parcels
m_log.InfoFormat("[ARCHIVER]: Loading {0} parcels. Please wait.", serialisedParcels.Count);
@@ -382,9 +567,27 @@ namespace OpenSim.Region.CoreModules.World.Archiver
foreach (string serialisedParcel in serialisedParcels)
{
LandData parcel = LandDataSerializer.Deserialize(serialisedParcel);
- if (!ResolveUserUuid(parcel.OwnerID))
- parcel.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ // Validate User and Group UUID's
+
+ if (!ResolveUserUuid(scene, parcel.OwnerID))
+ parcel.OwnerID = m_rootScene.RegionInfo.EstateSettings.EstateOwner;
+
+ if (!ResolveGroupUuid(parcel.GroupID))
+ {
+ parcel.GroupID = UUID.Zero;
+ parcel.IsGroupOwned = false;
+ }
+
+ List accessList = new List();
+ foreach (LandAccessEntry entry in parcel.ParcelAccessList)
+ {
+ if (ResolveUserUuid(scene, entry.AgentID))
+ accessList.Add(entry);
+ // else, drop this access rule
+ }
+ parcel.ParcelAccessList = accessList;
+
// m_log.DebugFormat(
// "[ARCHIVER]: Adding parcel {0}, local id {1}, area {2}",
// parcel.Name, parcel.LocalID, parcel.Area);
@@ -395,23 +598,24 @@ namespace OpenSim.Region.CoreModules.World.Archiver
if (!m_merge)
{
bool setupDefaultParcel = (landData.Count == 0);
- m_scene.LandChannel.Clear(setupDefaultParcel);
+ scene.LandChannel.Clear(setupDefaultParcel);
}
- m_scene.EventManager.TriggerIncomingLandDataFromStorage(landData);
+ scene.EventManager.TriggerIncomingLandDataFromStorage(landData);
m_log.InfoFormat("[ARCHIVER]: Restored {0} parcels.", landData.Count);
}
///
/// Look up the given user id to check whether it's one that is valid for this grid.
///
+ ///
///
///
- private bool ResolveUserUuid(UUID uuid)
+ private bool ResolveUserUuid(Scene scene, UUID uuid)
{
if (!m_validUserUuids.ContainsKey(uuid))
{
- UserAccount account = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid);
+ UserAccount account = scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, uuid);
m_validUserUuids.Add(uuid, account != null);
}
@@ -419,6 +623,30 @@ namespace OpenSim.Region.CoreModules.World.Archiver
}
///
+ /// Look up the given group id to check whether it's one that is valid for this grid.
+ ///
+ ///
+ ///
+ private bool ResolveGroupUuid(UUID uuid)
+ {
+ if (uuid == UUID.Zero)
+ return true; // this means the object has no group
+
+ if (!m_validGroupUuids.ContainsKey(uuid))
+ {
+ bool exists;
+
+ if (m_groupsModule == null)
+ exists = false;
+ else
+ exists = (m_groupsModule.GetGroupRecord(uuid) != null);
+
+ m_validGroupUuids.Add(uuid, exists);
+ }
+
+ return m_validGroupUuids[uuid];
+ }
+
/// Load an asset
///
///
@@ -442,7 +670,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
string extension = filename.Substring(i);
string uuid = filename.Remove(filename.Length - extension.Length);
- if (m_scene.AssetService.GetMetadata(uuid) != null)
+ if (m_assetService.GetMetadata(uuid) != null)
{
// m_log.DebugFormat("[ARCHIVER]: found existing asset {0}",uuid);
return true;
@@ -462,7 +690,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
// We're relying on the asset service to do the sensible thing and not store the asset if it already
// exists.
- m_scene.AssetService.Store(asset);
+ m_assetService.Store(asset);
/**
* Create layers on decode for image assets. This is likely to significantly increase the time to load archives so
@@ -490,12 +718,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
///
/// Load region settings data
///
+ ///
///
///
+ ///
///
/// true if settings were loaded successfully, false otherwise
///
- private bool LoadRegionSettings(string settingsPath, byte[] data)
+ private bool LoadRegionSettings(Scene scene, string settingsPath, byte[] data, DearchiveScenesInfo dearchivedScenes)
{
RegionSettings loadedRegionSettings;
@@ -511,7 +741,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
return false;
}
- RegionSettings currentRegionSettings = m_scene.RegionInfo.RegionSettings;
+ RegionSettings currentRegionSettings = scene.RegionInfo.RegionSettings;
currentRegionSettings.AgentLimit = loadedRegionSettings.AgentLimit;
currentRegionSettings.AllowDamage = loadedRegionSettings.AllowDamage;
@@ -548,12 +778,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
foreach (SpawnPoint sp in loadedRegionSettings.SpawnPoints())
currentRegionSettings.AddSpawnPoint(sp);
+ currentRegionSettings.LoadedCreationDateTime = dearchivedScenes.LoadedCreationDateTime;
+ currentRegionSettings.LoadedCreationID = dearchivedScenes.GetOriginalRegionID(scene.RegionInfo.RegionID).ToString();
+
currentRegionSettings.Save();
- m_scene.TriggerEstateSunUpdate();
+ scene.TriggerEstateSunUpdate();
- IEstateModule estateModule = m_scene.RequestModuleInterface();
-
+ IEstateModule estateModule = scene.RequestModuleInterface();
if (estateModule != null)
estateModule.sendRegionHandshakeToAll();
@@ -563,14 +795,15 @@ namespace OpenSim.Region.CoreModules.World.Archiver
///
/// Load terrain data
///
+ ///
///
///
///
/// true if terrain was resolved successfully, false otherwise.
///
- private bool LoadTerrain(string terrainPath, byte[] data)
+ private bool LoadTerrain(Scene scene, string terrainPath, byte[] data)
{
- ITerrainModule terrainModule = m_scene.RequestModuleInterface();
+ ITerrainModule terrainModule = scene.RequestModuleInterface();
MemoryStream ms = new MemoryStream(data);
terrainModule.LoadFromStream(terrainPath, ms);
@@ -586,17 +819,18 @@ namespace OpenSim.Region.CoreModules.World.Archiver
///
///
///
- public void LoadControlFile(string path, byte[] data)
+ ///
+ public DearchiveScenesInfo LoadControlFile(string path, byte[] data, DearchiveScenesInfo dearchivedScenes)
{
XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);
XmlTextReader xtr = new XmlTextReader(Encoding.ASCII.GetString(data), XmlNodeType.Document, context);
- RegionSettings currentRegionSettings = m_scene.RegionInfo.RegionSettings;
+ // Loaded metadata will be empty if no information exists in the archive
+ dearchivedScenes.LoadedCreationDateTime = 0;
+ dearchivedScenes.DefaultOriginalID = "";
- // Loaded metadata will empty if no information exists in the archive
- currentRegionSettings.LoadedCreationDateTime = 0;
- currentRegionSettings.LoadedCreationID = "";
+ bool multiRegion = false;
while (xtr.Read())
{
@@ -622,18 +856,44 @@ namespace OpenSim.Region.CoreModules.World.Archiver
{
int value;
if (Int32.TryParse(xtr.ReadElementContentAsString(), out value))
- currentRegionSettings.LoadedCreationDateTime = value;
+ dearchivedScenes.LoadedCreationDateTime = value;
}
- else if (xtr.Name.ToString() == "id")
+ else if (xtr.Name.ToString() == "row")
{
- currentRegionSettings.LoadedCreationID = xtr.ReadElementContentAsString();
+ multiRegion = true;
+ dearchivedScenes.StartRow();
+ }
+ else if (xtr.Name.ToString() == "region")
+ {
+ dearchivedScenes.StartRegion();
+ }
+ else if (xtr.Name.ToString() == "id")
+ {
+ string id = xtr.ReadElementContentAsString();
+ dearchivedScenes.DefaultOriginalID = id;
+ if (multiRegion)
+ dearchivedScenes.SetRegionOriginalID(id);
+ }
+ else if (xtr.Name.ToString() == "dir")
+ {
+ dearchivedScenes.SetRegionDirectory(xtr.ReadElementContentAsString());
}
}
}
-
- currentRegionSettings.Save();
-
+
+ dearchivedScenes.MultiRegionFormat = multiRegion;
+ if (!multiRegion)
+ {
+ // Add the single scene
+ dearchivedScenes.StartRow();
+ dearchivedScenes.StartRegion();
+ dearchivedScenes.SetRegionOriginalID(dearchivedScenes.DefaultOriginalID);
+ dearchivedScenes.SetRegionDirectory("");
+ }
+
ControlFileLoaded = true;
+
+ return dearchivedScenes;
}
}
-}
\ No newline at end of file
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs
new file mode 100644
index 0000000000..d8dace20c8
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs
@@ -0,0 +1,176 @@
+/*
+ * 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.Linq;
+using System.Text;
+using OpenSim.Region.Framework.Scenes;
+using OpenMetaverse;
+using System.Drawing;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// A group of regions arranged in a rectangle, possibly with holes.
+ ///
+ ///
+ /// The regions usually (but not necessarily) belong to an archive file, in which case we
+ /// store additional information used to create the archive (e.g., each region's
+ /// directory within the archive).
+ ///
+ public class ArchiveScenesGroup
+ {
+ ///
+ /// All the regions. The outer dictionary contains rows (key: Y coordinate).
+ /// The inner dictionaries contain each row's regions (key: X coordinate).
+ ///
+ public SortedDictionary> Regions { get; set; }
+
+ ///
+ /// The subdirectory where each region is stored in the archive.
+ ///
+ protected Dictionary m_regionDirs;
+
+ ///
+ /// The grid coordinates of the regions' bounding box.
+ ///
+ public Rectangle Rect { get; set; }
+
+
+ public ArchiveScenesGroup()
+ {
+ Regions = new SortedDictionary>();
+ m_regionDirs = new Dictionary();
+ Rect = new Rectangle(0, 0, 0, 0);
+ }
+
+ public void AddScene(Scene scene)
+ {
+ uint x = scene.RegionInfo.RegionLocX;
+ uint y = scene.RegionInfo.RegionLocY;
+
+ SortedDictionary row;
+ if (!Regions.TryGetValue(y, out row))
+ {
+ row = new SortedDictionary();
+ Regions[y] = row;
+ }
+
+ row[x] = scene;
+ }
+
+ ///
+ /// Called after all the scenes have been added. Performs calculations that require
+ /// knowledge of all the scenes.
+ ///
+ public void CalcSceneLocations()
+ {
+ if (Regions.Count == 0)
+ return;
+
+ // Find the bounding rectangle
+
+ uint firstY = Regions.First().Key;
+ uint lastY = Regions.Last().Key;
+
+ uint? firstX = null;
+ uint? lastX = null;
+
+ foreach (SortedDictionary row in Regions.Values)
+ {
+ uint curFirstX = row.First().Key;
+ uint curLastX = row.Last().Key;
+
+ firstX = (firstX == null) ? curFirstX : (firstX < curFirstX) ? firstX : curFirstX;
+ lastX = (lastX == null) ? curLastX : (lastX > curLastX) ? lastX : curLastX;
+ }
+
+ Rect = new Rectangle((int)firstX, (int)firstY, (int)(lastX - firstX + 1), (int)(lastY - firstY + 1));
+
+
+ // Calculate the subdirectory in which each region will be stored in the archive
+
+ m_regionDirs.Clear();
+ ForEachScene(delegate(Scene scene)
+ {
+ // We add the region's coordinates to ensure uniqueness even if multiple regions have the same name
+ string path = string.Format("{0}_{1}_{2}",
+ scene.RegionInfo.RegionLocX - Rect.X + 1,
+ scene.RegionInfo.RegionLocY - Rect.Y + 1,
+ scene.RegionInfo.RegionName.Replace(' ', '_'));
+ m_regionDirs[scene.RegionInfo.RegionID] = path;
+ });
+ }
+
+ ///
+ /// Returns the subdirectory where the region is stored.
+ ///
+ ///
+ ///
+ public string GetRegionDir(UUID regionID)
+ {
+ return m_regionDirs[regionID];
+ }
+
+ ///
+ /// Performs an action on all the scenes in this order: rows from South to North,
+ /// and within each row West to East.
+ ///
+ ///
+ public void ForEachScene(Action action)
+ {
+ foreach (SortedDictionary row in Regions.Values)
+ {
+ foreach (Scene scene in row.Values)
+ {
+ action(scene);
+ }
+ }
+ }
+
+ ///
+ /// Returns the scene at position 'location'.
+ ///
+ /// A location in the grid
+ /// The scene at this location
+ /// Whether the scene was found
+ public bool TryGetScene(Point location, out Scene scene)
+ {
+ SortedDictionary row;
+ if (Regions.TryGetValue((uint)location.Y, out row))
+ {
+ if (row.TryGetValue((uint)location.X, out scene))
+ return true;
+ }
+
+ scene = null;
+ return false;
+ }
+
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs
new file mode 100644
index 0000000000..d751b1c990
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs
@@ -0,0 +1,634 @@
+/*
+ * 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.IO;
+using System.IO.Compression;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Xml;
+using log4net;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Framework.Serialization;
+using OpenSim.Region.CoreModules.World.Terrain;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+using Ionic.Zlib;
+using GZipStream = Ionic.Zlib.GZipStream;
+using CompressionMode = Ionic.Zlib.CompressionMode;
+using OpenSim.Framework.Serialization.External;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// Prepare to write out an archive.
+ ///
+ public class ArchiveWriteRequest
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ ///
+ /// The minimum major version of OAR that we can write.
+ ///
+ public static int MIN_MAJOR_VERSION = 0;
+
+ ///
+ /// The maximum major version of OAR that we can write.
+ ///
+ public static int MAX_MAJOR_VERSION = 1;
+
+ ///
+ /// Whether we're saving a multi-region archive.
+ ///
+ public bool MultiRegionFormat { get; set; }
+
+ ///
+ /// Determine whether this archive will save assets. Default is true.
+ ///
+ public bool SaveAssets { get; set; }
+
+ ///
+ /// Determines which objects will be included in the archive, according to their permissions.
+ /// Default is null, meaning no permission checks.
+ ///
+ public string CheckPermissions { get; set; }
+
+ protected Scene m_rootScene;
+ protected Stream m_saveStream;
+ protected TarArchiveWriter m_archiveWriter;
+ protected Guid m_requestId;
+ protected Dictionary m_options;
+
+ ///
+ /// Constructor
+ ///
+ /// Calling module
+ /// The path to which to save data.
+ /// The id associated with this request
+ ///
+ /// If there was a problem opening a stream for the file specified by the savePath
+ ///
+ public ArchiveWriteRequest(Scene scene, string savePath, Guid requestId) : this(scene, requestId)
+ {
+ try
+ {
+ m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress, CompressionLevel.BestCompression);
+ }
+ catch (EntryPointNotFoundException e)
+ {
+ m_log.ErrorFormat(
+ "[ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream."
+ + "If you've manually installed Mono, have you appropriately updated zlib1g as well?");
+ m_log.ErrorFormat("{0} {1}", e.Message, e.StackTrace);
+ }
+ }
+
+ ///
+ /// Constructor.
+ ///
+ /// The root scene to archive
+ /// The stream to which to save data.
+ /// The id associated with this request
+ public ArchiveWriteRequest(Scene scene, Stream saveStream, Guid requestId) : this(scene, requestId)
+ {
+ m_saveStream = saveStream;
+ }
+
+ protected ArchiveWriteRequest(Scene scene, Guid requestId)
+ {
+ m_rootScene = scene;
+ m_requestId = requestId;
+ m_archiveWriter = null;
+
+ MultiRegionFormat = false;
+ SaveAssets = true;
+ CheckPermissions = null;
+ }
+
+ ///
+ /// Archive the region requested.
+ ///
+ /// if there was an io problem with creating the file
+ public void ArchiveRegion(Dictionary options)
+ {
+ m_options = options;
+
+ if (options.ContainsKey("all") && (bool)options["all"])
+ MultiRegionFormat = true;
+
+ if (options.ContainsKey("noassets") && (bool)options["noassets"])
+ SaveAssets = false;
+
+ Object temp;
+ if (options.TryGetValue("checkPermissions", out temp))
+ CheckPermissions = (string)temp;
+
+
+ // Find the regions to archive
+ ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup();
+ if (MultiRegionFormat)
+ {
+ m_log.InfoFormat("[ARCHIVER]: Saving {0} regions", SceneManager.Instance.Scenes.Count);
+ SceneManager.Instance.ForEachScene(delegate(Scene scene)
+ {
+ scenesGroup.AddScene(scene);
+ });
+ }
+ else
+ {
+ scenesGroup.AddScene(m_rootScene);
+ }
+ scenesGroup.CalcSceneLocations();
+
+
+ m_archiveWriter = new TarArchiveWriter(m_saveStream);
+
+ try
+ {
+ // Write out control file. It should be first so that it will be found ASAP when loading the file.
+ m_archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, CreateControlFile(scenesGroup));
+ m_log.InfoFormat("[ARCHIVER]: Added control file to archive.");
+
+ // Archive the regions
+
+ Dictionary assetUuids = new Dictionary();
+
+ scenesGroup.ForEachScene(delegate(Scene scene)
+ {
+ string regionDir = MultiRegionFormat ? scenesGroup.GetRegionDir(scene.RegionInfo.RegionID) : "";
+ ArchiveOneRegion(scene, regionDir, assetUuids);
+ });
+
+ // Archive the assets
+
+ if (SaveAssets)
+ {
+ m_log.DebugFormat("[ARCHIVER]: Saving {0} assets", assetUuids.Count);
+
+ // Asynchronously request all the assets required to perform this archive operation
+ AssetsRequest ar
+ = new AssetsRequest(
+ new AssetsArchiver(m_archiveWriter), assetUuids,
+ m_rootScene.AssetService, m_rootScene.UserAccountService,
+ m_rootScene.RegionInfo.ScopeID, options, ReceivedAllAssets);
+
+ Util.FireAndForget(o => ar.Execute());
+
+ // CloseArchive() will be called from ReceivedAllAssets()
+ }
+ else
+ {
+ m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified");
+ CloseArchive(string.Empty);
+ }
+ }
+ catch (Exception e)
+ {
+ CloseArchive(e.Message);
+ throw;
+ }
+ }
+
+
+ private void ArchiveOneRegion(Scene scene, string regionDir, Dictionary assetUuids)
+ {
+ m_log.InfoFormat("[ARCHIVER]: Writing region {0}", scene.RegionInfo.RegionName);
+
+ EntityBase[] entities = scene.GetEntities();
+ List sceneObjects = new List();
+
+ int numObjectsSkippedPermissions = 0;
+
+ // Filter entities so that we only have scene objects.
+ // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods
+ // end up having to do this
+ IPermissionsModule permissionsModule = scene.RequestModuleInterface();
+ foreach (EntityBase entity in entities)
+ {
+ if (entity is SceneObjectGroup)
+ {
+ SceneObjectGroup sceneObject = (SceneObjectGroup)entity;
+
+ if (!sceneObject.IsDeleted && !sceneObject.IsAttachment)
+ {
+ if (!CanUserArchiveObject(scene.RegionInfo.EstateSettings.EstateOwner, sceneObject, CheckPermissions, permissionsModule))
+ {
+ // The user isn't allowed to copy/transfer this object, so it will not be included in the OAR.
+ ++numObjectsSkippedPermissions;
+ }
+ else
+ {
+ sceneObjects.Add(sceneObject);
+ }
+ }
+ }
+ }
+
+ if (SaveAssets)
+ {
+ UuidGatherer assetGatherer = new UuidGatherer(scene.AssetService);
+ int prevAssets = assetUuids.Count;
+
+ foreach (SceneObjectGroup sceneObject in sceneObjects)
+ {
+ assetGatherer.GatherAssetUuids(sceneObject, assetUuids);
+ }
+
+ m_log.DebugFormat(
+ "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets",
+ sceneObjects.Count, assetUuids.Count - prevAssets);
+ }
+
+ if (numObjectsSkippedPermissions > 0)
+ {
+ m_log.DebugFormat(
+ "[ARCHIVER]: {0} scene objects skipped due to lack of permissions",
+ numObjectsSkippedPermissions);
+ }
+
+ // Make sure that we also request terrain texture assets
+ RegionSettings regionSettings = scene.RegionInfo.RegionSettings;
+
+ if (regionSettings.TerrainTexture1 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_1)
+ assetUuids[regionSettings.TerrainTexture1] = AssetType.Texture;
+
+ if (regionSettings.TerrainTexture2 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_2)
+ assetUuids[regionSettings.TerrainTexture2] = AssetType.Texture;
+
+ if (regionSettings.TerrainTexture3 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_3)
+ assetUuids[regionSettings.TerrainTexture3] = AssetType.Texture;
+
+ if (regionSettings.TerrainTexture4 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_4)
+ assetUuids[regionSettings.TerrainTexture4] = AssetType.Texture;
+
+ Save(scene, sceneObjects, regionDir);
+ }
+
+ ///
+ /// Checks whether the user has permission to export an object group to an OAR.
+ ///
+ /// The user
+ /// The object group
+ /// Which permissions to check: "C" = Copy, "T" = Transfer
+ /// The scene's permissions module
+ /// Whether the user is allowed to export the object to an OAR
+ private bool CanUserArchiveObject(UUID user, SceneObjectGroup objGroup, string checkPermissions, IPermissionsModule permissionsModule)
+ {
+ if (checkPermissions == null)
+ return true;
+
+ if (permissionsModule == null)
+ return true; // this shouldn't happen
+
+ // Check whether the user is permitted to export all of the parts in the SOG. If any
+ // part can't be exported then the entire SOG can't be exported.
+
+ bool permitted = true;
+ //int primNumber = 1;
+
+ foreach (SceneObjectPart obj in objGroup.Parts)
+ {
+ uint perm;
+ PermissionClass permissionClass = permissionsModule.GetPermissionClass(user, obj);
+ switch (permissionClass)
+ {
+ case PermissionClass.Owner:
+ perm = obj.BaseMask;
+ break;
+ case PermissionClass.Group:
+ perm = obj.GroupMask | obj.EveryoneMask;
+ break;
+ case PermissionClass.Everyone:
+ default:
+ perm = obj.EveryoneMask;
+ break;
+ }
+
+ bool canCopy = (perm & (uint)PermissionMask.Copy) != 0;
+ bool canTransfer = (perm & (uint)PermissionMask.Transfer) != 0;
+
+ // Special case: if Everyone can copy the object then this implies it can also be
+ // Transferred.
+ // However, if the user is the Owner then we don't check EveryoneMask, because it seems that the mask
+ // always (incorrectly) includes the Copy bit set in this case. But that's a mistake: the viewer
+ // does NOT show that the object has Everyone-Copy permissions, and doesn't allow it to be copied.
+ if (permissionClass != PermissionClass.Owner)
+ canTransfer |= (obj.EveryoneMask & (uint)PermissionMask.Copy) != 0;
+
+ bool partPermitted = true;
+ if (checkPermissions.Contains("C") && !canCopy)
+ partPermitted = false;
+ if (checkPermissions.Contains("T") && !canTransfer)
+ partPermitted = false;
+
+ // If the user is the Creator of the object then it can always be included in the OAR
+ bool creator = (obj.CreatorID.Guid == user.Guid);
+ if (creator)
+ partPermitted = true;
+
+ //string name = (objGroup.PrimCount == 1) ? objGroup.Name : string.Format("{0} ({1}/{2})", obj.Name, primNumber, objGroup.PrimCount);
+ //m_log.DebugFormat("[ARCHIVER]: Object permissions: {0}: Base={1:X4}, Owner={2:X4}, Everyone={3:X4}, permissionClass={4}, checkPermissions={5}, canCopy={6}, canTransfer={7}, creator={8}, permitted={9}",
+ // name, obj.BaseMask, obj.OwnerMask, obj.EveryoneMask,
+ // permissionClass, checkPermissions, canCopy, canTransfer, creator, partPermitted);
+
+ if (!partPermitted)
+ {
+ permitted = false;
+ break;
+ }
+
+ //++primNumber;
+ }
+
+ return permitted;
+ }
+
+ ///
+ /// Create the control file.
+ ///
+ ///
+ public string CreateControlFile(ArchiveScenesGroup scenesGroup)
+ {
+ int majorVersion;
+ int minorVersion;
+
+ if (MultiRegionFormat)
+ {
+ majorVersion = MAX_MAJOR_VERSION;
+ minorVersion = 0;
+ }
+ else
+ {
+ // To support older versions of OpenSim, we continue to create single-region OARs
+ // using the old file format. In the future this format will be discontinued.
+ majorVersion = 0;
+ minorVersion = 8;
+ }
+//
+// if (m_options.ContainsKey("version"))
+// {
+// string[] parts = m_options["version"].ToString().Split('.');
+// if (parts.Length >= 1)
+// {
+// majorVersion = Int32.Parse(parts[0]);
+//
+// if (parts.Length >= 2)
+// minorVersion = Int32.Parse(parts[1]);
+// }
+// }
+//
+// if (majorVersion < MIN_MAJOR_VERSION || majorVersion > MAX_MAJOR_VERSION)
+// {
+// throw new Exception(
+// string.Format(
+// "OAR version number for save must be between {0} and {1}",
+// MIN_MAJOR_VERSION, MAX_MAJOR_VERSION));
+// }
+// else if (majorVersion == MAX_MAJOR_VERSION)
+// {
+// // Force 1.0
+// minorVersion = 0;
+// }
+// else if (majorVersion == MIN_MAJOR_VERSION)
+// {
+// // Force 0.4
+// minorVersion = 4;
+// }
+
+ m_log.InfoFormat("[ARCHIVER]: Creating version {0}.{1} OAR", majorVersion, minorVersion);
+ if (majorVersion == 1)
+ {
+ m_log.WarnFormat("[ARCHIVER]: Please be aware that version 1.0 OARs are not compatible with OpenSim versions prior to 0.7.4. Do not use the --all option if you want to produce a compatible OAR");
+ }
+
+ String s;
+
+ using (StringWriter sw = new StringWriter())
+ {
+ using (XmlTextWriter xtw = new XmlTextWriter(sw))
+ {
+ xtw.Formatting = Formatting.Indented;
+ xtw.WriteStartDocument();
+ xtw.WriteStartElement("archive");
+ xtw.WriteAttributeString("major_version", majorVersion.ToString());
+ xtw.WriteAttributeString("minor_version", minorVersion.ToString());
+
+ xtw.WriteStartElement("creation_info");
+ DateTime now = DateTime.UtcNow;
+ TimeSpan t = now - new DateTime(1970, 1, 1);
+ xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString());
+ if (!MultiRegionFormat)
+ xtw.WriteElementString("id", m_rootScene.RegionInfo.RegionID.ToString());
+ xtw.WriteEndElement();
+
+ xtw.WriteElementString("assets_included", SaveAssets.ToString());
+
+ if (MultiRegionFormat)
+ {
+ WriteRegionsManifest(scenesGroup, xtw);
+ }
+ else
+ {
+ xtw.WriteStartElement("region_info");
+ WriteRegionInfo(m_rootScene, xtw);
+ xtw.WriteEndElement();
+ }
+
+ xtw.WriteEndElement();
+
+ xtw.Flush();
+ }
+
+ s = sw.ToString();
+ }
+
+ return s;
+ }
+
+ ///
+ /// Writes the list of regions included in a multi-region OAR.
+ ///
+ private static void WriteRegionsManifest(ArchiveScenesGroup scenesGroup, XmlTextWriter xtw)
+ {
+ xtw.WriteStartElement("regions");
+
+ // Write the regions in order: rows from South to North, then regions from West to East.
+ // The list of regions can have "holes"; we write empty elements in their position.
+
+ for (uint y = (uint)scenesGroup.Rect.Top; y < scenesGroup.Rect.Bottom; ++y)
+ {
+ SortedDictionary row;
+ if (scenesGroup.Regions.TryGetValue(y, out row))
+ {
+ xtw.WriteStartElement("row");
+
+ for (uint x = (uint)scenesGroup.Rect.Left; x < scenesGroup.Rect.Right; ++x)
+ {
+ Scene scene;
+ if (row.TryGetValue(x, out scene))
+ {
+ xtw.WriteStartElement("region");
+ xtw.WriteElementString("id", scene.RegionInfo.RegionID.ToString());
+ xtw.WriteElementString("dir", scenesGroup.GetRegionDir(scene.RegionInfo.RegionID));
+ WriteRegionInfo(scene, xtw);
+ xtw.WriteEndElement();
+ }
+ else
+ {
+ // Write a placeholder for a missing region
+ xtw.WriteElementString("region", "");
+ }
+ }
+
+ xtw.WriteEndElement();
+ }
+ else
+ {
+ // Write a placeholder for a missing row
+ xtw.WriteElementString("row", "");
+ }
+ }
+
+ xtw.WriteEndElement(); // "regions"
+ }
+
+ protected static void WriteRegionInfo(Scene scene, XmlTextWriter xtw)
+ {
+ bool isMegaregion;
+ Vector2 size;
+
+ IRegionCombinerModule rcMod = scene.RequestModuleInterface();
+
+ if (rcMod != null)
+ isMegaregion = rcMod.IsRootForMegaregion(scene.RegionInfo.RegionID);
+ else
+ isMegaregion = false;
+
+ if (isMegaregion)
+ size = rcMod.GetSizeOfMegaregion(scene.RegionInfo.RegionID);
+ else
+ size = new Vector2((float)Constants.RegionSize, (float)Constants.RegionSize);
+
+ xtw.WriteElementString("is_megaregion", isMegaregion.ToString());
+ xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y));
+ }
+
+
+ protected void Save(Scene scene, List sceneObjects, string regionDir)
+ {
+ if (regionDir != string.Empty)
+ regionDir = ArchiveConstants.REGIONS_PATH + regionDir + "/";
+
+ m_log.InfoFormat("[ARCHIVER]: Adding region settings to archive.");
+
+ // Write out region settings
+ string settingsPath = String.Format("{0}{1}{2}.xml",
+ regionDir, ArchiveConstants.SETTINGS_PATH, scene.RegionInfo.RegionName);
+ m_archiveWriter.WriteFile(settingsPath, RegionSettingsSerializer.Serialize(scene.RegionInfo.RegionSettings));
+
+ m_log.InfoFormat("[ARCHIVER]: Adding parcel settings to archive.");
+
+ // Write out land data (aka parcel) settings
+ List landObjects = scene.LandChannel.AllParcels();
+ foreach (ILandObject lo in landObjects)
+ {
+ LandData landData = lo.LandData;
+ string landDataPath = String.Format("{0}{1}{2}.xml",
+ regionDir, ArchiveConstants.LANDDATA_PATH, landData.GlobalID.ToString());
+ m_archiveWriter.WriteFile(landDataPath, LandDataSerializer.Serialize(landData, m_options));
+ }
+
+ m_log.InfoFormat("[ARCHIVER]: Adding terrain information to archive.");
+
+ // Write out terrain
+ string terrainPath = String.Format("{0}{1}{2}.r32",
+ regionDir, ArchiveConstants.TERRAINS_PATH, scene.RegionInfo.RegionName);
+
+ MemoryStream ms = new MemoryStream();
+ scene.RequestModuleInterface().SaveToStream(terrainPath, ms);
+ m_archiveWriter.WriteFile(terrainPath, ms.ToArray());
+ ms.Close();
+
+ m_log.InfoFormat("[ARCHIVER]: Adding scene objects to archive.");
+
+ // Write out scene object metadata
+ IRegionSerialiserModule serializer = scene.RequestModuleInterface();
+ foreach (SceneObjectGroup sceneObject in sceneObjects)
+ {
+ //m_log.DebugFormat("[ARCHIVER]: Saving {0} {1}, {2}", entity.Name, entity.UUID, entity.GetType());
+
+ string serializedObject = serializer.SerializeGroupToXml2(sceneObject, m_options);
+ string objectPath = string.Format("{0}{1}", regionDir, ArchiveHelpers.CreateObjectPath(sceneObject));
+ m_archiveWriter.WriteFile(objectPath, serializedObject);
+ }
+ }
+
+ protected void ReceivedAllAssets(
+ ICollection assetsFoundUuids, ICollection assetsNotFoundUuids)
+ {
+ foreach (UUID uuid in assetsNotFoundUuids)
+ {
+ m_log.DebugFormat("[ARCHIVER]: Could not find asset {0}", uuid);
+ }
+
+ // m_log.InfoFormat(
+ // "[ARCHIVER]: Received {0} of {1} assets requested",
+ // assetsFoundUuids.Count, assetsFoundUuids.Count + assetsNotFoundUuids.Count);
+
+ CloseArchive(String.Empty);
+ }
+
+
+ ///
+ /// Closes the archive and notifies that we're done.
+ ///
+ /// The error that occurred, or empty for success
+ protected void CloseArchive(string errorMessage)
+ {
+ try
+ {
+ if (m_archiveWriter != null)
+ m_archiveWriter.Close();
+ m_saveStream.Close();
+ }
+ catch (Exception e)
+ {
+ m_log.Error(string.Format("[ARCHIVER]: Error closing archive: {0} ", e.Message), e);
+ if (errorMessage == string.Empty)
+ errorMessage = e.Message;
+ }
+
+ m_log.InfoFormat("[ARCHIVER]: Finished writing out OAR for {0}", m_rootScene.RegionInfo.RegionName);
+
+ m_rootScene.EventManager.TriggerOarFileSaved(m_requestId, errorMessage);
+ }
+
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs
deleted file mode 100644
index 0780d86ed5..0000000000
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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.IO;
-using System.Reflection;
-using System.Xml;
-using log4net;
-using OpenMetaverse;
-using OpenSim.Framework;
-using OpenSim.Framework.Serialization;
-using OpenSim.Framework.Serialization.External;
-using OpenSim.Region.CoreModules.World.Terrain;
-using OpenSim.Region.Framework.Interfaces;
-using OpenSim.Region.Framework.Scenes;
-
-namespace OpenSim.Region.CoreModules.World.Archiver
-{
- ///
- /// Method called when all the necessary assets for an archive request have been received.
- ///
- public delegate void AssetsRequestCallback(
- ICollection assetsFoundUuids, ICollection assetsNotFoundUuids);
-
- ///
- /// Execute the write of an archive once we have received all the necessary data
- ///
- public class ArchiveWriteRequestExecution
- {
- private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
- protected ITerrainModule m_terrainModule;
- protected IRegionSerialiserModule m_serialiser;
- protected List m_sceneObjects;
- protected Scene m_scene;
- protected TarArchiveWriter m_archiveWriter;
- protected Guid m_requestId;
- protected Dictionary m_options;
-
- public ArchiveWriteRequestExecution(
- List sceneObjects,
- ITerrainModule terrainModule,
- IRegionSerialiserModule serialiser,
- Scene scene,
- TarArchiveWriter archiveWriter,
- Guid requestId,
- Dictionary options)
- {
- m_sceneObjects = sceneObjects;
- m_terrainModule = terrainModule;
- m_serialiser = serialiser;
- m_scene = scene;
- m_archiveWriter = archiveWriter;
- m_requestId = requestId;
- m_options = options;
- }
-
- protected internal void ReceivedAllAssets(
- ICollection assetsFoundUuids, ICollection assetsNotFoundUuids)
- {
- try
- {
- Save(assetsFoundUuids, assetsNotFoundUuids);
- }
- finally
- {
- m_archiveWriter.Close();
- }
-
- m_log.InfoFormat("[ARCHIVER]: Finished writing out OAR for {0}", m_scene.RegionInfo.RegionName);
-
- m_scene.EventManager.TriggerOarFileSaved(m_requestId, String.Empty);
- }
-
- protected internal void Save(ICollection assetsFoundUuids, ICollection assetsNotFoundUuids)
- {
- foreach (UUID uuid in assetsNotFoundUuids)
- {
- m_log.DebugFormat("[ARCHIVER]: Could not find asset {0}", uuid);
- }
-
-// m_log.InfoFormat(
-// "[ARCHIVER]: Received {0} of {1} assets requested",
-// assetsFoundUuids.Count, assetsFoundUuids.Count + assetsNotFoundUuids.Count);
-
- m_log.InfoFormat("[ARCHIVER]: Adding region settings to archive.");
-
- // Write out region settings
- string settingsPath
- = String.Format("{0}{1}.xml", ArchiveConstants.SETTINGS_PATH, m_scene.RegionInfo.RegionName);
- m_archiveWriter.WriteFile(settingsPath, RegionSettingsSerializer.Serialize(m_scene.RegionInfo.RegionSettings));
-
- m_log.InfoFormat("[ARCHIVER]: Adding parcel settings to archive.");
-
- // Write out land data (aka parcel) settings
- ListlandObjects = m_scene.LandChannel.AllParcels();
- foreach (ILandObject lo in landObjects)
- {
- LandData landData = lo.LandData;
- string landDataPath = String.Format("{0}{1}.xml", ArchiveConstants.LANDDATA_PATH,
- landData.GlobalID.ToString());
- m_archiveWriter.WriteFile(landDataPath, LandDataSerializer.Serialize(landData, m_options));
- }
-
- m_log.InfoFormat("[ARCHIVER]: Adding terrain information to archive.");
-
- // Write out terrain
- string terrainPath
- = String.Format("{0}{1}.r32", ArchiveConstants.TERRAINS_PATH, m_scene.RegionInfo.RegionName);
-
- MemoryStream ms = new MemoryStream();
- m_terrainModule.SaveToStream(terrainPath, ms);
- m_archiveWriter.WriteFile(terrainPath, ms.ToArray());
- ms.Close();
-
- m_log.InfoFormat("[ARCHIVER]: Adding scene objects to archive.");
-
- // Write out scene object metadata
- foreach (SceneObjectGroup sceneObject in m_sceneObjects)
- {
- //m_log.DebugFormat("[ARCHIVER]: Saving {0} {1}, {2}", entity.Name, entity.UUID, entity.GetType());
-
- string serializedObject = m_serialiser.SerializeGroupToXml2(sceneObject, m_options);
- m_archiveWriter.WriteFile(ArchiveHelpers.CreateObjectPath(sceneObject), serializedObject);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs
deleted file mode 100644
index 4edaaca594..0000000000
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * 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.IO;
-using System.IO.Compression;
-using System.Reflection;
-using System.Text.RegularExpressions;
-using System.Threading;
-using System.Xml;
-using log4net;
-using OpenMetaverse;
-using OpenSim.Framework;
-using OpenSim.Framework.Serialization;
-using OpenSim.Region.CoreModules.World.Terrain;
-using OpenSim.Region.Framework.Interfaces;
-using OpenSim.Region.Framework.Scenes;
-using Ionic.Zlib;
-using GZipStream = Ionic.Zlib.GZipStream;
-using CompressionMode = Ionic.Zlib.CompressionMode;
-
-namespace OpenSim.Region.CoreModules.World.Archiver
-{
- ///
- /// Prepare to write out an archive.
- ///
- public class ArchiveWriteRequestPreparation
- {
- private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
- ///
- /// The minimum major version of OAR that we can write.
- ///
- public static int MIN_MAJOR_VERSION = 0;
-
- ///
- /// The maximum major version of OAR that we can write.
- ///
- public static int MAX_MAJOR_VERSION = 0;
-
- ///
- /// Determine whether this archive will save assets. Default is true.
- ///
- public bool SaveAssets { get; set; }
-
- protected ArchiverModule m_module;
- protected Scene m_scene;
- protected Stream m_saveStream;
- protected Guid m_requestId;
-
- ///
- /// Constructor
- ///
- /// Calling module
- /// The path to which to save data.
- /// The id associated with this request
- ///
- /// If there was a problem opening a stream for the file specified by the savePath
- ///
- public ArchiveWriteRequestPreparation(ArchiverModule module, string savePath, Guid requestId) : this(module, requestId)
- {
- try
- {
- m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress, CompressionLevel.BestCompression);
- }
- catch (EntryPointNotFoundException e)
- {
- m_log.ErrorFormat(
- "[ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream."
- + "If you've manually installed Mono, have you appropriately updated zlib1g as well?");
- m_log.ErrorFormat("{0} {1}", e.Message, e.StackTrace);
- }
- }
-
- ///
- /// Constructor.
- ///
- /// Calling module
- /// The stream to which to save data.
- /// The id associated with this request
- public ArchiveWriteRequestPreparation(ArchiverModule module, Stream saveStream, Guid requestId) : this(module, requestId)
- {
- m_saveStream = saveStream;
- }
-
- protected ArchiveWriteRequestPreparation(ArchiverModule module, Guid requestId)
- {
- m_module = module;
-
- // FIXME: This is only here for regression test purposes since they do not supply a module. Need to fix
- // this.
- if (m_module != null)
- m_scene = m_module.Scene;
-
- m_requestId = requestId;
-
- SaveAssets = true;
- }
-
- ///
- /// Archive the region requested.
- ///
- /// if there was an io problem with creating the file
- public void ArchiveRegion(Dictionary options)
- {
- if (options.ContainsKey("noassets") && (bool)options["noassets"])
- SaveAssets = false;
-
- try
- {
- Dictionary assetUuids = new Dictionary();
-
- EntityBase[] entities = m_scene.GetEntities();
- List sceneObjects = new List();
-
- string checkPermissions = null;
- int numObjectsSkippedPermissions = 0;
- Object temp;
- if (options.TryGetValue("checkPermissions", out temp))
- checkPermissions = (string)temp;
-
- // Filter entities so that we only have scene objects.
- // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods
- // end up having to do this
- foreach (EntityBase entity in entities)
- {
- if (entity is SceneObjectGroup)
- {
- SceneObjectGroup sceneObject = (SceneObjectGroup)entity;
-
- if (!sceneObject.IsDeleted && !sceneObject.IsAttachment)
- {
- if (!CanUserArchiveObject(m_scene.RegionInfo.EstateSettings.EstateOwner, sceneObject, checkPermissions))
- {
- // The user isn't allowed to copy/transfer this object, so it will not be included in the OAR.
- ++numObjectsSkippedPermissions;
- }
- else
- {
- sceneObjects.Add(sceneObject);
- }
- }
- }
- }
-
- if (SaveAssets)
- {
- UuidGatherer assetGatherer = new UuidGatherer(m_scene.AssetService);
-
- foreach (SceneObjectGroup sceneObject in sceneObjects)
- {
- assetGatherer.GatherAssetUuids(sceneObject, assetUuids);
- }
-
- m_log.DebugFormat(
- "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets",
- sceneObjects.Count, assetUuids.Count);
- }
- else
- {
- m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified");
- }
-
- if (numObjectsSkippedPermissions > 0)
- {
- m_log.DebugFormat(
- "[ARCHIVER]: {0} scene objects skipped due to lack of permissions",
- numObjectsSkippedPermissions);
- }
-
- // Make sure that we also request terrain texture assets
- RegionSettings regionSettings = m_scene.RegionInfo.RegionSettings;
-
- if (regionSettings.TerrainTexture1 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_1)
- assetUuids[regionSettings.TerrainTexture1] = AssetType.Texture;
-
- if (regionSettings.TerrainTexture2 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_2)
- assetUuids[regionSettings.TerrainTexture2] = AssetType.Texture;
-
- if (regionSettings.TerrainTexture3 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_3)
- assetUuids[regionSettings.TerrainTexture3] = AssetType.Texture;
-
- if (regionSettings.TerrainTexture4 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_4)
- assetUuids[regionSettings.TerrainTexture4] = AssetType.Texture;
-
- TarArchiveWriter archiveWriter = new TarArchiveWriter(m_saveStream);
-
- // Asynchronously request all the assets required to perform this archive operation
- ArchiveWriteRequestExecution awre
- = new ArchiveWriteRequestExecution(
- sceneObjects,
- m_scene.RequestModuleInterface(),
- m_scene.RequestModuleInterface(),
- m_scene,
- archiveWriter,
- m_requestId,
- options);
-
- m_log.InfoFormat("[ARCHIVER]: Creating archive file. This may take some time.");
-
- // Write out control file. This has to be done first so that subsequent loaders will see this file first
- // XXX: I know this is a weak way of doing it since external non-OAR aware tar executables will not do this
- archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, CreateControlFile(options));
- m_log.InfoFormat("[ARCHIVER]: Added control file to archive.");
-
- if (SaveAssets)
- {
- AssetsRequest ar
- = new AssetsRequest(
- new AssetsArchiver(archiveWriter), assetUuids,
- m_scene.AssetService, m_scene.UserAccountService,
- m_scene.RegionInfo.ScopeID, options, awre.ReceivedAllAssets);
-
- Util.FireAndForget(o => ar.Execute());
- }
- else
- {
- awre.ReceivedAllAssets(new List(), new List());
- }
- }
- catch (Exception)
- {
- m_saveStream.Close();
- throw;
- }
- }
-
- ///
- /// Checks whether the user has permission to export an object group to an OAR.
- ///
- /// The user
- /// The object group
- /// Which permissions to check: "C" = Copy, "T" = Transfer
- /// Whether the user is allowed to export the object to an OAR
- private bool CanUserArchiveObject(UUID user, SceneObjectGroup objGroup, string checkPermissions)
- {
- if (checkPermissions == null)
- return true;
-
- IPermissionsModule module = m_scene.RequestModuleInterface();
- if (module == null)
- return true; // this shouldn't happen
-
- // Check whether the user is permitted to export all of the parts in the SOG. If any
- // part can't be exported then the entire SOG can't be exported.
-
- bool permitted = true;
- //int primNumber = 1;
-
- foreach (SceneObjectPart obj in objGroup.Parts)
- {
- uint perm;
- PermissionClass permissionClass = module.GetPermissionClass(user, obj);
- switch (permissionClass)
- {
- case PermissionClass.Owner:
- perm = obj.BaseMask;
- break;
- case PermissionClass.Group:
- perm = obj.GroupMask | obj.EveryoneMask;
- break;
- case PermissionClass.Everyone:
- default:
- perm = obj.EveryoneMask;
- break;
- }
-
- bool canCopy = (perm & (uint)PermissionMask.Copy) != 0;
- bool canTransfer = (perm & (uint)PermissionMask.Transfer) != 0;
-
- // Special case: if Everyone can copy the object then this implies it can also be
- // Transferred.
- // However, if the user is the Owner then we don't check EveryoneMask, because it seems that the mask
- // always (incorrectly) includes the Copy bit set in this case. But that's a mistake: the viewer
- // does NOT show that the object has Everyone-Copy permissions, and doesn't allow it to be copied.
- if (permissionClass != PermissionClass.Owner)
- canTransfer |= (obj.EveryoneMask & (uint)PermissionMask.Copy) != 0;
-
- bool partPermitted = true;
- if (checkPermissions.Contains("C") && !canCopy)
- partPermitted = false;
- if (checkPermissions.Contains("T") && !canTransfer)
- partPermitted = false;
-
- // If the user is the Creator of the object then it can always be included in the OAR
- bool creator = (obj.CreatorID.Guid == user.Guid);
- if (creator)
- partPermitted = true;
-
- //string name = (objGroup.PrimCount == 1) ? objGroup.Name : string.Format("{0} ({1}/{2})", obj.Name, primNumber, objGroup.PrimCount);
- //m_log.DebugFormat("[ARCHIVER]: Object permissions: {0}: Base={1:X4}, Owner={2:X4}, Everyone={3:X4}, permissionClass={4}, checkPermissions={5}, canCopy={6}, canTransfer={7}, creator={8}, permitted={9}",
- // name, obj.BaseMask, obj.OwnerMask, obj.EveryoneMask,
- // permissionClass, checkPermissions, canCopy, canTransfer, creator, partPermitted);
-
- if (!partPermitted)
- {
- permitted = false;
- break;
- }
-
- //++primNumber;
- }
-
- return permitted;
- }
-
- ///
- /// Create the control file for the most up to date archive
- ///
- ///
- public string CreateControlFile(Dictionary options)
- {
- int majorVersion = MAX_MAJOR_VERSION, minorVersion = 8;
-//
-// if (options.ContainsKey("version"))
-// {
-// string[] parts = options["version"].ToString().Split('.');
-// if (parts.Length >= 1)
-// {
-// majorVersion = Int32.Parse(parts[0]);
-//
-// if (parts.Length >= 2)
-// minorVersion = Int32.Parse(parts[1]);
-// }
-// }
-//
-// if (majorVersion < MIN_MAJOR_VERSION || majorVersion > MAX_MAJOR_VERSION)
-// {
-// throw new Exception(
-// string.Format(
-// "OAR version number for save must be between {0} and {1}",
-// MIN_MAJOR_VERSION, MAX_MAJOR_VERSION));
-// }
-// else if (majorVersion == MAX_MAJOR_VERSION)
-// {
-// // Force 1.0
-// minorVersion = 0;
-// }
-// else if (majorVersion == MIN_MAJOR_VERSION)
-// {
-// // Force 0.4
-// minorVersion = 4;
-// }
-
- m_log.InfoFormat("[ARCHIVER]: Creating version {0}.{1} OAR", majorVersion, minorVersion);
- //if (majorVersion == 1)
- //{
- // m_log.WarnFormat("[ARCHIVER]: Please be aware that version 1.0 OARs are not compatible with OpenSim 0.7.0.2 and earlier. Please use the --version=0 option if you want to produce a compatible OAR");
- //}
-
- String s;
-
- using (StringWriter sw = new StringWriter())
- {
- using (XmlTextWriter xtw = new XmlTextWriter(sw))
- {
- xtw.Formatting = Formatting.Indented;
- xtw.WriteStartDocument();
- xtw.WriteStartElement("archive");
- xtw.WriteAttributeString("major_version", majorVersion.ToString());
- xtw.WriteAttributeString("minor_version", minorVersion.ToString());
-
- xtw.WriteStartElement("creation_info");
- DateTime now = DateTime.UtcNow;
- TimeSpan t = now - new DateTime(1970, 1, 1);
- xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString());
- xtw.WriteElementString("id", UUID.Random().ToString());
- xtw.WriteEndElement();
-
- xtw.WriteStartElement("region_info");
-
- bool isMegaregion;
- Vector2 size;
- IRegionCombinerModule rcMod = null;
-
- // FIXME: This is only here for regression test purposes since they do not supply a module. Need to fix
- // this, possibly by doing control file creation somewhere else.
- if (m_module != null)
- rcMod = m_module.RegionCombinerModule;
-
- if (rcMod != null)
- isMegaregion = rcMod.IsRootForMegaregion(m_scene.RegionInfo.RegionID);
- else
- isMegaregion = false;
-
- if (isMegaregion)
- size = rcMod.GetSizeOfMegaregion(m_scene.RegionInfo.RegionID);
- else
- size = new Vector2((float)Constants.RegionSize, (float)Constants.RegionSize);
-
- xtw.WriteElementString("is_megaregion", isMegaregion.ToString());
- xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y));
-
- xtw.WriteEndElement();
-
- xtw.WriteElementString("assets_included", SaveAssets.ToString());
-
- xtw.WriteEndElement();
-
- xtw.Flush();
- }
-
- s = sw.ToString();
- }
-
-// if (m_scene != null)
-// Console.WriteLine(
-// "[ARCHIVE WRITE REQUEST PREPARATION]: Control file for {0} is: {1}", m_scene.RegionInfo.RegionName, s);
-
- return s;
- }
- }
-}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs
index bf3b1240a4..abf3713671 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs
@@ -32,6 +32,8 @@ using System.Reflection;
using log4net;
using NDesk.Options;
using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Framework.Console;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
@@ -117,7 +119,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
//
// foreach (string param in mainParams)
// m_log.DebugFormat("GOT PARAM [{0}]", param);
-
+
if (mainParams.Count > 2)
{
DearchiveRegion(mainParams[2], mergeOar, skipAssets, Guid.Empty);
@@ -146,17 +148,22 @@ namespace OpenSim.Region.CoreModules.World.Archiver
ops.Add("noassets", delegate(string v) { options["noassets"] = v != null; });
ops.Add("publish", v => options["wipe-owners"] = v != null);
ops.Add("perm=", delegate(string v) { options["checkPermissions"] = v; });
+ ops.Add("all", delegate(string v) { options["all"] = v != null; });
List mainParams = ops.Parse(cmdparams);
+ string path;
if (mainParams.Count > 2)
- {
- ArchiveRegion(mainParams[2], options);
- }
+ path = mainParams[2];
else
- {
- ArchiveRegion(DEFAULT_OAR_BACKUP_FILENAME, options);
- }
+ path = DEFAULT_OAR_BACKUP_FILENAME;
+
+ // Not doing this right now as this causes some problems with auto-backup systems. Maybe a force flag is
+ // needed
+// if (!ConsoleUtil.CheckFileDoesNotExist(MainConsole.Instance, path))
+// return;
+
+ ArchiveRegion(path, options);
}
public void ArchiveRegion(string savePath, Dictionary options)
@@ -169,7 +176,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
m_log.InfoFormat(
"[ARCHIVER]: Writing archive for region {0} to {1}", Scene.RegionInfo.RegionName, savePath);
- new ArchiveWriteRequestPreparation(this, savePath, requestId).ArchiveRegion(options);
+ new ArchiveWriteRequest(Scene, savePath, requestId).ArchiveRegion(options);
}
public void ArchiveRegion(Stream saveStream)
@@ -184,7 +191,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
public void ArchiveRegion(Stream saveStream, Guid requestId, Dictionary options)
{
- new ArchiveWriteRequestPreparation(this, saveStream, requestId).ArchiveRegion(options);
+ new ArchiveWriteRequest(Scene, saveStream, requestId).ArchiveRegion(options);
}
public void DearchiveRegion(string loadPath)
diff --git a/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs
index 89e9593ba3..e2f88331c9 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs
@@ -46,6 +46,12 @@ namespace OpenSim.Region.CoreModules.World.Archiver
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+ ///
+ /// Method called when all the necessary assets for an archive request have been received.
+ ///
+ public delegate void AssetsRequestCallback(
+ ICollection assetsFoundUuids, ICollection assetsNotFoundUuids);
+
enum RequestState
{
Initial,
@@ -123,6 +129,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver
m_options = options;
m_repliesRequired = uuids.Count;
+ // FIXME: This is a really poor way of handling the timeout since it will always leave the original requesting thread
+ // hanging. Need to restructure so an original request thread waits for a ManualResetEvent on asset received
+ // so we can properly abort that thread. Or request all assets synchronously, though that would be a more
+ // radical change
m_requestCallbackTimer = new System.Timers.Timer(TIMEOUT);
m_requestCallbackTimer.AutoReset = false;
m_requestCallbackTimer.Elapsed += new ElapsedEventHandler(OnRequestCallbackTimeout);
diff --git a/OpenSim/Region/CoreModules/World/Archiver/DearchiveScenesGroup.cs b/OpenSim/Region/CoreModules/World/Archiver/DearchiveScenesGroup.cs
new file mode 100644
index 0000000000..3dcc020188
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/DearchiveScenesGroup.cs
@@ -0,0 +1,232 @@
+/*
+ * 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.Linq;
+using System.Text;
+using OpenSim.Region.Framework.Scenes;
+using OpenMetaverse;
+using System.Drawing;
+using log4net;
+using System.Reflection;
+using OpenSim.Framework.Serialization;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// The regions included in an OAR file.
+ ///
+ public class DearchiveScenesInfo
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ ///
+ /// One region in the archive.
+ ///
+ public class RegionInfo
+ {
+ ///
+ /// The subdirectory in which the region is stored.
+ ///
+ public string Directory { get; set; }
+
+ ///
+ /// The region's coordinates (relative to the South-West corner of the block).
+ ///
+ public Point Location { get; set; }
+
+ ///
+ /// The UUID of the original scene from which this archived region was saved.
+ ///
+ public string OriginalID { get; set; }
+
+ ///
+ /// The scene in the current simulator into which this region is loaded.
+ /// If null then the region doesn't have a corresponding scene, and it won't be loaded.
+ ///
+ public Scene Scene { get; set; }
+ }
+
+ ///
+ /// Whether this archive uses the multi-region format.
+ ///
+ public Boolean MultiRegionFormat { get; set; }
+
+ ///
+ /// Maps (Region directory -> region)
+ ///
+ protected Dictionary m_directory2region = new Dictionary();
+
+ ///
+ /// Maps (UUID of the scene in the simulator where the region will be loaded -> region)
+ ///
+ protected Dictionary m_newId2region = new Dictionary();
+
+ public int LoadedCreationDateTime { get; set; }
+ public string DefaultOriginalID { get; set; }
+
+ // These variables are used while reading the archive control file
+ protected int? m_curY = null;
+ protected int? m_curX = null;
+ protected RegionInfo m_curRegion;
+
+
+ public DearchiveScenesInfo()
+ {
+ MultiRegionFormat = false;
+ }
+
+
+ // The following methods are used while reading the archive control file
+
+ public void StartRow()
+ {
+ m_curY = (m_curY == null) ? 0 : m_curY + 1;
+ m_curX = null;
+ }
+
+ public void StartRegion()
+ {
+ m_curX = (m_curX == null) ? 0 : m_curX + 1;
+ // Note: this doesn't mean we have a real region in this location; this could just be a "hole"
+ }
+
+ public void SetRegionOriginalID(string id)
+ {
+ m_curRegion = new RegionInfo();
+ m_curRegion.Location = new Point((int)m_curX, (int)m_curY);
+ m_curRegion.OriginalID = id;
+ // 'curRegion' will be saved in 'm_directory2region' when SetRegionDir() is called
+ }
+
+ public void SetRegionDirectory(string directory)
+ {
+ m_curRegion.Directory = directory;
+ m_directory2region[directory] = m_curRegion;
+ }
+
+
+ ///
+ /// Sets all the scenes present in the simulator.
+ ///
+ ///
+ /// This method matches regions in the archive to scenes in the simulator according to
+ /// their relative position. We only load regions if there's an existing Scene in the
+ /// grid location where the region should be loaded.
+ ///
+ /// The scene where the Load OAR operation was run
+ /// All the scenes in the simulator
+ public void SetSimulatorScenes(Scene rootScene, ArchiveScenesGroup simulatorScenes)
+ {
+ foreach (RegionInfo archivedRegion in m_directory2region.Values)
+ {
+ Point location = new Point((int)rootScene.RegionInfo.RegionLocX, (int)rootScene.RegionInfo.RegionLocY);
+ location.Offset(archivedRegion.Location);
+
+ Scene scene;
+ if (simulatorScenes.TryGetScene(location, out scene))
+ {
+ archivedRegion.Scene = scene;
+ m_newId2region[scene.RegionInfo.RegionID] = archivedRegion;
+ }
+ else
+ {
+ m_log.WarnFormat("[ARCHIVER]: Not loading archived region {0} because there's no existing region at location {1},{2}",
+ archivedRegion.Directory, location.X, location.Y);
+ }
+ }
+ }
+
+ ///
+ /// Returns the archived region according to the path of a file in the archive.
+ /// Also, converts the full path into a path that is relative to the region's directory.
+ ///
+ /// The path of a file in the archive
+ /// The corresponding Scene, or null if none
+ /// The path relative to the region's directory. (Or the original
+ /// path, if this file doesn't belong to a region.)
+ /// True: use this file; False: skip it
+ public bool GetRegionFromPath(string fullPath, out Scene scene, out string relativePath)
+ {
+ scene = null;
+ relativePath = fullPath;
+
+ if (!MultiRegionFormat)
+ {
+ if (m_newId2region.Count > 0)
+ scene = m_newId2region.First().Value.Scene;
+ return true;
+ }
+
+ if (!fullPath.StartsWith(ArchiveConstants.REGIONS_PATH))
+ return true; // this file doesn't belong to a region
+
+ string[] parts = fullPath.Split(new Char[] { '/' }, 3);
+ if (parts.Length != 3)
+ return false;
+ string regionDirectory = parts[1];
+ relativePath = parts[2];
+
+ RegionInfo region;
+ if (m_directory2region.TryGetValue(regionDirectory, out region))
+ {
+ scene = region.Scene;
+ return (scene != null);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Returns the original UUID of a region (from the simulator where the OAR was saved),
+ /// given the UUID of the scene it was loaded into in the current simulator.
+ ///
+ ///
+ ///
+ public string GetOriginalRegionID(UUID newID)
+ {
+ RegionInfo region;
+ if (m_newId2region.TryGetValue(newID, out region))
+ return region.OriginalID;
+ else
+ return DefaultOriginalID;
+ }
+
+ ///
+ /// Returns the scenes that have been (or will be) loaded.
+ ///
+ ///
+ public List GetLoadedScenes()
+ {
+ return m_newId2region.Keys.ToList();
+ }
+
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs
index 5deaf5264b..82f49b0dfa 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs
@@ -47,32 +47,41 @@ using ArchiveConstants = OpenSim.Framework.Serialization.ArchiveConstants;
using TarArchiveReader = OpenSim.Framework.Serialization.TarArchiveReader;
using TarArchiveWriter = OpenSim.Framework.Serialization.TarArchiveWriter;
using RegionSettings = OpenSim.Framework.RegionSettings;
+using OpenSim.Region.Framework.Interfaces;
namespace OpenSim.Region.CoreModules.World.Archiver.Tests
{
[TestFixture]
- public class ArchiverTests
+ public class ArchiverTests : OpenSimTestCase
{
private Guid m_lastRequestId;
private string m_lastErrorMessage;
+ protected SceneHelpers m_sceneHelpers;
protected TestScene m_scene;
protected ArchiverModule m_archiverModule;
+ protected SerialiserModule m_serialiserModule;
protected TaskInventoryItem m_soundItem;
[SetUp]
- public void SetUp()
+ public override void SetUp()
{
+ base.SetUp();
+
+ // FIXME: Do something about this - relying on statics in unit tests causes trouble sooner or later
+ new SceneManager();
+
m_archiverModule = new ArchiverModule();
- SerialiserModule serialiserModule = new SerialiserModule();
+ m_serialiserModule = new SerialiserModule();
TerrainModule terrainModule = new TerrainModule();
- m_scene = new SceneHelpers().SetupScene();
- SceneHelpers.SetupSceneModules(m_scene, m_archiverModule, serialiserModule, terrainModule);
+ m_sceneHelpers = new SceneHelpers();
+ m_scene = m_sceneHelpers.SetupScene();
+ SceneHelpers.SetupSceneModules(m_scene, m_archiverModule, m_serialiserModule, terrainModule);
}
-
- private void LoadCompleted(Guid requestId, string errorMessage)
+
+ private void LoadCompleted(Guid requestId, List loadedScenes, string errorMessage)
{
lock (this)
{
@@ -128,26 +137,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
TestHelpers.InMethod();
// log4net.Config.XmlConfigurator.Configure();
- SceneObjectPart part1 = CreateSceneObjectPart1();
- SceneObjectGroup sog1 = new SceneObjectGroup(part1);
- m_scene.AddNewSceneObject(sog1, false);
-
- SceneObjectPart part2 = CreateSceneObjectPart2();
-
- AssetNotecard nc = new AssetNotecard();
- nc.BodyText = "Hello World!";
- nc.Encode();
- UUID ncAssetUuid = new UUID("00000000-0000-0000-1000-000000000000");
- UUID ncItemUuid = new UUID("00000000-0000-0000-1100-000000000000");
- AssetBase ncAsset
- = AssetHelpers.CreateAsset(ncAssetUuid, AssetType.Notecard, nc.AssetData, UUID.Zero);
- m_scene.AssetService.Store(ncAsset);
- SceneObjectGroup sog2 = new SceneObjectGroup(part2);
- TaskInventoryItem ncItem
- = new TaskInventoryItem { Name = "ncItem", AssetID = ncAssetUuid, ItemID = ncItemUuid };
- part2.Inventory.AddInventoryItem(ncItem, true);
-
- m_scene.AddNewSceneObject(sog2, false);
+ SceneObjectGroup sog1;
+ SceneObjectGroup sog2;
+ UUID ncAssetUuid;
+ CreateTestObjects(m_scene, out sog1, out sog2, out ncAssetUuid);
MemoryStream archiveWriteStream = new MemoryStream();
m_scene.EventManager.OnOarFileSaved += SaveCompleted;
@@ -186,7 +179,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
- arr.LoadControlFile(filePath, data);
+ arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
Assert.That(arr.ControlFileLoaded, Is.True);
@@ -211,6 +204,30 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
// TODO: Test presence of more files and contents of files.
}
+ private void CreateTestObjects(Scene scene, out SceneObjectGroup sog1, out SceneObjectGroup sog2, out UUID ncAssetUuid)
+ {
+ SceneObjectPart part1 = CreateSceneObjectPart1();
+ sog1 = new SceneObjectGroup(part1);
+ scene.AddNewSceneObject(sog1, false);
+
+ AssetNotecard nc = new AssetNotecard();
+ nc.BodyText = "Hello World!";
+ nc.Encode();
+ ncAssetUuid = UUID.Random();
+ UUID ncItemUuid = UUID.Random();
+ AssetBase ncAsset
+ = AssetHelpers.CreateAsset(ncAssetUuid, AssetType.Notecard, nc.AssetData, UUID.Zero);
+ m_scene.AssetService.Store(ncAsset);
+
+ TaskInventoryItem ncItem
+ = new TaskInventoryItem { Name = "ncItem", AssetID = ncAssetUuid, ItemID = ncItemUuid };
+ SceneObjectPart part2 = CreateSceneObjectPart2();
+ sog2 = new SceneObjectGroup(part2);
+ part2.Inventory.AddInventoryItem(ncItem, true);
+
+ scene.AddNewSceneObject(sog2, false);
+ }
+
///
/// Test saving an OpenSim Region Archive with the no assets option
///
@@ -270,7 +287,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
- arr.LoadControlFile(filePath, data);
+ arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
Assert.That(arr.ControlFileLoaded, Is.True);
@@ -307,7 +324,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
tar.WriteFile(
ArchiveConstants.CONTROL_FILE_PATH,
- new ArchiveWriteRequestPreparation(null, (Stream)null, Guid.Empty).CreateControlFile(new Dictionary()));
+ new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
SceneObjectGroup sog1 = SceneHelpers.CreateSceneObject(1, ownerId, "obj1-", 0x11);
SceneObjectPart sop2
@@ -362,11 +379,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
// Also check that direct entries which will also have a file entry containing that directory doesn't
// upset load
tar.WriteDir(ArchiveConstants.TERRAINS_PATH);
-
+
tar.WriteFile(
ArchiveConstants.CONTROL_FILE_PATH,
- new ArchiveWriteRequestPreparation(null, (Stream)null, Guid.Empty).CreateControlFile(new Dictionary()));
-
+ new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
SceneObjectPart part1 = CreateSceneObjectPart1();
part1.SitTargetOrientation = new Quaternion(0.2f, 0.3f, 0.4f, 0.5f);
@@ -389,31 +405,12 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
Assert.That(soundDataResourceName, Is.Not.Null);
byte[] soundData;
- Console.WriteLine("Loading " + soundDataResourceName);
- using (Stream resource = assembly.GetManifestResourceStream(soundDataResourceName))
- {
- using (BinaryReader br = new BinaryReader(resource))
- {
- // FIXME: Use the inspector instead
- soundData = br.ReadBytes(99999999);
- UUID soundUuid = UUID.Parse("00000000-0000-0000-0000-000000000001");
- string soundAssetFileName
- = ArchiveConstants.ASSETS_PATH + soundUuid
- + ArchiveConstants.ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV];
- tar.WriteFile(soundAssetFileName, soundData);
-
- /*
- AssetBase soundAsset = AssetHelpers.CreateAsset(soundUuid, soundData);
- scene.AssetService.Store(soundAsset);
- asset1FileName = ArchiveConstants.ASSETS_PATH + soundUuid + ".wav";
- */
-
- TaskInventoryItem item1
- = new TaskInventoryItem { AssetID = soundUuid, ItemID = soundItemUuid, Name = soundItemName };
- part1.Inventory.AddInventoryItem(item1, true);
- }
- }
-
+ UUID soundUuid;
+ CreateSoundAsset(tar, assembly, soundDataResourceName, out soundData, out soundUuid);
+
+ TaskInventoryItem item1
+ = new TaskInventoryItem { AssetID = soundUuid, ItemID = soundItemUuid, Name = soundItemName };
+ part1.Inventory.AddInventoryItem(item1, true);
m_scene.AddNewSceneObject(object1, false);
string object1FileName = string.Format(
@@ -435,6 +432,34 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
Assert.That(m_lastErrorMessage, Is.Null);
+ TestLoadedRegion(part1, soundItemName, soundData);
+ }
+
+ private static void CreateSoundAsset(TarArchiveWriter tar, Assembly assembly, string soundDataResourceName, out byte[] soundData, out UUID soundUuid)
+ {
+ using (Stream resource = assembly.GetManifestResourceStream(soundDataResourceName))
+ {
+ using (BinaryReader br = new BinaryReader(resource))
+ {
+ // FIXME: Use the inspector instead
+ soundData = br.ReadBytes(99999999);
+ soundUuid = UUID.Parse("00000000-0000-0000-0000-000000000001");
+ string soundAssetFileName
+ = ArchiveConstants.ASSETS_PATH + soundUuid
+ + ArchiveConstants.ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV];
+ tar.WriteFile(soundAssetFileName, soundData);
+
+ /*
+ AssetBase soundAsset = AssetHelpers.CreateAsset(soundUuid, soundData);
+ scene.AssetService.Store(soundAsset);
+ asset1FileName = ArchiveConstants.ASSETS_PATH + soundUuid + ".wav";
+ */
+ }
+ }
+ }
+
+ private void TestLoadedRegion(SceneObjectPart part1, string soundItemName, byte[] soundData)
+ {
SceneObjectPart object1PartLoaded = m_scene.GetSceneObjectPart(part1.Name);
Assert.That(object1PartLoaded, Is.Not.Null, "object1 was not loaded");
@@ -454,9 +479,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
Assert.That(loadedSoundAsset.Data, Is.EqualTo(soundData), "saved and loaded sound data do not match");
Assert.Greater(m_scene.LandChannel.AllParcels().Count, 0, "incorrect number of parcels");
-
- // Temporary
- Console.WriteLine("Successfully completed {0}", MethodBase.GetCurrentMethod());
}
///
@@ -516,7 +538,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
SerialiserModule serialiserModule = new SerialiserModule();
TerrainModule terrainModule = new TerrainModule();
- TestScene scene2 = new SceneHelpers().SetupScene();
+ m_sceneHelpers = new SceneHelpers();
+ TestScene scene2 = m_sceneHelpers.SetupScene();
SceneHelpers.SetupSceneModules(scene2, archiverModule, serialiserModule, terrainModule);
// Make sure there's a valid owner for the owner we saved (this should have been wiped if the code is
@@ -554,7 +577,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
tar.WriteDir(ArchiveConstants.TERRAINS_PATH);
tar.WriteFile(
ArchiveConstants.CONTROL_FILE_PATH,
- new ArchiveWriteRequestPreparation(null, (Stream)null, Guid.Empty).CreateControlFile(new Dictionary()));
+ new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
RegionSettings rs = new RegionSettings();
rs.AgentLimit = 17;
@@ -664,7 +687,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
SerialiserModule serialiserModule = new SerialiserModule();
TerrainModule terrainModule = new TerrainModule();
- Scene scene = new SceneHelpers().SetupScene();
+ Scene scene = m_sceneHelpers.SetupScene();
SceneHelpers.SetupSceneModules(scene, archiverModule, serialiserModule, terrainModule);
m_scene.AddNewSceneObject(new SceneObjectGroup(part2), false);
@@ -700,5 +723,258 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
Assert.That(object2PartMerged.GroupPosition, Is.EqualTo(part2.GroupPosition), "object2 group position not equal after merge");
}
}
+
+ ///
+ /// Test saving a multi-region OAR.
+ ///
+ [Test]
+ public void TestSaveMultiRegionOar()
+ {
+ TestHelpers.InMethod();
+
+ // Create test regions
+
+ int WIDTH = 2;
+ int HEIGHT = 2;
+
+ List scenes = new List();
+
+ // Maps (Directory in OAR file -> scene)
+ Dictionary regionPaths = new Dictionary();
+
+ // Maps (Scene -> expected object paths)
+ Dictionary> expectedPaths = new Dictionary>();
+
+ // List of expected assets
+ List expectedAssets = new List();
+
+ for (uint y = 0; y < HEIGHT; y++)
+ {
+ for (uint x = 0; x < WIDTH; x++)
+ {
+ Scene scene;
+ if (x == 0 && y == 0)
+ {
+ scene = m_scene; // this scene was already created in SetUp()
+ }
+ else
+ {
+ scene = m_sceneHelpers.SetupScene(string.Format("Unit test region {0}", (y * WIDTH) + x + 1), UUID.Random(), 1000 + x, 1000 + y);
+ SceneHelpers.SetupSceneModules(scene, new ArchiverModule(), m_serialiserModule, new TerrainModule());
+ }
+ scenes.Add(scene);
+
+ string dir = String.Format("{0}_{1}_{2}", x + 1, y + 1, scene.RegionInfo.RegionName.Replace(" ", "_"));
+ regionPaths[dir] = scene;
+
+ SceneObjectGroup sog1;
+ SceneObjectGroup sog2;
+ UUID ncAssetUuid;
+
+ CreateTestObjects(scene, out sog1, out sog2, out ncAssetUuid);
+
+ expectedPaths[scene.RegionInfo.RegionID] = new List();
+ expectedPaths[scene.RegionInfo.RegionID].Add(ArchiveHelpers.CreateObjectPath(sog1));
+ expectedPaths[scene.RegionInfo.RegionID].Add(ArchiveHelpers.CreateObjectPath(sog2));
+
+ expectedAssets.Add(ncAssetUuid);
+ }
+ }
+
+
+ // Save OAR
+
+ MemoryStream archiveWriteStream = new MemoryStream();
+ m_scene.EventManager.OnOarFileSaved += SaveCompleted;
+
+ Guid requestId = new Guid("00000000-0000-0000-0000-808080808080");
+
+ Dictionary options = new Dictionary();
+ options.Add("all", true);
+
+ lock (this)
+ {
+ m_archiverModule.ArchiveRegion(archiveWriteStream, requestId, options);
+ Monitor.Wait(this, 60000);
+ }
+
+
+ // Check that the OAR contains the expected data
+
+ Assert.That(m_lastRequestId, Is.EqualTo(requestId));
+
+ byte[] archive = archiveWriteStream.ToArray();
+ MemoryStream archiveReadStream = new MemoryStream(archive);
+ TarArchiveReader tar = new TarArchiveReader(archiveReadStream);
+
+ Dictionary> foundPaths = new Dictionary>();
+ List foundAssets = new List();
+
+ foreach (Scene scene in scenes)
+ {
+ foundPaths[scene.RegionInfo.RegionID] = new List();
+ }
+
+ string filePath;
+ TarArchiveReader.TarEntryType tarEntryType;
+
+ byte[] data = tar.ReadEntry(out filePath, out tarEntryType);
+ Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
+
+ ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
+ arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
+
+ Assert.That(arr.ControlFileLoaded, Is.True);
+
+ while (tar.ReadEntry(out filePath, out tarEntryType) != null)
+ {
+ if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH))
+ {
+ // Assets are shared, so this file doesn't belong to any specific region.
+ string fileName = filePath.Remove(0, ArchiveConstants.ASSETS_PATH.Length);
+ if (fileName.EndsWith("_notecard.txt"))
+ foundAssets.Add(UUID.Parse(fileName.Substring(0, fileName.Length - "_notecard.txt".Length)));
+ }
+ else
+ {
+ // This file belongs to one of the regions. Find out which one.
+ Assert.IsTrue(filePath.StartsWith(ArchiveConstants.REGIONS_PATH));
+ string[] parts = filePath.Split(new Char[] { '/' }, 3);
+ Assert.AreEqual(3, parts.Length);
+ string regionDirectory = parts[1];
+ string relativePath = parts[2];
+ Scene scene = regionPaths[regionDirectory];
+
+ if (relativePath.StartsWith(ArchiveConstants.OBJECTS_PATH))
+ {
+ foundPaths[scene.RegionInfo.RegionID].Add(relativePath);
+ }
+ }
+ }
+
+ Assert.AreEqual(scenes.Count, foundPaths.Count);
+ foreach (Scene scene in scenes)
+ {
+ Assert.That(foundPaths[scene.RegionInfo.RegionID], Is.EquivalentTo(expectedPaths[scene.RegionInfo.RegionID]));
+ }
+
+ Assert.That(foundAssets, Is.EquivalentTo(expectedAssets));
+ }
+
+ ///
+ /// Test loading a multi-region OAR.
+ ///
+ [Test]
+ public void TestLoadMultiRegionOar()
+ {
+ TestHelpers.InMethod();
+
+ // Create an ArchiveScenesGroup with the regions in the OAR. This is needed to generate the control file.
+
+ int WIDTH = 2;
+ int HEIGHT = 2;
+
+ for (uint y = 0; y < HEIGHT; y++)
+ {
+ for (uint x = 0; x < WIDTH; x++)
+ {
+ Scene scene;
+ if (x == 0 && y == 0)
+ {
+ scene = m_scene; // this scene was already created in SetUp()
+ }
+ else
+ {
+ scene = m_sceneHelpers.SetupScene(string.Format("Unit test region {0}", (y * WIDTH) + x + 1), UUID.Random(), 1000 + x, 1000 + y);
+ SceneHelpers.SetupSceneModules(scene, new ArchiverModule(), m_serialiserModule, new TerrainModule());
+ }
+ }
+ }
+
+ ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup();
+ SceneManager.Instance.ForEachScene(delegate(Scene scene)
+ {
+ scenesGroup.AddScene(scene);
+ });
+ scenesGroup.CalcSceneLocations();
+
+ // Generate the OAR file
+
+ MemoryStream archiveWriteStream = new MemoryStream();
+ TarArchiveWriter tar = new TarArchiveWriter(archiveWriteStream);
+
+ ArchiveWriteRequest writeRequest = new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty);
+ writeRequest.MultiRegionFormat = true;
+ tar.WriteFile(
+ ArchiveConstants.CONTROL_FILE_PATH, writeRequest.CreateControlFile(scenesGroup));
+
+ SceneObjectPart part1 = CreateSceneObjectPart1();
+ part1.SitTargetOrientation = new Quaternion(0.2f, 0.3f, 0.4f, 0.5f);
+ part1.SitTargetPosition = new Vector3(1, 2, 3);
+
+ SceneObjectGroup object1 = new SceneObjectGroup(part1);
+
+ // Let's put some inventory items into our object
+ string soundItemName = "sound-item1";
+ UUID soundItemUuid = UUID.Parse("00000000-0000-0000-0000-000000000002");
+ Type type = GetType();
+ Assembly assembly = type.Assembly;
+ string soundDataResourceName = null;
+ string[] names = assembly.GetManifestResourceNames();
+ foreach (string name in names)
+ {
+ if (name.EndsWith(".Resources.test-sound.wav"))
+ soundDataResourceName = name;
+ }
+ Assert.That(soundDataResourceName, Is.Not.Null);
+
+ byte[] soundData;
+ UUID soundUuid;
+ CreateSoundAsset(tar, assembly, soundDataResourceName, out soundData, out soundUuid);
+
+ TaskInventoryItem item1
+ = new TaskInventoryItem { AssetID = soundUuid, ItemID = soundItemUuid, Name = soundItemName };
+ part1.Inventory.AddInventoryItem(item1, true);
+ m_scene.AddNewSceneObject(object1, false);
+
+ string object1FileName = string.Format(
+ "{0}_{1:000}-{2:000}-{3:000}__{4}.xml",
+ part1.Name,
+ Math.Round(part1.GroupPosition.X), Math.Round(part1.GroupPosition.Y), Math.Round(part1.GroupPosition.Z),
+ part1.UUID);
+ string path = "regions/1_1_Unit_test_region/" + ArchiveConstants.OBJECTS_PATH + object1FileName;
+ tar.WriteFile(path, SceneObjectSerializer.ToXml2Format(object1));
+
+ tar.Close();
+
+
+ // Delete the current objects, to test that they're loaded from the OAR and didn't
+ // just remain in the scene.
+ SceneManager.Instance.ForEachScene(delegate(Scene scene)
+ {
+ scene.DeleteAllSceneObjects();
+ });
+
+ // Create a "hole", to test that that the corresponding region isn't loaded from the OAR
+ SceneManager.Instance.CloseScene(SceneManager.Instance.Scenes[1]);
+
+
+ // Check thay the OAR file contains the expected data
+
+ MemoryStream archiveReadStream = new MemoryStream(archiveWriteStream.ToArray());
+
+ lock (this)
+ {
+ m_scene.EventManager.OnOarFileLoaded += LoadCompleted;
+ m_archiverModule.DearchiveRegion(archiveReadStream);
+ }
+
+ Assert.That(m_lastErrorMessage, Is.Null);
+
+ Assert.AreEqual(3, SceneManager.Instance.Scenes.Count);
+
+ TestLoadedRegion(part1, soundItemName, soundData);
+ }
+
}
}
diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs
index fdef9d842f..5fd1bcef20 100644
--- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs
+++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs
@@ -40,6 +40,7 @@ using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
+using RegionFlags = OpenMetaverse.RegionFlags;
namespace OpenSim.Region.CoreModules.World.Estate
{
diff --git a/OpenSim/Region/CoreModules/World/Land/DwellModule.cs b/OpenSim/Region/CoreModules/World/Land/DwellModule.cs
new file mode 100644
index 0000000000..d1f05a714a
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Land/DwellModule.cs
@@ -0,0 +1,110 @@
+/*
+ * 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.Diagnostics;
+using System.Reflection;
+using System.Text;
+using log4net;
+using Nini.Config;
+using OpenMetaverse;
+using OpenMetaverse.StructuredData;
+using OpenMetaverse.Messages.Linden;
+using OpenSim.Framework;
+using OpenSim.Framework.Capabilities;
+using OpenSim.Framework.Console;
+using OpenSim.Framework.Servers;
+using OpenSim.Framework.Servers.HttpServer;
+using OpenSim.Region.CoreModules.Framework.InterfaceCommander;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.Physics.Manager;
+using OpenSim.Services.Interfaces;
+using Caps = OpenSim.Framework.Capabilities.Caps;
+using GridRegion = OpenSim.Services.Interfaces.GridRegion;
+
+namespace OpenSim.Region.CoreModules.World.Land
+{
+ public class DwellModule : IDwellModule, INonSharedRegionModule
+ {
+ private Scene m_scene;
+
+ public Type ReplaceableInterface
+ {
+ get { return typeof(IDwellModule); }
+ }
+
+ public string Name
+ {
+ get { return "DwellModule"; }
+ }
+
+ public void Initialise(IConfigSource source)
+ {
+ }
+
+ public void AddRegion(Scene scene)
+ {
+ m_scene = scene;
+
+ m_scene.EventManager.OnNewClient += OnNewClient;
+ }
+
+ public void RegionLoaded(Scene scene)
+ {
+ }
+
+ public void RemoveRegion(Scene scene)
+ {
+ }
+
+ public void Close()
+ {
+ }
+
+ public void OnNewClient(IClientAPI client)
+ {
+ client.OnParcelDwellRequest += ClientOnParcelDwellRequest;
+ }
+
+ private void ClientOnParcelDwellRequest(int localID, IClientAPI client)
+ {
+ ILandObject parcel = m_scene.LandChannel.GetLandObject(localID);
+ if (parcel == null)
+ return;
+
+ client.SendParcelDwellReply(localID, parcel.LandData.GlobalID, parcel.LandData.Dwell);
+ }
+
+ public int GetDwell(UUID parcelID)
+ {
+ return 0;
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs
index aae6603d57..b5e2bc3085 100644
--- a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs
+++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs
@@ -927,6 +927,7 @@ namespace OpenSim.Region.CoreModules.World.Land
ILandObject newLand = startLandObject.Copy();
newLand.LandData.Name = newLand.LandData.Name;
newLand.LandData.GlobalID = UUID.Random();
+ newLand.LandData.Dwell = 0;
newLand.SetLandBitmap(newLand.GetSquareLandBitmap(start_x, start_y, end_x, end_y));
diff --git a/OpenSim/Region/CoreModules/World/Land/LandObject.cs b/OpenSim/Region/CoreModules/World/Land/LandObject.cs
index 4f067374d2..d5b2adb00d 100644
--- a/OpenSim/Region/CoreModules/World/Land/LandObject.cs
+++ b/OpenSim/Region/CoreModules/World/Land/LandObject.cs
@@ -33,6 +33,7 @@ using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
+using RegionFlags = OpenMetaverse.RegionFlags;
namespace OpenSim.Region.CoreModules.World.Land
{
diff --git a/OpenSim/Region/CoreModules/World/Land/PrimCountModule.cs b/OpenSim/Region/CoreModules/World/Land/PrimCountModule.cs
index 102b4d7550..cbb3abe460 100644
--- a/OpenSim/Region/CoreModules/World/Land/PrimCountModule.cs
+++ b/OpenSim/Region/CoreModules/World/Land/PrimCountModule.cs
@@ -69,7 +69,7 @@ namespace OpenSim.Region.CoreModules.World.Land
/// without recounting the whole sim.
///
/// We start out tainted so that the first get call resets the various prim counts.
- ///
+ ///
private bool m_Tainted = true;
private Object m_TaintLock = new Object();
diff --git a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs
index 09f6758ba4..ab8f14344b 100644
--- a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs
+++ b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs
@@ -27,9 +27,12 @@
using System;
using System.Collections.Generic;
+using System.IO;
+using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
+using System.Xml;
using log4net;
using Mono.Addins;
using NDesk.Options;
@@ -40,6 +43,7 @@ using OpenSim.Framework.Console;
using OpenSim.Framework.Monitoring;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.Framework.Scenes.Serialization;
namespace OpenSim.Region.CoreModules.World.Objects.Commands
{
@@ -83,52 +87,85 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
m_console.Commands.AddCommand(
"Objects", false, "delete object owner",
"delete object owner ",
- "Delete a scene object by owner", HandleDeleteObject);
+ "Delete scene objects by owner",
+ "Command will ask for confirmation before proceeding.",
+ HandleDeleteObject);
m_console.Commands.AddCommand(
"Objects", false, "delete object creator",
"delete object creator ",
- "Delete a scene object by creator", HandleDeleteObject);
+ "Delete scene objects by creator",
+ "Command will ask for confirmation before proceeding.",
+ HandleDeleteObject);
m_console.Commands.AddCommand(
- "Objects", false, "delete object uuid",
- "delete object uuid ",
- "Delete a scene object by uuid", HandleDeleteObject);
+ "Objects", false, "delete object id",
+ "delete object id ",
+ "Delete a scene object by uuid or localID",
+ HandleDeleteObject);
m_console.Commands.AddCommand(
"Objects", false, "delete object name",
"delete object name [--regex] ",
"Delete a scene object by name.",
- "If --regex is specified then the name is treatead as a regular expression",
+ "Command will ask for confirmation before proceeding.\n"
+ + "If --regex is specified then the name is treatead as a regular expression",
HandleDeleteObject);
m_console.Commands.AddCommand(
"Objects", false, "delete object outside",
"delete object outside",
- "Delete all scene objects outside region boundaries", HandleDeleteObject);
+ "Delete all scene objects outside region boundaries",
+ "Command will ask for confirmation before proceeding.",
+ HandleDeleteObject);
m_console.Commands.AddCommand(
"Objects",
false,
- "show object uuid",
- "show object uuid ",
- "Show details of a scene object with the given UUID", HandleShowObjectByUuid);
+ "delete object pos",
+ "delete object pos to ",
+ "Delete scene objects within the given area.",
+ ConsoleUtil.CoordHelp,
+ HandleDeleteObject);
+
+ m_console.Commands.AddCommand(
+ "Objects",
+ false,
+ "show object id",
+ "show object id [--full] ",
+ "Show details of a scene object with the given UUID or localID",
+ "The --full option will print out information on all the parts of the object.\n"
+ + "For yet more detailed part information, use the \"show part\" commands.",
+ HandleShowObjectById);
m_console.Commands.AddCommand(
"Objects",
false,
"show object name",
- "show object name [--regex] ",
+ "show object name [--full] [--regex] ",
"Show details of scene objects with the given name.",
- "If --regex is specified then the name is treatead as a regular expression",
+ "The --full option will print out information on all the parts of the object.\n"
+ + "For yet more detailed part information, use the \"show part\" commands.\n"
+ + "If --regex is specified then the name is treatead as a regular expression.",
HandleShowObjectByName);
m_console.Commands.AddCommand(
"Objects",
false,
- "show part uuid",
- "show part uuid ",
- "Show details of a scene object parts with the given UUID", HandleShowPartByUuid);
+ "show object pos",
+ "show object pos [--full] to ",
+ "Show details of scene objects within the given area.",
+ "The --full option will print out information on all the parts of the object.\n"
+ + "For yet more detailed part information, use the \"show part\" commands.\n"
+ + ConsoleUtil.CoordHelp,
+ HandleShowObjectByPos);
+
+ m_console.Commands.AddCommand(
+ "Objects",
+ false,
+ "show part id",
+ "show part id ",
+ "Show details of a scene object part with the given UUID or localID", HandleShowPartById);
m_console.Commands.AddCommand(
"Objects",
@@ -136,8 +173,28 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
"show part name",
"show part name [--regex] ",
"Show details of scene object parts with the given name.",
- "If --regex is specified then the name is treatead as a regular expression",
+ "If --regex is specified then the name is treated as a regular expression",
HandleShowPartByName);
+
+ m_console.Commands.AddCommand(
+ "Objects",
+ false,
+ "show part pos",
+ "show part pos to ",
+ "Show details of scene object parts within the given area.",
+ ConsoleUtil.CoordHelp,
+ HandleShowPartByPos);
+
+ m_console.Commands.AddCommand(
+ "Objects",
+ false,
+ "dump object id",
+ "dump object id ",
+ "Dump the formatted serialization of the given object to the file .xml",
+ "e.g. dump object uuid c1ed6809-cc24-4061-a4c2-93082a2d1f1d will dump serialization to c1ed6809-cc24-4061-a4c2-93082a2d1f1d.xml\n"
+ + "To locate the UUID or localID in the first place, you need to use the other show object commands.\n"
+ + "If a local ID is given then the filename used is still that for the UUID",
+ HandleDumpObjectById);
}
public void RemoveRegion(Scene scene)
@@ -150,25 +207,75 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
// m_log.DebugFormat("[OBJECTS COMMANDS MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName);
}
- private void HandleShowObjectByUuid(string module, string[] cmd)
+ ///
+ /// Outputs the sogs to console.
+ ///
+ ///
+ /// If true then output all part details. If false then output summary.
+ private void OutputSogsToConsole(Predicate searchPredicate, bool showFull)
+ {
+ List sceneObjects = m_scene.GetSceneObjectGroups().FindAll(searchPredicate);
+
+ StringBuilder sb = new StringBuilder();
+
+ foreach (SceneObjectGroup so in sceneObjects)
+ {
+ AddSceneObjectReport(sb, so, showFull);
+ sb.Append("\n");
+ }
+
+ sb.AppendFormat("{0} object(s) found in {1}\n", sceneObjects.Count, m_scene.Name);
+
+ m_console.OutputFormat(sb.ToString());
+ }
+
+ private void OutputSopsToConsole(Predicate searchPredicate, bool showFull)
+ {
+ List sceneObjects = m_scene.GetSceneObjectGroups();
+ List parts = new List();
+
+ sceneObjects.ForEach(so => parts.AddRange(Array.FindAll(so.Parts, searchPredicate)));
+
+ StringBuilder sb = new StringBuilder();
+
+ foreach (SceneObjectPart part in parts)
+ {
+ AddScenePartReport(sb, part, showFull);
+ sb.Append("\n");
+ }
+
+ sb.AppendFormat("{0} parts found in {1}\n", parts.Count, m_scene.Name);
+
+ m_console.OutputFormat(sb.ToString());
+ }
+
+ private void HandleShowObjectById(string module, string[] cmdparams)
{
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
return;
- if (cmd.Length < 4)
+ bool showFull = false;
+ OptionSet options = new OptionSet().Add("full", v => showFull = v != null );
+
+ List mainParams = options.Parse(cmdparams);
+
+ if (mainParams.Count < 4)
{
m_console.OutputFormat("Usage: show object uuid ");
return;
}
- UUID objectUuid;
- if (!UUID.TryParse(cmd[3], out objectUuid))
- {
- m_console.OutputFormat("{0} is not a valid uuid", cmd[3]);
+ UUID uuid;
+ uint localId;
+ if (!ConsoleUtil.TryParseConsoleId(m_console, mainParams[3], out uuid, out localId))
return;
- }
- SceneObjectGroup so = m_scene.GetSceneObjectGroup(objectUuid);
+ SceneObjectGroup so;
+
+ if (localId != ConsoleUtil.LocalIdNotFound)
+ so = m_scene.GetSceneObjectGroup(localId);
+ else
+ so = m_scene.GetSceneObjectGroup(uuid);
if (so == null)
{
@@ -177,7 +284,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
}
StringBuilder sb = new StringBuilder();
- AddSceneObjectReport(sb, so);
+ AddSceneObjectReport(sb, so, showFull);
m_console.OutputFormat(sb.ToString());
}
@@ -187,70 +294,91 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
return;
+ bool showFull = false;
bool useRegex = false;
- OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null );
+ OptionSet options = new OptionSet();
+ options.Add("full", v => showFull = v != null );
+ options.Add("regex", v => useRegex = v != null );
List mainParams = options.Parse(cmdparams);
if (mainParams.Count < 4)
{
- m_console.OutputFormat("Usage: show object name [--regex] ");
+ m_console.OutputFormat("Usage: show object name [--full] [--regex] ");
return;
}
string name = mainParams[3];
- List sceneObjects = new List();
- Action searchAction;
+ Predicate searchPredicate;
if (useRegex)
{
Regex nameRegex = new Regex(name);
- searchAction = so => { if (nameRegex.IsMatch(so.Name)) { sceneObjects.Add(so); }};
+ searchPredicate = so => nameRegex.IsMatch(so.Name);
}
else
{
- searchAction = so => { if (so.Name == name) { sceneObjects.Add(so); }};
+ searchPredicate = so => so.Name == name;
}
- m_scene.ForEachSOG(searchAction);
-
- if (sceneObjects.Count == 0)
- {
- m_console.OutputFormat("No objects with name {0} found in {1}", name, m_scene.RegionInfo.RegionName);
- return;
- }
-
- StringBuilder sb = new StringBuilder();
-
- foreach (SceneObjectGroup so in sceneObjects)
- {
- AddSceneObjectReport(sb, so);
- sb.Append("\n");
- }
-
- m_console.OutputFormat(sb.ToString());
+ OutputSogsToConsole(searchPredicate, showFull);
}
- private void HandleShowPartByUuid(string module, string[] cmd)
+ private void HandleShowObjectByPos(string module, string[] cmdparams)
{
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
return;
- if (cmd.Length < 4)
+ bool showFull = false;
+ OptionSet options = new OptionSet().Add("full", v => showFull = v != null );
+
+ List mainParams = options.Parse(cmdparams);
+
+ if (mainParams.Count < 5)
{
- m_console.OutputFormat("Usage: show part uuid ");
+ m_console.OutputFormat("Usage: show object pos [--full] to ");
+ return;
+ }
+
+ Vector3 startVector, endVector;
+
+ if (!TryParseVectorRange(cmdparams.Skip(3).Take(3), out startVector, out endVector))
+ return;
+
+ Predicate searchPredicate
+ = so => Util.IsInsideBox(so.AbsolutePosition, startVector, endVector);
+
+ OutputSogsToConsole(searchPredicate, showFull);
+ }
+
+ private void HandleShowPartById(string module, string[] cmdparams)
+ {
+ if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
+ return;
+
+// bool showFull = false;
+ OptionSet options = new OptionSet();
+// options.Add("full", v => showFull = v != null );
+
+ List mainParams = options.Parse(cmdparams);
+
+ if (mainParams.Count < 4)
+ {
+ m_console.OutputFormat("Usage: show part id [--full] ");
return;
}
UUID objectUuid;
- if (!UUID.TryParse(cmd[3], out objectUuid))
- {
- m_console.OutputFormat("{0} is not a valid uuid", cmd[3]);
+ uint localId;
+ if (!ConsoleUtil.TryParseConsoleId(m_console, mainParams[3], out objectUuid, out localId))
return;
- }
- SceneObjectPart sop = m_scene.GetSceneObjectPart(objectUuid);
+ SceneObjectPart sop;
+ if (localId == ConsoleUtil.LocalIdNotFound)
+ sop = m_scene.GetSceneObjectPart(objectUuid);
+ else
+ sop = m_scene.GetSceneObjectPart(localId);
if (sop == null)
{
@@ -259,84 +387,239 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
}
StringBuilder sb = new StringBuilder();
- AddScenePartReport(sb, sop);
+ AddScenePartReport(sb, sop, true);
m_console.OutputFormat(sb.ToString());
}
+ private void HandleShowPartByPos(string module, string[] cmdparams)
+ {
+ if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
+ return;
+
+// bool showFull = false;
+ OptionSet options = new OptionSet();
+// options.Add("full", v => showFull = v != null );
+
+ List mainParams = options.Parse(cmdparams);
+
+ if (mainParams.Count < 5)
+ {
+ m_console.OutputFormat("Usage: show part pos [--full] to ");
+ return;
+ }
+
+ string rawConsoleStartVector = mainParams[3];
+ Vector3 startVector;
+
+ if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector))
+ {
+ m_console.OutputFormat("Error: Start vector {0} does not have a valid format", rawConsoleStartVector);
+ return;
+ }
+
+ string rawConsoleEndVector = mainParams[5];
+ Vector3 endVector;
+
+ if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector))
+ {
+ m_console.OutputFormat("Error: End vector {0} does not have a valid format", rawConsoleEndVector);
+ return;
+ }
+
+ OutputSopsToConsole(sop => Util.IsInsideBox(sop.AbsolutePosition, startVector, endVector), true);
+ }
+
private void HandleShowPartByName(string module, string[] cmdparams)
{
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
return;
+// bool showFull = false;
bool useRegex = false;
- OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null );
+ OptionSet options = new OptionSet();
+// options.Add("full", v => showFull = v != null );
+ options.Add("regex", v => useRegex = v != null );
List mainParams = options.Parse(cmdparams);
if (mainParams.Count < 4)
{
- m_console.OutputFormat("Usage: show part name [--regex] ");
+ m_console.OutputFormat("Usage: show part name [--full] [--regex] ");
return;
}
string name = mainParams[3];
- List parts = new List();
-
- Action searchAction;
+ Predicate searchPredicate;
if (useRegex)
{
Regex nameRegex = new Regex(name);
- searchAction = so => so.ForEachPart(sop => { if (nameRegex.IsMatch(sop.Name)) { parts.Add(sop); } });
+ searchPredicate = sop => nameRegex.IsMatch(sop.Name);
}
else
{
- searchAction = so => so.ForEachPart(sop => { if (sop.Name == name) { parts.Add(sop); } });
+ searchPredicate = sop => sop.Name == name;
}
- m_scene.ForEachSOG(searchAction);
+ OutputSopsToConsole(searchPredicate, true);
+ }
- if (parts.Count == 0)
+ private void HandleDumpObjectById(string module, string[] cmdparams)
+ {
+ if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
+ return;
+
+ if (cmdparams.Length < 4)
{
- m_console.OutputFormat("No parts with name {0} found in {1}", name, m_scene.RegionInfo.RegionName);
+ m_console.OutputFormat("Usage: dump object id ");
return;
}
- StringBuilder sb = new StringBuilder();
+ UUID objectUuid;
+ uint localId;
+ if (!ConsoleUtil.TryParseConsoleId(m_console, cmdparams[3], out objectUuid, out localId))
+ return;
- foreach (SceneObjectPart part in parts)
+ SceneObjectGroup so;
+ if (localId == ConsoleUtil.LocalIdNotFound)
+ so = m_scene.GetSceneObjectGroup(objectUuid);
+ else
+ so = m_scene.GetSceneObjectGroup(localId);
+
+ if (so == null)
{
- AddScenePartReport(sb, part);
- sb.Append("\n");
+// m_console.OutputFormat("No part found with uuid {0}", objectUuid);
+ return;
}
- m_console.OutputFormat(sb.ToString());
+ // In case we found it via local ID.
+ objectUuid = so.UUID;
+
+ string fileName = string.Format("{0}.xml", objectUuid);
+
+ if (!ConsoleUtil.CheckFileDoesNotExist(m_console, fileName))
+ return;
+
+ using (XmlTextWriter xtw = new XmlTextWriter(fileName, Encoding.UTF8))
+ {
+ xtw.Formatting = Formatting.Indented;
+ SceneObjectSerializer.ToOriginalXmlFormat(so, xtw, true);
+ }
+
+ m_console.OutputFormat("Object dumped to file {0}", fileName);
}
- private StringBuilder AddSceneObjectReport(StringBuilder sb, SceneObjectGroup so)
+ ///
+ /// Append a scene object report to an input StringBuilder
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// If true then information on all parts of an object is appended.
+ /// If false then only summary information about an object is appended.
+ ///
+ private StringBuilder AddSceneObjectReport(StringBuilder sb, SceneObjectGroup so, bool showFull)
{
- sb.AppendFormat("Name: {0}\n", so.Name);
- sb.AppendFormat("Description: {0}\n", so.Description);
- sb.AppendFormat("Location: {0} @ {1}\n", so.AbsolutePosition, so.Scene.RegionInfo.RegionName);
- sb.AppendFormat("Parts: {0}\n", so.PrimCount);
- sb.AppendFormat("Flags: {0}\n", so.RootPart.Flags);
+ if (showFull)
+ {
+ foreach (SceneObjectPart sop in so.Parts)
+ {
+ AddScenePartReport(sb, sop, false);
+ sb.Append("\n");
+ }
+ }
+ else
+ {
+ AddSummarySceneObjectReport(sb, so);
+ }
return sb;
}
- private StringBuilder AddScenePartReport(StringBuilder sb, SceneObjectPart sop)
+ private StringBuilder AddSummarySceneObjectReport(StringBuilder sb, SceneObjectGroup so)
{
- sb.AppendFormat("Name: {0}\n", sop.Name);
- sb.AppendFormat("Description: {0}\n", sop.Description);
- sb.AppendFormat("Location: {0} @ {1}\n", sop.AbsolutePosition, sop.ParentGroup.Scene.RegionInfo.RegionName);
- sb.AppendFormat("Parent: {0}",
- sop.IsRoot ? "Is Root\n" : string.Format("{0} {1}\n", sop.ParentGroup.Name, sop.ParentGroup.UUID));
- sb.AppendFormat("Link number: {0}\n", sop.LinkNum);
- sb.AppendFormat("Flags: {0}\n", sop.Flags);
+ ConsoleDisplayList cdl = new ConsoleDisplayList();
+ cdl.AddRow("Name", so.Name);
+ cdl.AddRow("Descrition", so.Description);
+ cdl.AddRow("Local ID", so.LocalId);
+ cdl.AddRow("UUID", so.UUID);
+ cdl.AddRow("Location", string.Format("{0} @ {1}", so.AbsolutePosition, so.Scene.Name));
+ cdl.AddRow("Parts", so.PrimCount);
+ cdl.AddRow("Flags", so.RootPart.Flags);
- return sb;
+ return sb.Append(cdl.ToString());
+ }
+
+ ///
+ /// Append a scene object part report to an input StringBuilder
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// If true then information on each inventory item will be shown.
+ /// If false then only summary inventory information is shown.
+ ///
+ private StringBuilder AddScenePartReport(StringBuilder sb, SceneObjectPart sop, bool showFull)
+ {
+ ConsoleDisplayList cdl = new ConsoleDisplayList();
+ cdl.AddRow("Name", sop.Name);
+ cdl.AddRow("Description", sop.Description);
+ cdl.AddRow("Local ID", sop.LocalId);
+ cdl.AddRow("UUID", sop.UUID);
+ cdl.AddRow("Location", string.Format("{0} @ {1}", sop.AbsolutePosition, sop.ParentGroup.Scene.Name));
+ cdl.AddRow(
+ "Parent",
+ sop.IsRoot ? "Is Root" : string.Format("{0} {1}", sop.ParentGroup.Name, sop.ParentGroup.UUID));
+ cdl.AddRow("Link number", sop.LinkNum);
+ cdl.AddRow("Flags", sop.Flags);
+
+ object itemsOutput;
+ if (showFull)
+ {
+ StringBuilder itemsSb = new StringBuilder("\n");
+ itemsOutput = AddScenePartItemsReport(itemsSb, sop.Inventory).ToString();
+ }
+ else
+ {
+ itemsOutput = sop.Inventory.Count;
+ }
+
+
+ cdl.AddRow("Items", itemsOutput);
+
+ return sb.Append(cdl.ToString());
+ }
+
+ private StringBuilder AddScenePartItemsReport(StringBuilder sb, IEntityInventory inv)
+ {
+ ConsoleDisplayTable cdt = new ConsoleDisplayTable();
+ cdt.Indent = 2;
+
+ cdt.AddColumn("Name", 50);
+ cdt.AddColumn("Type", 12);
+ cdt.AddColumn("Running", 7);
+ cdt.AddColumn("Item UUID", 36);
+ cdt.AddColumn("Asset UUID", 36);
+
+ foreach (TaskInventoryItem item in inv.GetInventoryItems())
+ {
+ bool foundScriptInstance, scriptRunning;
+ foundScriptInstance
+ = SceneObjectPartInventory.TryGetScriptInstanceRunning(m_scene, item, out scriptRunning);
+
+ cdt.AddRow(
+ item.Name,
+ ((InventoryType)item.InvType).ToString(),
+ foundScriptInstance ? scriptRunning.ToString() : "n/a",
+ item.ItemID.ToString(),
+ item.AssetID.ToString());
+ }
+
+ return sb.Append(cdt.ToString());
}
private void HandleDeleteObject(string module, string[] cmd)
@@ -398,19 +681,24 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
break;
- case "uuid":
- if (!UUID.TryParse(o, out match))
+ case "id":
+ UUID uuid;
+ uint localId;
+ if (!ConsoleUtil.TryParseConsoleId(m_console, o, out uuid, out localId))
return;
requireConfirmation = false;
deletes = new List();
-
- m_scene.ForEachSOG(delegate (SceneObjectGroup g)
- {
- if (g.UUID == match && !g.IsAttachment)
- deletes.Add(g);
- });
-
+
+ SceneObjectGroup so;
+ if (localId == ConsoleUtil.LocalIdNotFound)
+ so = m_scene.GetSceneObjectGroup(uuid);
+ else
+ so = m_scene.GetSceneObjectGroup(localId);
+
+ if (!so.IsAttachment)
+ deletes.Add(so);
+
// if (deletes.Count == 0)
// m_console.OutputFormat("No objects were found with uuid {0}", match);
@@ -450,6 +738,10 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
break;
+ case "pos":
+ deletes = GetDeleteCandidatesByPos(module, cmd);
+ break;
+
default:
m_console.OutputFormat("Unrecognized mode {0}", mode);
return;
@@ -464,7 +756,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
string.Format(
"Are you sure that you want to delete {0} objects from {1}",
deletes.Count, m_scene.RegionInfo.RegionName),
- "n");
+ "y/N");
if (response.ToLower() != "y")
{
@@ -486,9 +778,6 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
private List GetDeleteCandidatesByName(string module, string[] cmdparams)
{
- if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene))
- return null;
-
bool useRegex = false;
OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null );
@@ -522,5 +811,52 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
return sceneObjects;
}
+
+ ///
+ /// Get scene object delete candidates by position
+ ///
+ ///
+ ///
+ /// null if parsing failed on one of the arguments, otherwise a list of objects to delete. If there
+ /// are no objects to delete then the list will be empty./returns>
+ private List