Compare commits
240 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cd1dc8d60 | ||
|
|
d87813a194 | ||
|
|
046113c137 | ||
|
|
03136df1a7 | ||
|
|
7c7cdf3431 | ||
|
|
4832ed6960 | ||
|
|
f0178a6a41 | ||
|
|
276fdbc1b3 | ||
|
|
5b90f5bb17 | ||
|
|
d98af79f77 | ||
|
|
b977f962fa | ||
|
|
aa52c8b20f | ||
|
|
3d36a6d55c | ||
|
|
e717398f6c | ||
|
|
a2ab3b88de | ||
|
|
b9baab5521 | ||
|
|
653a98e891 | ||
|
|
e56ef2720e | ||
|
|
df3d1d1301 | ||
|
|
a5b8359b8e | ||
|
|
ca5c0814f4 | ||
|
|
f1886c449c | ||
|
|
a1679517d3 | ||
|
|
f555febdb7 | ||
|
|
ce7694c108 | ||
|
|
c677c04f10 | ||
|
|
35b7c80e0b | ||
|
|
e81e19a3b4 | ||
|
|
210f227fe6 | ||
|
|
ce47d0c454 | ||
|
|
d8a786870b | ||
|
|
ce97ebdc88 | ||
|
|
b6b505163c | ||
|
|
1e5869dcf6 | ||
|
|
f2c78281ce | ||
|
|
33617e09a1 | ||
|
|
c1740a2903 | ||
|
|
5221f2421e | ||
|
|
54ec8b197b | ||
|
|
14b659e550 | ||
|
|
402128bb53 | ||
|
|
0b9bf236dd | ||
|
|
503ce70f74 | ||
|
|
060d6fe8f4 | ||
|
|
7cc89cdadc | ||
|
|
f8a4b61954 | ||
|
|
91a5c602e3 | ||
|
|
531edd51d8 | ||
|
|
14a4b146bf | ||
|
|
5b69872655 | ||
|
|
4e9509dafa | ||
|
|
f457952755 | ||
|
|
818379ba14 | ||
|
|
fae869c4ca | ||
|
|
d588467d5b | ||
|
|
e3d6d36c72 | ||
|
|
7f96bb601a | ||
|
|
76e9cc41bd | ||
|
|
6f89975526 | ||
|
|
74dea4cfd5 | ||
|
|
7b65985047 | ||
|
|
5781d4d52d | ||
|
|
f82b903dee | ||
|
|
4589bc84a3 | ||
|
|
735d89e369 | ||
|
|
d016051fa0 | ||
|
|
42802669dd | ||
|
|
22290ef35a | ||
|
|
a27e4ce6cb | ||
|
|
91efccabdc | ||
|
|
ee7cda261c | ||
|
|
d26fbf727a | ||
|
|
b5af17c8e3 | ||
|
|
3db10fdbef | ||
|
|
6511c3b5c6 | ||
|
|
7a50705188 | ||
|
|
0e5d3d051b | ||
|
|
f5fac2c916 | ||
|
|
581e76942f | ||
|
|
2bf42f30af | ||
|
|
b9934fc4db | ||
|
|
31c636f1e4 | ||
|
|
36e3123069 | ||
|
|
327320d1a7 | ||
|
|
21f30a1b5f | ||
|
|
90dd5844d6 | ||
|
|
3c77b8f463 | ||
|
|
80133f9ebe | ||
|
|
2f61763923 | ||
|
|
7d2cd0d935 | ||
|
|
ae58cf4224 | ||
|
|
495a3e7daf | ||
|
|
92afa6aa44 | ||
|
|
ddd9384b39 | ||
|
|
4dc27aa91f | ||
|
|
b96a53962b | ||
|
|
3c2fb77133 | ||
|
|
eb5bec96e4 | ||
|
|
2f795e4fa6 | ||
|
|
4fc0cfba3c | ||
|
|
77355295da | ||
|
|
4ec2fc4cc2 | ||
|
|
020103c51e | ||
|
|
ac02cadbdb | ||
|
|
1cfd63d0b4 | ||
|
|
1c42337381 | ||
|
|
87d810217e | ||
|
|
772aedc731 | ||
|
|
fb6d6e5cca | ||
|
|
48f4b32d7f | ||
|
|
ebd99c9e3e | ||
|
|
21a6ef5bb6 | ||
|
|
a6f7328216 | ||
|
|
b36ef60aca | ||
|
|
8d7b1f8aa0 | ||
|
|
b0da4b8d13 | ||
|
|
cfa022700d | ||
|
|
9ab5c5040d | ||
|
|
5df3944607 | ||
|
|
fb2ace6fff | ||
|
|
f931c0a868 | ||
|
|
f4cc19968c | ||
|
|
b542622b3a | ||
|
|
5f97b3e1d9 | ||
|
|
e379566e6e | ||
|
|
80f486c778 | ||
|
|
632a42e2b1 | ||
|
|
bceef401fa | ||
|
|
1b0abf8f0c | ||
|
|
e29d563557 | ||
|
|
33aa6d172f | ||
|
|
3089b6d824 | ||
|
|
a5b3989e5d | ||
|
|
387a1bb283 | ||
|
|
b481a78234 | ||
|
|
d667f9d260 | ||
|
|
696b3f66c4 | ||
|
|
49e2872f9e | ||
|
|
61f4523e01 | ||
|
|
a74167bb06 | ||
|
|
f2a9d26118 | ||
|
|
e50b6b5334 | ||
|
|
967d42d393 | ||
|
|
e50155ebca | ||
|
|
f99278c956 | ||
|
|
95809cabd9 | ||
|
|
9d973ec3b3 | ||
|
|
d29fc53052 | ||
|
|
91312daeb2 | ||
|
|
0e5f5538bc | ||
|
|
f9721573d9 | ||
|
|
c7948a669a | ||
|
|
5e626ce55d | ||
|
|
380962d359 | ||
|
|
daa4745fb7 | ||
|
|
de69a24574 | ||
|
|
b602b476ad | ||
|
|
c77be802d2 | ||
|
|
1826b2b18e | ||
|
|
f35bd6eb7d | ||
|
|
dcb94b8a24 | ||
|
|
6632eb7c05 | ||
|
|
f0a098924e | ||
|
|
7c347f4c5c | ||
|
|
d86cbe6379 | ||
|
|
c0fec70b1a | ||
|
|
2c5ff93990 | ||
|
|
1ec84ac8b1 | ||
|
|
d5c8c6bc12 | ||
|
|
5dd2569bf7 | ||
|
|
d7e6fe488d | ||
|
|
ce468215d5 | ||
|
|
6f7825e310 | ||
|
|
dd0a89a0e6 | ||
|
|
190f9c258b | ||
|
|
46d8405229 | ||
|
|
9598c1af9e | ||
|
|
82487549f7 | ||
|
|
cdc3781f42 | ||
|
|
7df7b86ec5 | ||
|
|
81fda6f947 | ||
|
|
e9322b0bf7 | ||
|
|
96f889b20e | ||
|
|
224efe7b76 | ||
|
|
ebb394bbda | ||
|
|
c17965eec4 | ||
|
|
4ead48f09f | ||
|
|
41f3f2400e | ||
|
|
f06394f195 | ||
|
|
d53a53d4c5 | ||
|
|
757d9163fa | ||
|
|
25111e703f | ||
|
|
ad1b9bbba6 | ||
|
|
97b8739c1d | ||
|
|
0760121eb9 | ||
|
|
0a22e78f59 | ||
|
|
5e51f16ceb | ||
|
|
fd8a7e64b9 | ||
|
|
ed5f574356 | ||
|
|
44349f742e | ||
|
|
e3993eefa5 | ||
|
|
8db5d79f4b | ||
|
|
bf56b30024 | ||
|
|
8321ecb7a2 | ||
|
|
783ee949ea | ||
|
|
e041f09750 | ||
|
|
fa3edcf55c | ||
|
|
874bde366a | ||
|
|
d8df2d6bed | ||
|
|
126eae7100 | ||
|
|
3c097cb7a9 | ||
|
|
189f51233e | ||
|
|
76dc29dc37 | ||
|
|
df3914c7cd | ||
|
|
740eac192b | ||
|
|
783ecd01f4 | ||
|
|
a0e3e23aa9 | ||
|
|
1acee36822 | ||
|
|
ac90322f1b | ||
|
|
2ca93cb144 | ||
|
|
4e2805496c | ||
|
|
46af3a3d92 | ||
|
|
717b11856e | ||
|
|
0199abd625 | ||
|
|
550f6c4ad2 | ||
|
|
2b506cffb1 | ||
|
|
33469a0d2b | ||
|
|
1f18ce516f | ||
|
|
2ec34580ce | ||
|
|
288baaecaf | ||
|
|
d2b00749ef | ||
|
|
8f02fd926e | ||
|
|
9f914327c6 | ||
|
|
4215877b48 | ||
|
|
d2e79e26d7 | ||
|
|
3f6c6eed33 | ||
|
|
0a71e3ab39 | ||
|
|
8fe8fdb0c9 | ||
|
|
fbfd28a61b | ||
|
|
a0d0c9f751 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -66,6 +66,7 @@ Examples/*.dll
|
||||
OpenSim.build
|
||||
OpenSim.sln
|
||||
OpenSim.suo
|
||||
OpenSim.userprefs
|
||||
Prebuild/Prebuild.build
|
||||
Prebuild/Prebuild.sln
|
||||
TestResult.xml
|
||||
|
||||
@@ -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
|
||||
@@ -1,5 +1,5 @@
|
||||
<<<>>>>The following people have contributed to OpenSim (Thank you
|
||||
for your effort!)
|
||||
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.
|
||||
@@ -209,3 +209,4 @@ In addition, we would like to thank:
|
||||
* The NANT Developers
|
||||
* Microsoft (.NET, MSSQL-Adapters)
|
||||
*x
|
||||
|
||||
|
||||
@@ -572,7 +572,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
|
||||
@@ -728,7 +728,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");
|
||||
}
|
||||
@@ -982,8 +981,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"))
|
||||
@@ -1180,9 +1179,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"]);
|
||||
@@ -1369,6 +1368,8 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
||||
/// <description>profile url</description></item>
|
||||
/// <item><term>noassets</term>
|
||||
/// <description>true if no assets should be saved</description></item>
|
||||
/// <item><term>all</term>
|
||||
/// <description>true to save all the regions in the simulator</description></item>
|
||||
/// <item><term>perm</term>
|
||||
/// <description>C and/or T</description></item>
|
||||
/// </list>
|
||||
@@ -1425,6 +1426,11 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
||||
options["checkPermissions"] = (string)requestData["perm"];
|
||||
}
|
||||
|
||||
if ((string)requestData["all"] == "true")
|
||||
{
|
||||
options["all"] = (string)requestData["all"];
|
||||
}
|
||||
|
||||
IRegionArchiverModule archiver = scene.RequestModuleInterface<IRegionArchiverModule>();
|
||||
|
||||
if (archiver != null)
|
||||
@@ -1874,29 +1880,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;
|
||||
@@ -1918,56 +1901,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"]))
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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<string, IHttpAgentHandler> h in _agents)
|
||||
{
|
||||
_httpd.RemoveAgentHandler(h.Key, h.Value);
|
||||
}
|
||||
// foreach (KeyValuePair<string, IHttpAgentHandler> h in _agents)
|
||||
// {
|
||||
// _httpd.RemoveAgentHandler(h.Key, h.Value);
|
||||
// }
|
||||
_agents = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ namespace OpenSim.Capabilities.Handlers
|
||||
|
||||
if (texture == null)
|
||||
{
|
||||
//m_log.DebugFormat("[GETTEXTURE]: texture was not in the cache");
|
||||
// m_log.DebugFormat("[GETTEXTURE]: texture was not in the cache");
|
||||
|
||||
// Fetch locally or remotely. Misses return a 404
|
||||
texture = m_assetService.Get(textureID.ToString());
|
||||
@@ -197,7 +197,7 @@ namespace OpenSim.Capabilities.Handlers
|
||||
}
|
||||
else // it was on the cache
|
||||
{
|
||||
//m_log.DebugFormat("[GETTEXTURE]: texture was in the cache");
|
||||
// m_log.DebugFormat("[GETTEXTURE]: texture was in the cache");
|
||||
WriteTextureData(httpRequest, httpResponse, texture, format);
|
||||
return true;
|
||||
}
|
||||
@@ -219,12 +219,30 @@ 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)
|
||||
{
|
||||
response.StatusCode = (int)System.Net.HttpStatusCode.RequestedRangeNotSatisfiable;
|
||||
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;
|
||||
// response.AddHeader("Content-Range", String.Format("bytes */{0}", texture.Data.Length));
|
||||
// response.StatusCode = (int)System.Net.HttpStatusCode.OK;
|
||||
response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
|
||||
response.ContentType = texture.Metadata.ContentType;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -232,12 +250,18 @@ 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);
|
||||
|
||||
// Always return PartialContent, even if the range covered the entire data length
|
||||
// We were accidentally sending back 404 before in this situation
|
||||
// https://issues.apache.org/bugzilla/show_bug.cgi?id=51878 supports sending 206 even if the
|
||||
// entire range is requested, and viewer 3.2.2 (and very probably earlier) seems fine with this.
|
||||
//
|
||||
// We also do not want to send back OK even if the whole range was satisfiable since this causes
|
||||
// HTTP textures on at least Imprudence 1.4.0-beta2 to never display the final texture quality.
|
||||
// if (end > maxEnd)
|
||||
// response.StatusCode = (int)System.Net.HttpStatusCode.OK;
|
||||
// else
|
||||
response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
|
||||
|
||||
response.ContentLength = len;
|
||||
|
||||
@@ -17,3 +17,8 @@ CREATE TABLE `GridUser` (
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
COMMIT;
|
||||
|
||||
:VERSION 2 # --------------------------
|
||||
BEGIN;
|
||||
|
||||
COMMIT;
|
||||
|
||||
84
OpenSim/Framework/AssetPermissions.cs
Normal file
84
OpenSim/Framework/AssetPermissions.cs
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -355,10 +355,19 @@ 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);
|
||||
|
||||
foreach (KeyValuePair<string, Stat> kvp in StatsManager.RegisteredStats)
|
||||
{
|
||||
Stat stat = kvp.Value;
|
||||
|
||||
if (stat.Category == "scene" && stat.Verbosity == StatVerbosity.Info)
|
||||
{
|
||||
sb.AppendFormat("Slow frames ({0}): {1}\n", stat.Container, stat.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
sb.Append(Environment.NewLine);
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenSim.Framework.Monitoring
|
||||
{
|
||||
/// <summary>
|
||||
@@ -32,6 +35,14 @@ namespace OpenSim.Framework.Monitoring
|
||||
/// </summary>
|
||||
public class StatsManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Registered stats.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Do not add or remove from this dictionary.
|
||||
/// </remarks>
|
||||
public static Dictionary<string, Stat> RegisteredStats = new Dictionary<string, Stat>();
|
||||
|
||||
private static AssetStatsCollector assetStats;
|
||||
private static UserStatsCollector userStats;
|
||||
private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector();
|
||||
@@ -61,5 +72,139 @@ namespace OpenSim.Framework.Monitoring
|
||||
|
||||
return userStats;
|
||||
}
|
||||
|
||||
public static bool RegisterStat(Stat stat)
|
||||
{
|
||||
lock (RegisteredStats)
|
||||
{
|
||||
if (RegisteredStats.ContainsKey(stat.UniqueName))
|
||||
{
|
||||
// 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.
|
||||
return false;
|
||||
|
||||
// throw new Exception(
|
||||
// "StatsManager already contains stat with ShortName {0} in Category {1}", stat.ShortName, stat.Category);
|
||||
}
|
||||
|
||||
// We take a replace-on-write approach here so that we don't need to generate a new Dictionary
|
||||
Dictionary<string, Stat> newRegisteredStats = new Dictionary<string, Stat>(RegisteredStats);
|
||||
newRegisteredStats[stat.UniqueName] = stat;
|
||||
RegisteredStats = newRegisteredStats;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool DeregisterStat(Stat stat)
|
||||
{
|
||||
lock (RegisteredStats)
|
||||
{
|
||||
if (!RegisteredStats.ContainsKey(stat.UniqueName))
|
||||
return false;
|
||||
|
||||
Dictionary<string, Stat> newRegisteredStats = new Dictionary<string, Stat>(RegisteredStats);
|
||||
newRegisteredStats.Remove(stat.UniqueName);
|
||||
RegisteredStats = newRegisteredStats;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verbosity of stat.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Info will always be displayed.
|
||||
/// </remarks>
|
||||
public enum StatVerbosity
|
||||
{
|
||||
Debug,
|
||||
Info
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds individual static details
|
||||
/// </summary>
|
||||
public class Stat
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique stat name used for indexing. Each ShortName in a Category must be unique.
|
||||
/// </summary>
|
||||
public string UniqueName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Category of this stat (e.g. cache, scene, etc).
|
||||
/// </summary>
|
||||
public string Category { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The container.
|
||||
/// </value>
|
||||
public string Container { 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; set; }
|
||||
|
||||
public Stat(
|
||||
string shortName, string name, string unitName, string category, string container, StatVerbosity verbosity, string description)
|
||||
{
|
||||
ShortName = shortName;
|
||||
Name = name;
|
||||
UnitName = unitName;
|
||||
Category = category;
|
||||
Container = container;
|
||||
Verbosity = verbosity;
|
||||
Description = description;
|
||||
|
||||
UniqueName = GenUniqueName(Container, Category, ShortName);
|
||||
}
|
||||
|
||||
public static string GenUniqueName(string container, string category, string shortName)
|
||||
{
|
||||
return string.Format("{0}+{1}+{2}", container, category, shortName);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
throw new Exception("Cannot set value on a PercentageStat");
|
||||
}
|
||||
}
|
||||
|
||||
public PercentageStat(
|
||||
string shortName, string name, string category, string container, StatVerbosity verbosity, string description)
|
||||
: base(shortName, name, " %", category, container, verbosity, description)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,6 +126,7 @@ namespace OpenSim.Framework
|
||||
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();
|
||||
@@ -317,6 +318,11 @@ namespace OpenSim.Framework
|
||||
get { return m_objectCapacity; }
|
||||
}
|
||||
|
||||
public int LinksetCapacity
|
||||
{
|
||||
get { return m_linksetCapacity; }
|
||||
}
|
||||
|
||||
public int AgentCapacity
|
||||
{
|
||||
get { return m_agentCapacity; }
|
||||
@@ -637,11 +643,11 @@ namespace OpenSim.Framework
|
||||
|
||||
#region Prim stuff
|
||||
|
||||
m_nonphysPrimMin = config.GetFloat("NonphysicalPrimMin", 0);
|
||||
allKeys.Remove("NonphysicalPrimMin");
|
||||
m_nonphysPrimMin = config.GetFloat("NonPhysicalPrimMin", 0);
|
||||
allKeys.Remove("NonPhysicalPrimMin");
|
||||
|
||||
m_nonphysPrimMax = config.GetInt("NonphysicalPrimMax", 0);
|
||||
allKeys.Remove("NonphysicalPrimMax");
|
||||
m_nonphysPrimMax = config.GetInt("NonPhysicalPrimMax", 0);
|
||||
allKeys.Remove("NonPhysicalPrimMax");
|
||||
|
||||
m_physPrimMin = config.GetFloat("PhysicalPrimMin", 0);
|
||||
allKeys.Remove("PhysicalPrimMin");
|
||||
@@ -654,6 +660,9 @@ namespace OpenSim.Framework
|
||||
|
||||
m_objectCapacity = config.GetInt("MaxPrims", 15000);
|
||||
allKeys.Remove("MaxPrims");
|
||||
|
||||
m_linksetCapacity = config.GetInt("LinksetPrims", 0);
|
||||
allKeys.Remove("LinksetPrims");
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -692,24 +701,27 @@ namespace OpenSim.Framework
|
||||
|
||||
config.Set("ExternalHostName", m_externalHostName);
|
||||
|
||||
if (m_nonphysPrimMin != 0)
|
||||
if (m_nonphysPrimMin > 0)
|
||||
config.Set("NonphysicalPrimMax", m_nonphysPrimMin);
|
||||
|
||||
if (m_nonphysPrimMax != 0)
|
||||
if (m_nonphysPrimMax > 0)
|
||||
config.Set("NonphysicalPrimMax", m_nonphysPrimMax);
|
||||
|
||||
if (m_physPrimMin != 0)
|
||||
if (m_physPrimMin > 0)
|
||||
config.Set("PhysicalPrimMax", m_physPrimMin);
|
||||
|
||||
if (m_physPrimMax != 0)
|
||||
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)
|
||||
@@ -804,6 +816,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);
|
||||
|
||||
@@ -922,6 +937,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;
|
||||
@@ -1052,4 +1070,4 @@ namespace OpenSim.Framework
|
||||
return kvp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,11 @@ namespace OpenSim.Framework.Serialization
|
||||
/// </value>
|
||||
public const string INVENTORY_PATH = "inventory/";
|
||||
|
||||
/// <value>
|
||||
/// Path for regions in a multi-region archive
|
||||
/// </value>
|
||||
public const string REGIONS_PATH = "regions/";
|
||||
|
||||
/// <value>
|
||||
/// Path for the prims file
|
||||
/// </value>
|
||||
|
||||
@@ -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();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the debug level.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// See MainServer.DebugLevel.
|
||||
/// </value>
|
||||
public int DebugLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Request number for diagnostic purposes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
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<string, LLSDMethod> m_llsdHandlers = new Dictionary<string, LLSDMethod>();
|
||||
protected Dictionary<string, IRequestHandler> m_streamHandlers = new Dictionary<string, IRequestHandler>();
|
||||
protected Dictionary<string, GenericHTTPMethod> m_HTTPHandlers = new Dictionary<string, GenericHTTPMethod>();
|
||||
protected Dictionary<string, IHttpAgentHandler> m_agentHandlers = new Dictionary<string, IHttpAgentHandler>();
|
||||
// protected Dictionary<string, IHttpAgentHandler> m_agentHandlers = new Dictionary<string, IHttpAgentHandler>();
|
||||
protected Dictionary<string, PollServiceEventArgs> m_pollHandlers =
|
||||
new Dictionary<string, PollServiceEventArgs>();
|
||||
|
||||
@@ -245,29 +260,29 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||
return new List<string>(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<string> GetAgentHandlerKeys()
|
||||
{
|
||||
lock (m_agentHandlers)
|
||||
return new List<string>(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<string> GetAgentHandlerKeys()
|
||||
// {
|
||||
// lock (m_agentHandlers)
|
||||
// return new List<string>(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;
|
||||
@@ -405,7 +422,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.
|
||||
@@ -422,22 +438,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;
|
||||
@@ -449,9 +465,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.
|
||||
|
||||
@@ -531,8 +545,8 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||
|
||||
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);
|
||||
"[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}",
|
||||
RequestNumber, Port, request.ContentType, request.HttpMethod, request.Url.PathAndQuery, request.RemoteIPEndPoint);
|
||||
|
||||
buffer = HandleHTTPRequest(request, response);
|
||||
break;
|
||||
@@ -543,8 +557,8 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||
|
||||
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);
|
||||
"[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}",
|
||||
RequestNumber, Port, request.ContentType, request.HttpMethod, request.Url.PathAndQuery, request.RemoteIPEndPoint);
|
||||
|
||||
buffer = HandleLLSDRequests(request, response);
|
||||
break;
|
||||
@@ -563,9 +577,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);
|
||||
}
|
||||
@@ -573,18 +585,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);
|
||||
@@ -628,11 +636,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
|
||||
@@ -640,17 +648,93 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||
// Every month or so this will wrap and give bad numbers, not really a problem
|
||||
// since its just for reporting
|
||||
int tickdiff = requestEndTick - requestStartTick;
|
||||
if (tickdiff > 3000)
|
||||
if (tickdiff > 3000 && 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,
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -746,24 +830,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;
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Try all the registered xmlrpc handlers when an xmlrpc request is received.
|
||||
@@ -1688,21 +1772,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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
/// <summary>
|
||||
/// Add a handler for an HTTP request.
|
||||
@@ -106,13 +106,13 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||
|
||||
bool SetDefaultLLSDHandler(DefaultLLSDMethod handler);
|
||||
|
||||
/// <summary>
|
||||
/// Remove the agent if it is registered.
|
||||
/// </summary>
|
||||
/// <param name="agent"></param>
|
||||
/// <param name="handler"></param>
|
||||
/// <returns></returns>
|
||||
bool RemoveAgentHandler(string agent, IHttpAgentHandler handler);
|
||||
// /// <summary>
|
||||
// /// Remove the agent if it is registered.
|
||||
// /// </summary>
|
||||
// /// <param name="agent"></param>
|
||||
// /// <param name="handler"></param>
|
||||
// /// <returns></returns>
|
||||
// bool RemoveAgentHandler(string agent, IHttpAgentHandler handler);
|
||||
|
||||
/// <summary>
|
||||
/// Remove an HTTP handler
|
||||
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public static int DebugLevel
|
||||
{
|
||||
@@ -101,17 +105,28 @@ namespace OpenSim.Framework.Servers
|
||||
get { return new Dictionary<uint, BaseHttpServer>(m_Servers); }
|
||||
}
|
||||
|
||||
|
||||
public static void RegisterHttpConsoleCommands(ICommandConsole console)
|
||||
{
|
||||
console.Commands.AddCommand(
|
||||
"Debug", false, "debug http", "debug http [<level>]",
|
||||
"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 <in|out|all> [<level>]",
|
||||
"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.
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
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 <in|out|all> 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<String> 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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1007,6 +1007,38 @@ namespace OpenSim.Framework
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy data from one stream to another, leaving the read position of both streams at the beginning.
|
||||
/// </summary>
|
||||
/// <param name='inputStream'>
|
||||
/// Input stream. Must be seekable.
|
||||
/// </param>
|
||||
/// <exception cref='ArgumentException'>
|
||||
/// Thrown if the input stream is not seekable.
|
||||
/// </exception>
|
||||
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);
|
||||
|
||||
@@ -53,10 +53,18 @@ namespace OpenSim.Framework
|
||||
LogManager.GetLogger(
|
||||
MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
/// <summary>
|
||||
/// Control the printing of certain debug messages.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If DebugLevel >= 3 then short notices about outgoing HTTP requests are logged.
|
||||
/// </remarks>
|
||||
public static int DebugLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Request number for diagnostic purposes.
|
||||
/// </summary>
|
||||
public static int RequestNumber = 0;
|
||||
public static int RequestNumber { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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();
|
||||
@@ -229,7 +241,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,
|
||||
@@ -238,10 +250,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);
|
||||
}
|
||||
@@ -317,7 +333,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();
|
||||
@@ -380,7 +400,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,
|
||||
@@ -389,9 +409,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);
|
||||
}
|
||||
@@ -643,7 +667,6 @@ namespace OpenSim.Framework
|
||||
/// <returns></returns>
|
||||
public static string[] GetPreferredImageTypes(string accept)
|
||||
{
|
||||
|
||||
if (accept == null || accept == string.Empty)
|
||||
return new string[0];
|
||||
|
||||
@@ -693,18 +716,31 @@ namespace OpenSim.Framework
|
||||
//
|
||||
public static void MakeRequest<TRequest, TResponse>(string verb,
|
||||
string requestUrl, TRequest obj, Action<TResponse> action)
|
||||
{
|
||||
MakeRequest<TRequest, TResponse>(verb, requestUrl, obj, action, 0);
|
||||
}
|
||||
|
||||
public static void MakeRequest<TRequest, TResponse>(string verb,
|
||||
string requestUrl, TRequest obj, Action<TResponse> action,
|
||||
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;
|
||||
|
||||
// m_log.DebugFormat("[ASYNC REQUEST]: Starting {0} {1}", verb, requestUrl);
|
||||
|
||||
Type type = typeof(TRequest);
|
||||
|
||||
WebRequest request = WebRequest.Create(requestUrl);
|
||||
HttpWebRequest ht = (HttpWebRequest)request;
|
||||
if (maxConnections > 0 && ht.ServicePoint.ConnectionLimit < maxConnections)
|
||||
ht.ServicePoint.ConnectionLimit = maxConnections;
|
||||
|
||||
WebResponse response = null;
|
||||
TResponse deserial = default(TResponse);
|
||||
XmlSerializer deserializer = new XmlSerializer(typeof(TResponse));
|
||||
@@ -854,7 +890,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,
|
||||
@@ -862,6 +898,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,7 +924,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;
|
||||
@@ -967,13 +1013,17 @@ 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,
|
||||
tickdiff,
|
||||
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;
|
||||
}
|
||||
@@ -996,9 +1046,23 @@ namespace OpenSim.Framework
|
||||
/// <exception cref="System.Net.WebException">Thrown if we encounter a network issue while posting
|
||||
/// the request. You'll want to make sure you deal with this as they're not uncommon</exception>
|
||||
public static TResponse MakeRequest<TRequest, TResponse>(string verb, string requestUrl, TRequest obj)
|
||||
{
|
||||
return MakeRequest<TRequest, TResponse>(verb, requestUrl, obj, 0);
|
||||
}
|
||||
|
||||
public static TResponse MakeRequest<TRequest, TResponse>(string verb, string requestUrl, TRequest obj, int pTimeout)
|
||||
{
|
||||
return MakeRequest<TRequest, TResponse>(verb, requestUrl, obj, pTimeout, 0);
|
||||
}
|
||||
|
||||
public static TResponse MakeRequest<TRequest, TResponse>(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;
|
||||
@@ -1007,6 +1071,10 @@ namespace OpenSim.Framework
|
||||
TResponse deserial = default(TResponse);
|
||||
|
||||
WebRequest request = WebRequest.Create(requestUrl);
|
||||
HttpWebRequest ht = (HttpWebRequest)request;
|
||||
if (maxConnections > 0 && ht.ServicePoint.ConnectionLimit < maxConnections)
|
||||
ht.ServicePoint.ConnectionLimit = maxConnections;
|
||||
|
||||
request.Method = verb;
|
||||
MemoryStream buffer = null;
|
||||
|
||||
@@ -1111,7 +1179,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,
|
||||
@@ -1119,8 +1187,14 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using log4net;
|
||||
using log4net.Config;
|
||||
@@ -73,6 +74,7 @@ namespace OpenSim
|
||||
AppDomain.CurrentDomain.UnhandledException +=
|
||||
new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
|
||||
|
||||
ServicePointManager.DefaultConnectionLimit = 12;
|
||||
|
||||
// Add the arguments supplied when running the application to the configuration
|
||||
ArgvConfigSource configSource = new ArgvConfigSource(args);
|
||||
|
||||
@@ -292,7 +292,7 @@ namespace OpenSim
|
||||
|
||||
m_console.Commands.AddCommand("Archiving", false, "save oar",
|
||||
//"save oar [-v|--version=<N>] [-p|--profile=<url>] [<OAR path>]",
|
||||
"save oar [-h|--home=<url>] [--noassets] [--publish] [--perm=<permissions>] [<OAR path>]",
|
||||
"save oar [-h|--home=<url>] [--noassets] [--publish] [--perm=<permissions>] [--all] [<OAR path>]",
|
||||
"Save a region's data to an OAR archive.",
|
||||
// "-v|--version=<N> generates scene objects as per older versions of the serialization (e.g. -v=0)" + Environment.NewLine
|
||||
"-h|--home=<url> adds the url of the profile service to the saved user information.\n"
|
||||
@@ -302,6 +302,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=<permissions> stops objects with insufficient permissions from being saved to the OAR.\n"
|
||||
+ " <permissions> 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);
|
||||
@@ -332,10 +333,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);
|
||||
@@ -1013,33 +1010,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<String> 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)
|
||||
|
||||
@@ -222,6 +222,15 @@ 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);
|
||||
}
|
||||
|
||||
#region BinaryStats
|
||||
config = configSource.Configs["Statistics.Binary"];
|
||||
m_shouldCollectStats = false;
|
||||
|
||||
@@ -32,9 +32,8 @@ using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using log4net;
|
||||
|
||||
namespace OpenSim.Framework
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
|
||||
public sealed class PacketPool
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
@@ -44,6 +43,9 @@ namespace OpenSim.Framework
|
||||
private bool packetPoolEnabled = true;
|
||||
private bool dataBlockPoolEnabled = true;
|
||||
|
||||
/// <summary>
|
||||
/// Pool of packets available for reuse.
|
||||
/// </summary>
|
||||
private readonly Dictionary<PacketType, Stack<Packet>> pool = new Dictionary<PacketType, Stack<Packet>>();
|
||||
|
||||
private static Dictionary<Type, Stack<Object>> DataBlocks =
|
||||
@@ -244,4 +246,4 @@ namespace OpenSim.Framework
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ namespace OpenSim.Region.ClientStack
|
||||
|
||||
protected override void StartupSpecific()
|
||||
{
|
||||
SceneManager = new SceneManager();
|
||||
SceneManager = SceneManager.Instance;
|
||||
m_clientStackManager = CreateClientStackManager();
|
||||
|
||||
Initialize();
|
||||
|
||||
@@ -57,39 +57,36 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a xfer uploader if one does not already exist.
|
||||
/// Return the xfer uploader for the given transaction.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If an uploader does not already exist for this transaction then it is created, otherwise the existing
|
||||
/// uploader is returned.
|
||||
/// </remarks>
|
||||
/// <param name="transactionID"></param>
|
||||
/// <param name="assetID">
|
||||
/// 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.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// 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.
|
||||
/// </returns>
|
||||
public AssetXferUploader RequestXferUploader(UUID transactionID, UUID assetID)
|
||||
/// <returns>The asset xfer uploader</returns>
|
||||
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,115 +148,28 @@ 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];
|
||||
}
|
||||
|
||||
if (uploader != null)
|
||||
uploader.RequestCreateInventoryItem(
|
||||
remoteClient, transactionID, folderID,
|
||||
callbackID, description, name, invType, type,
|
||||
wearableType, nextOwnerMask);
|
||||
else
|
||||
m_log.ErrorFormat(
|
||||
"[AGENT ASSET TRANSACTIONS]: Could not find uploader with transaction ID {0} when handling request to create inventory item {1} from {2}",
|
||||
transactionID, name, remoteClient.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an uploaded asset. If the data is successfully retrieved,
|
||||
/// the transaction will be removed.
|
||||
/// </summary>
|
||||
/// <param name="transactionID"></param>
|
||||
/// <returns>The asset if the upload has completed, null if it has not.</returns>
|
||||
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;
|
||||
uploader.RequestCreateInventoryItem(
|
||||
remoteClient, folderID, callbackID,
|
||||
description, name, invType, type, wearableType, nextOwnerMask);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -40,39 +40,75 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
/// <summary>
|
||||
/// Upload state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// New -> Uploading -> Complete
|
||||
/// </remarks>
|
||||
private enum UploadState
|
||||
{
|
||||
New,
|
||||
Uploading,
|
||||
Complete
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the object that holds this uploader. Used to remove ourselves from it's list if we
|
||||
/// are performing a delayed update.
|
||||
/// </summary>
|
||||
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;
|
||||
public ulong XferID;
|
||||
private Scene m_Scene;
|
||||
|
||||
public AssetXferUploader(AgentAssetTransactions transactions, Scene scene, UUID assetID, bool dumpAssetToFile)
|
||||
/// <summary>
|
||||
/// AssetXferUploader constructor
|
||||
/// </summary>
|
||||
/// <param name='transactions'>/param>
|
||||
/// <param name='scene'></param>
|
||||
/// <param name='transactionID'></param>
|
||||
/// <param name='dumpAssetToFile'>
|
||||
/// 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.
|
||||
/// </param>
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -118,30 +154,50 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialise asset transfer from the client
|
||||
/// Start asset transfer from the client
|
||||
/// </summary>
|
||||
/// <param name="xferID"></param>
|
||||
/// <param name="packetID"></param>
|
||||
/// <param name="data"></param>
|
||||
public void Initialise(IClientAPI remoteClient, UUID assetID,
|
||||
UUID transaction, sbyte type, byte[] data, bool storeLocal,
|
||||
bool tempFile)
|
||||
/// <param name="remoteClient"></param>
|
||||
/// <param name="assetID"></param>
|
||||
/// <param name="transaction"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="data">
|
||||
/// 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.
|
||||
/// </param>
|
||||
/// <param name="storeLocal"></param>
|
||||
/// <param name="tempFile"></param>
|
||||
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)
|
||||
{
|
||||
@@ -166,36 +222,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)
|
||||
{
|
||||
@@ -223,40 +278,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)
|
||||
@@ -271,9 +323,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
|
||||
{
|
||||
@@ -287,20 +339,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store the asset for the given item.
|
||||
/// Store the asset for the given item when it has been uploaded.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
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)
|
||||
/// <summary>
|
||||
/// Store the asset for the given task item when it has been uploaded.
|
||||
/// </summary>
|
||||
/// <param name="taskItem"></param>
|
||||
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)
|
||||
{
|
||||
m_Scene.AssetService.Store(m_asset);
|
||||
|
||||
@@ -326,20 +417,8 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
|
||||
ourClient.SendInventoryItemCreateUpdate(item, callbackID);
|
||||
else
|
||||
ourClient.SendAlertMessage("Unable to create inventory item");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the asset data uploaded in this transfer.
|
||||
/// </summary>
|
||||
/// <returns>null if the asset has not finished uploading</returns>
|
||||
public AssetBase GetAssetData()
|
||||
{
|
||||
if (m_finished)
|
||||
{
|
||||
return m_asset;
|
||||
}
|
||||
|
||||
return null;
|
||||
m_transactions.RemoveXferUploader(m_transactionID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Scene> m_Scenes = new List<Scene>();
|
||||
|
||||
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);
|
||||
@@ -506,13 +502,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))
|
||||
{
|
||||
@@ -705,11 +698,14 @@ namespace Flotsam.RegionModules.AssetCache
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private int CacheScenes()
|
||||
/// <param name="storeUncached">
|
||||
/// If true, then assets scanned which are not found in cache are added to the cache.
|
||||
/// </param>
|
||||
/// <returns>Number of distinct asset references found in the scene.</returns>
|
||||
private int TouchAllSceneAssets(bool storeUncached)
|
||||
{
|
||||
UuidGatherer gatherer = new UuidGatherer(m_AssetService);
|
||||
|
||||
@@ -732,7 +728,7 @@ namespace Flotsam.RegionModules.AssetCache
|
||||
{
|
||||
File.SetLastAccessTime(filename, DateTime.Now);
|
||||
}
|
||||
else
|
||||
else if (storeUncached)
|
||||
{
|
||||
m_AssetService.Get(assetID.ToString());
|
||||
}
|
||||
@@ -860,13 +856,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -238,8 +238,22 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
|
||||
|
||||
sp.ClearAttachments();
|
||||
}
|
||||
|
||||
|
||||
public bool AttachObject(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent, bool temp)
|
||||
{
|
||||
if (!Enabled)
|
||||
return false;
|
||||
|
||||
if (AttachObjectInternal(sp, group, attachmentPt, silent, 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 temp)
|
||||
{
|
||||
lock (sp.AttachmentsSyncLock)
|
||||
{
|
||||
@@ -392,6 +406,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;
|
||||
@@ -434,7 +453,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();
|
||||
@@ -794,7 +817,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
|
||||
// This will throw if the attachment fails
|
||||
try
|
||||
{
|
||||
AttachObject(sp, objatt, attachmentPt, false, false);
|
||||
AttachObjectInternal(sp, objatt, attachmentPt, false, false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -859,6 +882,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)
|
||||
{
|
||||
@@ -948,15 +974,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
|
||||
AttachmentPt &= 0x7f;
|
||||
|
||||
// Calls attach with a Zero position
|
||||
if (AttachObject(sp, part.ParentGroup, AttachmentPt, false, false))
|
||||
{
|
||||
// m_log.Debug(
|
||||
// "[ATTACHMENTS MODULE]: Saving avatar attachment. AgentID: " + remoteClient.AgentId
|
||||
// + ", AttachmentPoint: " + AttachmentPt);
|
||||
|
||||
// Save avatar attachment information
|
||||
m_scene.EventManager.TriggerOnAttach(objectLocalID, part.ParentGroup.FromItemID, remoteClient.AgentId);
|
||||
}
|
||||
AttachObject(sp, part.ParentGroup, AttachmentPt, false, false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -461,10 +489,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
||||
|
||||
SceneObjectGroup rezzedAtt = presence.GetAttachments()[0];
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -525,7 +525,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
||||
{
|
||||
for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++)
|
||||
{
|
||||
for (int j = 0; j < appearance.Wearables[j].Count; j++)
|
||||
for (int j = 0; j < appearance.Wearables[i].Count; j++)
|
||||
{
|
||||
if (appearance.Wearables[i][j].ItemID == UUID.Zero)
|
||||
continue;
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -482,9 +482,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);
|
||||
|
||||
@@ -297,6 +297,45 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (im.dialog == (byte) InstantMessageDialog.TaskInventoryAccepted)
|
||||
{
|
||||
UUID destinationFolderID = UUID.Zero;
|
||||
|
||||
if (im.binaryBucket != null && im.binaryBucket.Length >= 16)
|
||||
{
|
||||
destinationFolderID = new UUID(im.binaryBucket, 0);
|
||||
}
|
||||
|
||||
if (destinationFolderID != UUID.Zero)
|
||||
{
|
||||
IInventoryService invService = scene.InventoryService;
|
||||
|
||||
UUID inventoryID = new UUID(im.imSessionID); // The inventory item/folder, back from it's trip
|
||||
|
||||
InventoryItemBase item = new InventoryItemBase(inventoryID, client.AgentId);
|
||||
item = invService.GetItem(item);
|
||||
InventoryFolderBase folder = null;
|
||||
|
||||
if (item != null) // It's an item
|
||||
{
|
||||
item.Folder = destinationFolderID;
|
||||
|
||||
invService.DeleteItems(item.Owner, new List<UUID>() { item.ID });
|
||||
scene.AddInventoryItem(client, item);
|
||||
}
|
||||
else
|
||||
{
|
||||
folder = new InventoryFolderBase(inventoryID, client.AgentId);
|
||||
folder = invService.GetFolder(folder);
|
||||
|
||||
if (folder != null) // It's a folder
|
||||
{
|
||||
folder.ParentID = destinationFolderID;
|
||||
invService.MoveFolder(folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (
|
||||
im.dialog == (byte)InstantMessageDialog.InventoryDeclined
|
||||
|| im.dialog == (byte)InstantMessageDialog.TaskInventoryDeclined)
|
||||
|
||||
@@ -323,6 +323,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
|
||||
//
|
||||
@@ -354,6 +362,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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this instance is within the max transfer distance.
|
||||
/// </summary>
|
||||
@@ -568,7 +583,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(
|
||||
@@ -695,7 +710,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);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,59 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||
|
||||
private GatekeeperServiceConnector m_GatekeeperConnector;
|
||||
|
||||
protected bool m_RestrictAppearanceAbroad;
|
||||
protected string m_AccountName;
|
||||
protected List<AvatarAppearance> m_ExportedAppearances;
|
||||
protected List<AvatarAttachment> m_Attachs;
|
||||
|
||||
protected List<AvatarAppearance> ExportedAppearance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_ExportedAppearances != null)
|
||||
return m_ExportedAppearances;
|
||||
|
||||
m_ExportedAppearances = new List<AvatarAppearance>();
|
||||
m_Attachs = new List<AvatarAttachment>();
|
||||
|
||||
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<IUserAgentVerificationModule>(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<UUID, AssetType> ids = new Dictionary<UUID, AssetType>();
|
||||
HGUuidGatherer uuidGatherer = new HGUuidGatherer(so.Scene.AssetService, url);
|
||||
uuidGatherer.GatherAssetUuids(so, ids);
|
||||
|
||||
foreach (KeyValuePair<UUID, AssetType> kvp in ids)
|
||||
uuidGatherer.FetchAsset(kvp.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNewClient(IClientAPI client)
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,6 +289,124 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||
return base.CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout);
|
||||
}
|
||||
|
||||
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.Data.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<IAvatarFactoryModule>();
|
||||
// //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 void TeleportHome(UUID id, IClientAPI client)
|
||||
{
|
||||
m_log.DebugFormat(
|
||||
|
||||
@@ -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<UUID, AssetType> ids = new Dictionary<UUID, AssetType>();
|
||||
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<UUID, AssetType> ids = new Dictionary<UUID, AssetType>();
|
||||
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<UUID, AssetType> ids = new Dictionary<UUID, AssetType>();
|
||||
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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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<string, float> 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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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]";
|
||||
|
||||
/// <summary>
|
||||
/// Create a log writer that will not write anything. Good for when not enabled
|
||||
/// but the write statements are still in the code.
|
||||
/// </summary>
|
||||
public LogWriter()
|
||||
{
|
||||
Enabled = false;
|
||||
m_logFile = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a log writer instance.
|
||||
/// </summary>
|
||||
/// <param name="dir">The directory to create the log file in. May be 'null' for default.</param>
|
||||
/// <param name="headr">The characters that begin the log file name. May be 'null' for default.</param>
|
||||
/// <param name="maxFileTime">Maximum age of a log file in minutes. If zero, will set default.</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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]";
|
||||
|
||||
/// <summary>
|
||||
/// Create a log writer that will not write anything. Good for when not enabled
|
||||
/// but the write statements are still in the code.
|
||||
/// </summary>
|
||||
public LogWriter()
|
||||
{
|
||||
Enabled = false;
|
||||
m_logFile = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a log writer instance.
|
||||
/// </summary>
|
||||
/// <param name="dir">The directory to create the log file in. May be 'null' for default.</param>
|
||||
/// <param name="headr">The characters that begin the log file name. May be 'null' for default.</param>
|
||||
/// <param name="maxFileTime">Maximum age of a log file in minutes. If zero, will set default.</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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<UUID, UserData> 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;
|
||||
|
||||
@@ -26,32 +26,36 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
|
||||
namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
|
||||
{
|
||||
public class HGUuidGatherer : UuidGatherer
|
||||
public class DynamicTexture : IDynamicTexture
|
||||
{
|
||||
protected string m_assetServerURL;
|
||||
protected HGAssetMapper m_assetMapper;
|
||||
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 HGUuidGatherer(HGAssetMapper assMap, IAssetService assetCache, string assetServerURL) : base(assetCache)
|
||||
public DynamicTexture(string inputCommands, string inputParams, byte[] data, Size size, bool isReuseable)
|
||||
{
|
||||
m_assetMapper = assMap;
|
||||
m_assetServerURL = assetServerURL;
|
||||
InputCommands = inputCommands;
|
||||
InputParams = inputParams;
|
||||
Data = data;
|
||||
Size = size;
|
||||
IsReuseable = isReuseable;
|
||||
}
|
||||
|
||||
protected override AssetBase GetAsset(UUID uuid)
|
||||
public DynamicTexture(Uri inputUri, string inputParams, byte[] data, Size size, bool isReuseable)
|
||||
{
|
||||
if (string.Empty == m_assetServerURL)
|
||||
return m_assetCache.Get(uuid.ToString());
|
||||
else
|
||||
return m_assetMapper.FetchAsset(m_assetServerURL, uuid);
|
||||
InputUri = inputUri;
|
||||
InputParams = inputParams;
|
||||
Data = data;
|
||||
Size = size;
|
||||
IsReuseable = isReuseable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ 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;
|
||||
|
||||
@@ -54,6 +54,17 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
|
||||
/// </summary>
|
||||
public bool ReuseTextures { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If false, then textures which have a low data size are not reused when ReuseTextures = true.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.</remarks>
|
||||
public bool ReuseLowDataTextures { get; set; }
|
||||
|
||||
private Dictionary<UUID, Scene> RegisteredScenes = new Dictionary<UUID, Scene>();
|
||||
|
||||
private Dictionary<string, IDynamicTextureRender> RenderPlugins =
|
||||
@@ -83,18 +94,17 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
|
||||
/// <summary>
|
||||
/// Called by code which actually renders the dynamic texture to supply texture data.
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="isReuseable">True if the data generated can be reused for subsequent identical requests</param>
|
||||
public void ReturnData(UUID id, byte[] data, bool isReuseable)
|
||||
/// <param name="updaterId"></param>
|
||||
/// <param name="texture"></param>
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,11 +113,16 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
|
||||
if (RegisteredScenes.ContainsKey(updater.SimUUID))
|
||||
{
|
||||
Scene scene = RegisteredScenes[updater.SimUUID];
|
||||
UUID newTextureID = updater.DataReceived(data, scene);
|
||||
UUID newTextureID = updater.DataReceived(texture.Data, scene);
|
||||
|
||||
if (ReuseTextures && isReuseable && !updater.BlendWithOldTexture)
|
||||
if (ReuseTextures
|
||||
&& !updater.BlendWithOldTexture
|
||||
&& texture.IsReuseable
|
||||
&& (ReuseLowDataTextures || IsDataSizeReuseable(texture)))
|
||||
{
|
||||
m_reuseableDynamicTextures.Store(
|
||||
GenerateReusableTextureKey(updater.BodyData, updater.Params), newTextureID);
|
||||
GenerateReusableTextureKey(texture.InputCommands, texture.InputParams), newTextureID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +138,27 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the texture is reuseable based on its data size.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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).
|
||||
/// </remarks>
|
||||
/// <returns></returns>
|
||||
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)
|
||||
{
|
||||
@@ -249,10 +285,18 @@ 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);
|
||||
}
|
||||
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);
|
||||
@@ -285,7 +329,10 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
|
||||
{
|
||||
IConfig texturesConfig = config.Configs["Textures"];
|
||||
if (texturesConfig != null)
|
||||
{
|
||||
ReuseTextures = texturesConfig.GetBoolean("ReuseDynamicTextures", false);
|
||||
ReuseLowDataTextures = texturesConfig.GetBoolean("ReuseDynamicLowDataTextures", false);
|
||||
}
|
||||
|
||||
if (!RegisteredScenes.ContainsKey(scene.RegionInfo.RegionID))
|
||||
{
|
||||
@@ -448,8 +495,10 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
|
||||
IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface<IJ2KDecoder>();
|
||||
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 = UpdatePart(part, asset.FullID);
|
||||
|
||||
@@ -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;
|
||||
@@ -73,12 +74,12 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
|
||||
// return false;
|
||||
// }
|
||||
|
||||
public byte[] ConvertUrl(string url, string extraParams)
|
||||
public IDynamicTexture ConvertUrl(string url, string extraParams)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] ConvertData(string bodyData, string extraParams)
|
||||
public IDynamicTexture ConvertData(string bodyData, string extraParams)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -171,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
|
||||
{
|
||||
@@ -188,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)
|
||||
{
|
||||
@@ -233,7 +240,6 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -243,10 +249,13 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
|
||||
}
|
||||
}
|
||||
|
||||
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, false);
|
||||
m_textureManager.ReturnData(
|
||||
state.RequestID,
|
||||
new OpenSim.Region.CoreModules.Scripting.DynamicTexture.DynamicTexture(
|
||||
request.RequestUri, null, imageJ2000, newSize, false));
|
||||
}
|
||||
|
||||
#region Nested type: RequestState
|
||||
|
||||
@@ -211,6 +211,23 @@ namespace OpenSim.Region.OptionalModules.Scripting.ScriptModuleComms
|
||||
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()
|
||||
{
|
||||
@@ -313,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operation to check for a registered constant
|
||||
/// </summary>
|
||||
@@ -57,6 +57,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
|
||||
|
||||
m_dtm = new DynamicTextureModule();
|
||||
m_dtm.ReuseTextures = reuseTextures;
|
||||
// m_dtm.ReuseLowDataTextures = reuseTextures;
|
||||
|
||||
m_vrm = new VectorRenderModule();
|
||||
|
||||
@@ -201,6 +202,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
|
||||
public void TestRepeatSameDrawReusingTexture()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
|
||||
|
||||
@@ -228,6 +230,46 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
|
||||
Assert.That(firstDynamicTextureID, Is.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test a low data dynamically generated texture such that it is treated as a low data texture that causes
|
||||
/// problems for current viewers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As we do not set DynamicTextureModule.ReuseLowDataTextures = true in this test, it should not reuse the
|
||||
/// texture
|
||||
/// </remarks>
|
||||
[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()
|
||||
{
|
||||
|
||||
@@ -35,6 +35,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;
|
||||
@@ -46,6 +47,11 @@ 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 Scene m_scene;
|
||||
@@ -80,20 +86,14 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
|
||||
// return lines.Any((str, r) => str.StartsWith("Image"));
|
||||
// }
|
||||
|
||||
public byte[] ConvertUrl(string url, string extraParams)
|
||||
public IDynamicTexture ConvertUrl(string url, string extraParams)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] ConvertData(string bodyData, string extraParams)
|
||||
public IDynamicTexture ConvertData(string bodyData, string extraParams)
|
||||
{
|
||||
bool reuseable;
|
||||
return Draw(bodyData, extraParams, out reuseable);
|
||||
}
|
||||
|
||||
private byte[] ConvertData(string bodyData, string extraParams, out bool reuseable)
|
||||
{
|
||||
return Draw(bodyData, extraParams, out reuseable);
|
||||
return Draw(bodyData, extraParams);
|
||||
}
|
||||
|
||||
public bool AsyncConvertUrl(UUID id, string url, string extraParams)
|
||||
@@ -104,10 +104,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
|
||||
public bool AsyncConvertData(UUID id, string bodyData, string extraParams)
|
||||
{
|
||||
// XXX: This isn't actually being done asynchronously!
|
||||
bool reuseable;
|
||||
byte[] data = ConvertData(bodyData, extraParams, out reuseable);
|
||||
|
||||
m_textureManager.ReturnData(id, data, reuseable);
|
||||
m_textureManager.ReturnData(id, ConvertData(bodyData, extraParams));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -115,14 +112,19 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,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()
|
||||
@@ -179,7 +188,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
|
||||
|
||||
#endregion
|
||||
|
||||
private byte[] Draw(string data, string extraParams, out bool reuseable)
|
||||
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
|
||||
@@ -322,6 +331,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
|
||||
|
||||
Bitmap bitmap = null;
|
||||
Graphics graph = null;
|
||||
bool reuseable = false;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -364,6 +374,14 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
@@ -376,7 +394,8 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
|
||||
e.Message, e.StackTrace);
|
||||
}
|
||||
|
||||
return imageJ2000;
|
||||
return new OpenSim.Region.CoreModules.Scripting.DynamicTexture.DynamicTexture(
|
||||
data, extraParams, imageJ2000, new Size(width, height), reuseable);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<string, GridUserInfo> m_Infos = new ExpiringCache<string, GridUserInfo>();
|
||||
|
||||
#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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
/// <summary>
|
||||
/// Contains data used while dearchiving a single scene.
|
||||
/// </summary>
|
||||
private class DearchiveContext
|
||||
{
|
||||
public Scene Scene { get; set; }
|
||||
|
||||
public List<string> SerialisedSceneObjects { get; set; }
|
||||
|
||||
public List<string> SerialisedParcels { get; set; }
|
||||
|
||||
public List<SceneObjectGroup> SceneObjects { get; set; }
|
||||
|
||||
public DearchiveContext(Scene scene)
|
||||
{
|
||||
Scene = scene;
|
||||
SerialisedSceneObjects = new List<string>();
|
||||
SerialisedParcels = new List<string>();
|
||||
SceneObjects = new List<SceneObjectGroup>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// <summary>
|
||||
/// Has the control file been loaded for this archive?
|
||||
/// </summary>
|
||||
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,7 +116,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||
{
|
||||
if (m_UserMan == null)
|
||||
{
|
||||
m_UserMan = m_scene.RequestModuleInterface<IUserManagement>();
|
||||
m_UserMan = m_rootScene.RequestModuleInterface<IUserManagement>();
|
||||
}
|
||||
return m_UserMan;
|
||||
}
|
||||
@@ -104,10 +129,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||
|
||||
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);
|
||||
@@ -128,12 +157,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||
// Zero can never be a valid user id
|
||||
m_validUserUuids[UUID.Zero] = false;
|
||||
|
||||
m_groupsModule = m_scene.RequestModuleInterface<IGroupsModule>();
|
||||
m_groupsModule = m_rootScene.RequestModuleInterface<IGroupsModule>();
|
||||
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;
|
||||
@@ -142,33 +173,34 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||
// Zero can never be a valid user id
|
||||
m_validUserUuids[UUID.Zero] = false;
|
||||
|
||||
m_groupsModule = m_scene.RequestModuleInterface<IGroupsModule>();
|
||||
m_groupsModule = m_rootScene.RequestModuleInterface<IGroupsModule>();
|
||||
m_assetService = m_rootScene.AssetService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dearchive the region embodied in this request.
|
||||
/// </summary>
|
||||
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<string> serialisedSceneObjects = new List<string>();
|
||||
List<string> serialisedParcels = new List<string>();
|
||||
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<UUID, DearchiveContext> sceneContexts = new Dictionary<UUID, DearchiveContext>();
|
||||
|
||||
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);
|
||||
@@ -176,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)
|
||||
{
|
||||
@@ -192,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,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<UUID>(), m_errorMessage);
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
archive.Close();
|
||||
if (archive != null)
|
||||
archive.Close();
|
||||
}
|
||||
|
||||
if (!m_skipAssets)
|
||||
@@ -234,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<IEstateModule>();
|
||||
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<UUID>(), 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("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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
/// <param name="archive"></param>
|
||||
/// <param name="dearchivedScenes"></param>
|
||||
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");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load serialized scene objects.
|
||||
/// </summary>
|
||||
/// <param name="serialisedSceneObjects"></param>
|
||||
protected void LoadObjects(List<string> serialisedSceneObjects)
|
||||
protected void LoadObjects(Scene scene, List<string> serialisedSceneObjects, List<SceneObjectGroup> 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>();
|
||||
IRegionSerialiserModule serialiser = scene.RequestModuleInterface<IRegionSerialiserModule>();
|
||||
int sceneObjectsLoadedCount = 0;
|
||||
|
||||
foreach (string serialisedSceneObject in serialisedSceneObjects)
|
||||
@@ -280,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
|
||||
@@ -290,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;
|
||||
}
|
||||
|
||||
@@ -301,17 +466,17 @@ 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;
|
||||
@@ -328,15 +493,15 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||
TaskInventoryDictionary inv = part.TaskInventory;
|
||||
foreach (KeyValuePair<UUID, TaskInventoryItem> 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)
|
||||
@@ -348,10 +513,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -366,16 +531,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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load serialized parcels.
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="serialisedParcels"></param>
|
||||
protected void LoadParcels(List<string> serialisedParcels)
|
||||
protected void LoadParcels(Scene scene, List<string> serialisedParcels)
|
||||
{
|
||||
// Reload serialized parcels
|
||||
m_log.InfoFormat("[ARCHIVER]: Loading {0} parcels. Please wait.", serialisedParcels.Count);
|
||||
@@ -386,8 +552,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||
|
||||
// Validate User and Group UUID's
|
||||
|
||||
if (!ResolveUserUuid(parcel.OwnerID))
|
||||
parcel.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
|
||||
if (!ResolveUserUuid(scene, parcel.OwnerID))
|
||||
parcel.OwnerID = m_rootScene.RegionInfo.EstateSettings.EstateOwner;
|
||||
|
||||
if (!ResolveGroupUuid(parcel.GroupID))
|
||||
{
|
||||
@@ -398,7 +564,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||
List<LandAccessEntry> accessList = new List<LandAccessEntry>();
|
||||
foreach (LandAccessEntry entry in parcel.ParcelAccessList)
|
||||
{
|
||||
if (ResolveUserUuid(entry.AgentID))
|
||||
if (ResolveUserUuid(scene, entry.AgentID))
|
||||
accessList.Add(entry);
|
||||
// else, drop this access rule
|
||||
}
|
||||
@@ -414,23 +580,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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Look up the given user id to check whether it's one that is valid for this grid.
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="uuid"></param>
|
||||
/// <returns></returns>
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -485,7 +652,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;
|
||||
@@ -505,7 +672,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
|
||||
@@ -533,12 +700,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||
/// <summary>
|
||||
/// Load region settings data
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="settingsPath"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="dearchivedScenes"></param>
|
||||
/// <returns>
|
||||
/// true if settings were loaded successfully, false otherwise
|
||||
/// </returns>
|
||||
private bool LoadRegionSettings(string settingsPath, byte[] data)
|
||||
private bool LoadRegionSettings(Scene scene, string settingsPath, byte[] data, DearchiveScenesInfo dearchivedScenes)
|
||||
{
|
||||
RegionSettings loadedRegionSettings;
|
||||
|
||||
@@ -554,7 +723,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;
|
||||
@@ -591,12 +760,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>();
|
||||
|
||||
IEstateModule estateModule = scene.RequestModuleInterface<IEstateModule>();
|
||||
if (estateModule != null)
|
||||
estateModule.sendRegionHandshakeToAll();
|
||||
|
||||
@@ -606,14 +777,15 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||
/// <summary>
|
||||
/// Load terrain data
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="terrainPath"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <returns>
|
||||
/// true if terrain was resolved successfully, false otherwise.
|
||||
/// </returns>
|
||||
private bool LoadTerrain(string terrainPath, byte[] data)
|
||||
private bool LoadTerrain(Scene scene, string terrainPath, byte[] data)
|
||||
{
|
||||
ITerrainModule terrainModule = m_scene.RequestModuleInterface<ITerrainModule>();
|
||||
ITerrainModule terrainModule = scene.RequestModuleInterface<ITerrainModule>();
|
||||
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
terrainModule.LoadFromStream(terrainPath, ms);
|
||||
@@ -629,17 +801,18 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="data"></param>
|
||||
public void LoadControlFile(string path, byte[] data)
|
||||
/// <param name="dearchivedScenes"></param>
|
||||
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())
|
||||
{
|
||||
@@ -665,18 +838,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
176
OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs
Normal file
176
OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A group of regions arranged in a rectangle, possibly with holes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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).
|
||||
/// </remarks>
|
||||
public class ArchiveScenesGroup
|
||||
{
|
||||
/// <summary>
|
||||
/// All the regions. The outer dictionary contains rows (key: Y coordinate).
|
||||
/// The inner dictionaries contain each row's regions (key: X coordinate).
|
||||
/// </summary>
|
||||
public SortedDictionary<uint, SortedDictionary<uint, Scene>> Regions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The subdirectory where each region is stored in the archive.
|
||||
/// </summary>
|
||||
protected Dictionary<UUID, string> m_regionDirs;
|
||||
|
||||
/// <summary>
|
||||
/// The grid coordinates of the regions' bounding box.
|
||||
/// </summary>
|
||||
public Rectangle Rect { get; set; }
|
||||
|
||||
|
||||
public ArchiveScenesGroup()
|
||||
{
|
||||
Regions = new SortedDictionary<uint, SortedDictionary<uint, Scene>>();
|
||||
m_regionDirs = new Dictionary<UUID, string>();
|
||||
Rect = new Rectangle(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
public void AddScene(Scene scene)
|
||||
{
|
||||
uint x = scene.RegionInfo.RegionLocX;
|
||||
uint y = scene.RegionInfo.RegionLocY;
|
||||
|
||||
SortedDictionary<uint, Scene> row;
|
||||
if (!Regions.TryGetValue(y, out row))
|
||||
{
|
||||
row = new SortedDictionary<uint, Scene>();
|
||||
Regions[y] = row;
|
||||
}
|
||||
|
||||
row[x] = scene;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after all the scenes have been added. Performs calculations that require
|
||||
/// knowledge of all the scenes.
|
||||
/// </summary>
|
||||
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<uint, Scene> 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)(lastY - firstY + 1), (int)(lastX - firstX + 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;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the subdirectory where the region is stored.
|
||||
/// </summary>
|
||||
/// <param name="regionID"></param>
|
||||
/// <returns></returns>
|
||||
public string GetRegionDir(UUID regionID)
|
||||
{
|
||||
return m_regionDirs[regionID];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs an action on all the scenes in this order: rows from South to North,
|
||||
/// and within each row West to East.
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
public void ForEachScene(Action<Scene> action)
|
||||
{
|
||||
foreach (SortedDictionary<uint, Scene> row in Regions.Values)
|
||||
{
|
||||
foreach (Scene scene in row.Values)
|
||||
{
|
||||
action(scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the scene at position 'location'.
|
||||
/// </summary>
|
||||
/// <param name="location">A location in the grid</param>
|
||||
/// <param name="scene">The scene at this location</param>
|
||||
/// <returns>Whether the scene was found</returns>
|
||||
public bool TryGetScene(Point location, out Scene scene)
|
||||
{
|
||||
SortedDictionary<uint, Scene> row;
|
||||
if (Regions.TryGetValue((uint)location.Y, out row))
|
||||
{
|
||||
if (row.TryGetValue((uint)location.X, out scene))
|
||||
return true;
|
||||
}
|
||||
|
||||
scene = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
634
OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs
Normal file
634
OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Prepare to write out an archive.
|
||||
/// </summary>
|
||||
public class ArchiveWriteRequest
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
/// <summary>
|
||||
/// The minimum major version of OAR that we can write.
|
||||
/// </summary>
|
||||
public static int MIN_MAJOR_VERSION = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum major version of OAR that we can write.
|
||||
/// </summary>
|
||||
public static int MAX_MAJOR_VERSION = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we're saving a multi-region archive.
|
||||
/// </summary>
|
||||
public bool MultiRegionFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether this archive will save assets. Default is true.
|
||||
/// </summary>
|
||||
public bool SaveAssets { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines which objects will be included in the archive, according to their permissions.
|
||||
/// Default is null, meaning no permission checks.
|
||||
/// </summary>
|
||||
public string CheckPermissions { get; set; }
|
||||
|
||||
protected Scene m_rootScene;
|
||||
protected Stream m_saveStream;
|
||||
protected TarArchiveWriter m_archiveWriter;
|
||||
protected Guid m_requestId;
|
||||
protected Dictionary<string, object> m_options;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="module">Calling module</param>
|
||||
/// <param name="savePath">The path to which to save data.</param>
|
||||
/// <param name="requestId">The id associated with this request</param>
|
||||
/// <exception cref="System.IO.IOException">
|
||||
/// If there was a problem opening a stream for the file specified by the savePath
|
||||
/// </exception>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="scene">The root scene to archive</param>
|
||||
/// <param name="saveStream">The stream to which to save data.</param>
|
||||
/// <param name="requestId">The id associated with this request</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Archive the region requested.
|
||||
/// </summary>
|
||||
/// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception>
|
||||
public void ArchiveRegion(Dictionary<string, object> 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<UUID, AssetType> assetUuids = new Dictionary<UUID, AssetType>();
|
||||
|
||||
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<UUID, AssetType> assetUuids)
|
||||
{
|
||||
m_log.InfoFormat("[ARCHIVER]: Writing region {0}", scene.RegionInfo.RegionName);
|
||||
|
||||
EntityBase[] entities = scene.GetEntities();
|
||||
List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>();
|
||||
|
||||
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<IPermissionsModule>();
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the user has permission to export an object group to an OAR.
|
||||
/// </summary>
|
||||
/// <param name="user">The user</param>
|
||||
/// <param name="objGroup">The object group</param>
|
||||
/// <param name="checkPermissions">Which permissions to check: "C" = Copy, "T" = Transfer</param>
|
||||
/// <param name="permissionsModule">The scene's permissions module</param>
|
||||
/// <returns>Whether the user is allowed to export the object to an OAR</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the control file.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the list of regions included in a multi-region OAR.
|
||||
/// </summary>
|
||||
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<uint, Scene> 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<IRegionCombinerModule>();
|
||||
|
||||
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<SceneObjectGroup> 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<ILandObject> 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<ITerrainModule>().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<IRegionSerialiserModule>();
|
||||
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<UUID> assetsFoundUuids, ICollection<UUID> 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);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Closes the archive and notifies that we're done.
|
||||
/// </summary>
|
||||
/// <param name="errorMessage">The error that occurred, or empty for success</param>
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Method called when all the necessary assets for an archive request have been received.
|
||||
/// </summary>
|
||||
public delegate void AssetsRequestCallback(
|
||||
ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids);
|
||||
|
||||
/// <summary>
|
||||
/// Execute the write of an archive once we have received all the necessary data
|
||||
/// </summary>
|
||||
public class ArchiveWriteRequestExecution
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
protected ITerrainModule m_terrainModule;
|
||||
protected IRegionSerialiserModule m_serialiser;
|
||||
protected List<SceneObjectGroup> m_sceneObjects;
|
||||
protected Scene m_scene;
|
||||
protected TarArchiveWriter m_archiveWriter;
|
||||
protected Guid m_requestId;
|
||||
protected Dictionary<string, object> m_options;
|
||||
|
||||
public ArchiveWriteRequestExecution(
|
||||
List<SceneObjectGroup> sceneObjects,
|
||||
ITerrainModule terrainModule,
|
||||
IRegionSerialiserModule serialiser,
|
||||
Scene scene,
|
||||
TarArchiveWriter archiveWriter,
|
||||
Guid requestId,
|
||||
Dictionary<string, object> 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<UUID> assetsFoundUuids, ICollection<UUID> 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<UUID> assetsFoundUuids, ICollection<UUID> 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
|
||||
List<ILandObject>landObjects = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Prepare to write out an archive.
|
||||
/// </summary>
|
||||
public class ArchiveWriteRequestPreparation
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
/// <summary>
|
||||
/// The minimum major version of OAR that we can write.
|
||||
/// </summary>
|
||||
public static int MIN_MAJOR_VERSION = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum major version of OAR that we can write.
|
||||
/// </summary>
|
||||
public static int MAX_MAJOR_VERSION = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether this archive will save assets. Default is true.
|
||||
/// </summary>
|
||||
public bool SaveAssets { get; set; }
|
||||
|
||||
protected ArchiverModule m_module;
|
||||
protected Scene m_scene;
|
||||
protected Stream m_saveStream;
|
||||
protected Guid m_requestId;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="module">Calling module</param>
|
||||
/// <param name="savePath">The path to which to save data.</param>
|
||||
/// <param name="requestId">The id associated with this request</param>
|
||||
/// <exception cref="System.IO.IOException">
|
||||
/// If there was a problem opening a stream for the file specified by the savePath
|
||||
/// </exception>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="module">Calling module</param>
|
||||
/// <param name="saveStream">The stream to which to save data.</param>
|
||||
/// <param name="requestId">The id associated with this request</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Archive the region requested.
|
||||
/// </summary>
|
||||
/// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception>
|
||||
public void ArchiveRegion(Dictionary<string, object> options)
|
||||
{
|
||||
if (options.ContainsKey("noassets") && (bool)options["noassets"])
|
||||
SaveAssets = false;
|
||||
|
||||
try
|
||||
{
|
||||
Dictionary<UUID, AssetType> assetUuids = new Dictionary<UUID, AssetType>();
|
||||
|
||||
EntityBase[] entities = m_scene.GetEntities();
|
||||
List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>();
|
||||
|
||||
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<ITerrainModule>(),
|
||||
m_scene.RequestModuleInterface<IRegionSerialiserModule>(),
|
||||
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<UUID>(), new List<UUID>());
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
m_saveStream.Close();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the user has permission to export an object group to an OAR.
|
||||
/// </summary>
|
||||
/// <param name="user">The user</param>
|
||||
/// <param name="objGroup">The object group</param>
|
||||
/// <param name="checkPermissions">Which permissions to check: "C" = Copy, "T" = Transfer</param>
|
||||
/// <returns>Whether the user is allowed to export the object to an OAR</returns>
|
||||
private bool CanUserArchiveObject(UUID user, SceneObjectGroup objGroup, string checkPermissions)
|
||||
{
|
||||
if (checkPermissions == null)
|
||||
return true;
|
||||
|
||||
IPermissionsModule module = m_scene.RequestModuleInterface<IPermissionsModule>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the control file for the most up to date archive
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string CreateControlFile(Dictionary<string, object> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,6 +146,7 @@ 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<string> mainParams = ops.Parse(cmdparams);
|
||||
|
||||
@@ -169,7 +170,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 +185,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||
|
||||
public void ArchiveRegion(Stream saveStream, Guid requestId, Dictionary<string, object> options)
|
||||
{
|
||||
new ArchiveWriteRequestPreparation(this, saveStream, requestId).ArchiveRegion(options);
|
||||
new ArchiveWriteRequest(Scene, saveStream, requestId).ArchiveRegion(options);
|
||||
}
|
||||
|
||||
public void DearchiveRegion(string loadPath)
|
||||
|
||||
@@ -46,6 +46,12 @@ namespace OpenSim.Region.CoreModules.World.Archiver
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
/// <summary>
|
||||
/// Method called when all the necessary assets for an archive request have been received.
|
||||
/// </summary>
|
||||
public delegate void AssetsRequestCallback(
|
||||
ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids);
|
||||
|
||||
enum RequestState
|
||||
{
|
||||
Initial,
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The regions included in an OAR file.
|
||||
/// </summary>
|
||||
public class DearchiveScenesInfo
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
/// <summary>
|
||||
/// One region in the archive.
|
||||
/// </summary>
|
||||
public class RegionInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The subdirectory in which the region is stored.
|
||||
/// </summary>
|
||||
public string Directory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The region's coordinates (relative to the South-West corner of the block).
|
||||
/// </summary>
|
||||
public Point Location { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The UUID of the original scene from which this archived region was saved.
|
||||
/// </summary>
|
||||
public string OriginalID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public Scene Scene { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this archive uses the multi-region format.
|
||||
/// </summary>
|
||||
public Boolean MultiRegionFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maps (Region directory -> region)
|
||||
/// </summary>
|
||||
protected Dictionary<string, RegionInfo> m_directory2region = new Dictionary<string, RegionInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Maps (UUID of the scene in the simulator where the region will be loaded -> region)
|
||||
/// </summary>
|
||||
protected Dictionary<UUID, RegionInfo> m_newId2region = new Dictionary<UUID, RegionInfo>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets all the scenes present in the simulator.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
/// <param name="rootScene">The scene where the Load OAR operation was run</param>
|
||||
/// <param name="simulatorScenes">All the scenes in the simulator</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="fullPath">The path of a file in the archive</param>
|
||||
/// <param name="scene">The corresponding Scene, or null if none</param>
|
||||
/// <param name="relativePath">The path relative to the region's directory. (Or the original
|
||||
/// path, if this file doesn't belong to a region.)</param>
|
||||
/// <returns>True: use this file; False: skip it</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="newID"></param>
|
||||
/// <returns></returns>
|
||||
public string GetOriginalRegionID(UUID newID)
|
||||
{
|
||||
RegionInfo region;
|
||||
if (m_newId2region.TryGetValue(newID, out region))
|
||||
return region.OriginalID;
|
||||
else
|
||||
return DefaultOriginalID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the scenes that have been (or will be) loaded.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<UUID> GetLoadedScenes()
|
||||
{
|
||||
return m_newId2region.Keys.ToList();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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<UUID> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test saving an OpenSim Region Archive with the no assets option
|
||||
/// </summary>
|
||||
@@ -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<string, Object>()));
|
||||
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<string, Object>()));
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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<string, Object>()));
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test saving a multi-region OAR.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSaveMultiRegionOar()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
|
||||
// Create test regions
|
||||
|
||||
int WIDTH = 2;
|
||||
int HEIGHT = 2;
|
||||
|
||||
List<Scene> scenes = new List<Scene>();
|
||||
|
||||
// Maps (Directory in OAR file -> scene)
|
||||
Dictionary<string, Scene> regionPaths = new Dictionary<string, Scene>();
|
||||
|
||||
// Maps (Scene -> expected object paths)
|
||||
Dictionary<UUID, List<string>> expectedPaths = new Dictionary<UUID, List<string>>();
|
||||
|
||||
// List of expected assets
|
||||
List<UUID> expectedAssets = new List<UUID>();
|
||||
|
||||
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<string>();
|
||||
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<string, Object> options = new Dictionary<string, Object>();
|
||||
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<UUID, List<string>> foundPaths = new Dictionary<UUID, List<string>>();
|
||||
List<UUID> foundAssets = new List<UUID>();
|
||||
|
||||
foreach (Scene scene in scenes)
|
||||
{
|
||||
foundPaths[scene.RegionInfo.RegionID] = new List<string>();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test loading a multi-region OAR.
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
/// <value>
|
||||
/// </value>
|
||||
private bool m_Tainted = true;
|
||||
|
||||
private Object m_TaintLock = new Object();
|
||||
|
||||
@@ -215,12 +215,6 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
||||
|
||||
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)
|
||||
@@ -229,6 +223,8 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
||||
sb.Append("\n");
|
||||
}
|
||||
|
||||
sb.AppendFormat("{0} objects found in {1}\n", sceneObjects.Count, m_scene.Name);
|
||||
|
||||
m_console.OutputFormat(sb.ToString());
|
||||
}
|
||||
|
||||
@@ -298,12 +294,6 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
||||
|
||||
m_scene.ForEachSOG(searchAction);
|
||||
|
||||
if (parts.Count == 0)
|
||||
{
|
||||
m_console.OutputFormat("No parts with name {0} found in {1}", name, m_scene.RegionInfo.RegionName);
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
foreach (SceneObjectPart part in parts)
|
||||
@@ -312,6 +302,8 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
||||
sb.Append("\n");
|
||||
}
|
||||
|
||||
sb.AppendFormat("{0} parts found in {1}\n", parts.Count, m_scene.Name);
|
||||
|
||||
m_console.OutputFormat(sb.ToString());
|
||||
}
|
||||
|
||||
|
||||
@@ -208,6 +208,10 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
|
||||
bitmap = ImageUtils.ResizeImage(origBitmap, viewport.Width, viewport.Height);
|
||||
}
|
||||
|
||||
// XXX: It shouldn't really be necesary to force a GC here as one should occur anyway pretty shortly
|
||||
// afterwards. It's generally regarded as a bad idea to manually GC. If Warp3D is using lots of memory
|
||||
// then this may be some issue with the Warp3D code itself, though it's also quite possible that generating
|
||||
// this map tile simply takes a lot of memory.
|
||||
GC.Collect();
|
||||
m_log.Debug("[WARP 3D IMAGE MODULE]: GC.Collect()");
|
||||
|
||||
|
||||
@@ -108,6 +108,15 @@ namespace OpenSim.Region.Framework.Interfaces
|
||||
/// <param name="objectLocalID"></param>
|
||||
void DetachSingleAttachmentToGround(IScenePresence sp, uint objectLocalID);
|
||||
|
||||
/// <summary>
|
||||
/// Detach the given item to the ground at the specified coordinates & rotation
|
||||
/// </summary>
|
||||
/// <param name="sp"></param>
|
||||
/// <param name="objectLocalID"></param>
|
||||
/// <param name="absolutePos"></param>
|
||||
/// <param name="absoluteRot"></param>
|
||||
void DetachSingleAttachmentToGround(IScenePresence sp, uint objectLocalID, Vector3 absolutePos, Quaternion absoluteRot);
|
||||
|
||||
/// <summary>
|
||||
/// Detach the given attachment so that it remains in the user's inventory.
|
||||
/// </summary>
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using OpenMetaverse;
|
||||
|
||||
@@ -33,7 +35,14 @@ namespace OpenSim.Region.Framework.Interfaces
|
||||
public interface IDynamicTextureManager
|
||||
{
|
||||
void RegisterRender(string handleType, IDynamicTextureRender render);
|
||||
void ReturnData(UUID id, byte[] data, bool isReuseable);
|
||||
|
||||
/// <summary>
|
||||
/// Used by IDynamicTextureRender implementations to return renders
|
||||
/// </summary>
|
||||
/// <param name='id'></param>
|
||||
/// <param name='data'></param>
|
||||
/// <param name='isReuseable'></param>
|
||||
void ReturnData(UUID id, IDynamicTexture texture);
|
||||
|
||||
UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url, string extraParams,
|
||||
int updateTimer);
|
||||
@@ -125,11 +134,53 @@ namespace OpenSim.Region.Framework.Interfaces
|
||||
// /// <param name='extraParams'></param>
|
||||
// bool AlwaysIdenticalConversion(string bodyData, string extraParams);
|
||||
|
||||
byte[] ConvertUrl(string url, string extraParams);
|
||||
byte[] ConvertData(string bodyData, string extraParams);
|
||||
IDynamicTexture ConvertUrl(string url, string extraParams);
|
||||
IDynamicTexture ConvertData(string bodyData, string extraParams);
|
||||
|
||||
bool AsyncConvertUrl(UUID id, string url, string extraParams);
|
||||
bool AsyncConvertData(UUID id, string bodyData, string extraParams);
|
||||
|
||||
void GetDrawStringSize(string text, string fontName, int fontSize,
|
||||
out double xSize, out double ySize);
|
||||
}
|
||||
|
||||
public interface IDynamicTexture
|
||||
{
|
||||
/// <summary>
|
||||
/// Input commands used to generate this data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Null if input commands were not used.
|
||||
/// </remarks>
|
||||
string InputCommands { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Uri used to generate this data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Null if a uri was not used.
|
||||
/// </remarks>
|
||||
Uri InputUri { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Extra input params used to generate this data.
|
||||
/// </summary>
|
||||
string InputParams { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Texture data.
|
||||
/// </summary>
|
||||
byte[] Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of texture.
|
||||
/// </summary>
|
||||
Size Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Signal whether the texture is reuseable (i.e. whether the same input data will always generate the same
|
||||
/// texture).
|
||||
/// </summary>
|
||||
bool IsReuseable { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,11 @@ namespace OpenSim.Region.Framework.Interfaces
|
||||
/// </summary>
|
||||
void sendRegionHandshakeToAll();
|
||||
|
||||
/// <summary>
|
||||
/// Fires the OnRegionInfoChange event.
|
||||
/// </summary>
|
||||
void TriggerRegionInfoChange();
|
||||
|
||||
void setEstateTerrainBaseTexture(int level, UUID texture);
|
||||
void setEstateTerrainTextureHeights(int corner, float lowValue, float highValue);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace OpenSim.Region.Framework.Interfaces
|
||||
|
||||
public interface IJsonStoreModule
|
||||
{
|
||||
bool CreateStore(string value, out UUID result);
|
||||
bool CreateStore(string value, ref UUID result);
|
||||
bool DestroyStore(UUID storeID);
|
||||
bool TestPath(UUID storeID, string path, bool useJson);
|
||||
bool SetValue(UUID storeID, string path, string value, bool useJson);
|
||||
|
||||
@@ -74,6 +74,14 @@ namespace OpenSim.Region.Framework.Interfaces
|
||||
/// <param name="methods"></param>
|
||||
void RegisterScriptInvocation(Type target, string[] methods);
|
||||
|
||||
/// <summary>
|
||||
/// Automatically register script invocations by checking for methods
|
||||
/// with <see cref="ScriptInvocationAttribute"/>. Should only check
|
||||
/// public methods.
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
void RegisterScriptInvocations(IRegionModuleBase target);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of all registered script calls
|
||||
/// </summary>
|
||||
@@ -96,11 +104,43 @@ namespace OpenSim.Region.Framework.Interfaces
|
||||
/// <param name="key"></param>
|
||||
void DispatchReply(UUID scriptId, int code, string text, string key);
|
||||
|
||||
/// For constants
|
||||
/// <summary>
|
||||
/// Operation to for a region module to register a constant to be used
|
||||
/// by the script engine
|
||||
/// </summary>
|
||||
/// <param name="cname">
|
||||
/// The name of the constant. LSL convention is for constant names to
|
||||
/// be uppercase.
|
||||
/// </param>
|
||||
/// <param name="value">
|
||||
/// The value of the constant. Should be of a type that can be
|
||||
/// converted to one of <see cref="OpenSim.Region.ScriptEngine.Shared.LSL_Types"/>
|
||||
/// </param>
|
||||
void RegisterConstant(string cname, object value);
|
||||
|
||||
/// <summary>
|
||||
/// Automatically register all constants on a region module by
|
||||
/// checking for fields with <see cref="ScriptConstantAttribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
void RegisterConstants(IRegionModuleBase target);
|
||||
|
||||
/// <summary>
|
||||
/// Operation to check for a registered constant
|
||||
/// </summary>
|
||||
/// <param name="cname">Name of constant</param>
|
||||
/// <returns>Value of constant or null if none found.</returns>
|
||||
object LookupModConstant(string cname);
|
||||
|
||||
// For use ONLY by the script API
|
||||
void RaiseEvent(UUID script, string id, string module, string command, string key);
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class ScriptInvocationAttribute : Attribute
|
||||
{ }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class ScriptConstantAttribute : Attribute
|
||||
{ }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,31 @@
|
||||
using System;
|
||||
/*
|
||||
* 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 OpenMetaverse;
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace OpenSim.Region.Framework.Scenes.Animation
|
||||
{
|
||||
if (m_defaultAnimation.AnimID == animID)
|
||||
{
|
||||
ResetDefaultAnimation();
|
||||
m_defaultAnimation = new OpenSim.Framework.Animation(UUID.Zero, 1, UUID.Zero);
|
||||
}
|
||||
else if (HasAnimation(animID))
|
||||
{
|
||||
@@ -149,19 +149,26 @@ namespace OpenSim.Region.Framework.Scenes.Animation
|
||||
{
|
||||
lock (m_animations)
|
||||
{
|
||||
animIDs = new UUID[m_animations.Count + 1];
|
||||
sequenceNums = new int[m_animations.Count + 1];
|
||||
objectIDs = new UUID[m_animations.Count + 1];
|
||||
int defaultSize = 0;
|
||||
if (m_defaultAnimation.AnimID != UUID.Zero)
|
||||
defaultSize++;
|
||||
|
||||
animIDs[0] = m_defaultAnimation.AnimID;
|
||||
sequenceNums[0] = m_defaultAnimation.SequenceNum;
|
||||
objectIDs[0] = m_defaultAnimation.ObjectID;
|
||||
animIDs = new UUID[m_animations.Count + defaultSize];
|
||||
sequenceNums = new int[m_animations.Count + defaultSize];
|
||||
objectIDs = new UUID[m_animations.Count + defaultSize];
|
||||
|
||||
if (m_defaultAnimation.AnimID != UUID.Zero)
|
||||
{
|
||||
animIDs[0] = m_defaultAnimation.AnimID;
|
||||
sequenceNums[0] = m_defaultAnimation.SequenceNum;
|
||||
objectIDs[0] = m_defaultAnimation.ObjectID;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_animations.Count; ++i)
|
||||
{
|
||||
animIDs[i + 1] = m_animations[i].AnimID;
|
||||
sequenceNums[i + 1] = m_animations[i].SequenceNum;
|
||||
objectIDs[i + 1] = m_animations[i].ObjectID;
|
||||
animIDs[i + defaultSize] = m_animations[i].AnimID;
|
||||
sequenceNums[i + defaultSize] = m_animations[i].SequenceNum;
|
||||
objectIDs[i + defaultSize] = m_animations[i].ObjectID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,13 +408,19 @@ namespace OpenSim.Region.Framework.Scenes.Animation
|
||||
{
|
||||
lock (m_animations)
|
||||
{
|
||||
CurrentMovementAnimation = DetermineMovementAnimation();
|
||||
string newMovementAnimation = DetermineMovementAnimation();
|
||||
if (CurrentMovementAnimation != newMovementAnimation)
|
||||
{
|
||||
CurrentMovementAnimation = DetermineMovementAnimation();
|
||||
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE PRESENCE ANIMATOR]: Determined animation {0} for {1} in UpdateMovementAnimations()",
|
||||
// CurrentMovementAnimation, m_scenePresence.Name);
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE PRESENCE ANIMATOR]: Determined animation {0} for {1} in UpdateMovementAnimations()",
|
||||
// CurrentMovementAnimation, m_scenePresence.Name);
|
||||
|
||||
TrySetMovementAnimation(CurrentMovementAnimation);
|
||||
// Only set it if it's actually changed, give a script
|
||||
// a chance to stop a default animation
|
||||
TrySetMovementAnimation(CurrentMovementAnimation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,4 +542,4 @@ namespace OpenSim.Region.Framework.Scenes.Animation
|
||||
SendAnimPack(animIDs, sequenceNums, objectIDs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,26 +47,71 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
|
||||
public delegate void OnFrameDelegate();
|
||||
|
||||
/// <summary>
|
||||
/// Triggered on each sim frame.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This gets triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.Update"/>
|
||||
/// Core uses it for things like Sun, Wind & Clouds
|
||||
/// The MRM module also uses it.
|
||||
/// </remarks>
|
||||
public event OnFrameDelegate OnFrame;
|
||||
|
||||
public delegate void ClientMovement(ScenePresence client);
|
||||
|
||||
/// <summary>
|
||||
/// Trigerred when an agent moves.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This gets triggered in <see cref="OpenSim.Region.Framework.Scenes.ScenePresence.HandleAgentUpdate"/>
|
||||
/// prior to <see cref="OpenSim.Region.Framework.Scenes.ScenePresence.TriggerScenePresenceUpdated"/>
|
||||
/// </remarks>
|
||||
public event ClientMovement OnClientMovement;
|
||||
|
||||
public delegate void OnTerrainTaintedDelegate();
|
||||
|
||||
/// <summary>
|
||||
/// Triggered if the terrain has been edited
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This gets triggered in <see cref="OpenSim.Region.CoreModules.World.Terrain.CheckForTerrainUpdates"/>
|
||||
/// after it determines that an update has been made.
|
||||
/// </remarks>
|
||||
public event OnTerrainTaintedDelegate OnTerrainTainted;
|
||||
|
||||
public delegate void OnTerrainTickDelegate();
|
||||
|
||||
/// <summary>
|
||||
/// Triggered if the terrain has been edited
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This gets triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.UpdateTerrain"/>
|
||||
/// but is used by core solely to update the physics engine.
|
||||
/// </remarks>
|
||||
public event OnTerrainTickDelegate OnTerrainTick;
|
||||
|
||||
public delegate void OnBackupDelegate(ISimulationDataService datastore, bool forceBackup);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a region is backed up/persisted to storage
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This gets triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.Backup"/>
|
||||
/// and is fired before the persistence occurs.
|
||||
/// </remarks>
|
||||
public event OnBackupDelegate OnBackup;
|
||||
|
||||
public delegate void OnClientConnectCoreDelegate(IClientCore client);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a new client connects to the scene.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This gets triggered in <see cref="TriggerOnNewClient"/>,
|
||||
/// which checks if an instance of <see cref="OpenSim.Framework.IClientAPI"/>
|
||||
/// also implements <see cref="OpenSim.Framework.Client.IClientCore"/> and as such,
|
||||
/// is not triggered by <see cref="OpenSim.Region.OptionalModules.World.NPC">NPCs</see>.
|
||||
/// </remarks>
|
||||
public event OnClientConnectCoreDelegate OnClientConnect;
|
||||
|
||||
public delegate void OnNewClientDelegate(IClientAPI client);
|
||||
@@ -87,22 +132,74 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
|
||||
public delegate void OnNewPresenceDelegate(ScenePresence presence);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a new presence is added to the scene
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.AddNewClient"/> which is used by both
|
||||
/// <see cref="OpenSim.Framework.PresenceType.User">users</see> and <see cref="OpenSim.Framework.PresenceType.Npc">NPCs</see>
|
||||
/// </remarks>
|
||||
public event OnNewPresenceDelegate OnNewPresence;
|
||||
|
||||
public delegate void OnRemovePresenceDelegate(UUID agentId);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a presence is removed from the scene
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.AddNewClient"/> which is used by both
|
||||
/// <see cref="OpenSim.Framework.PresenceType.User">users</see> and <see cref="OpenSim.Framework.PresenceType.Npc">NPCs</see>
|
||||
/// </remarks>
|
||||
public event OnRemovePresenceDelegate OnRemovePresence;
|
||||
|
||||
public delegate void OnParcelPrimCountUpdateDelegate();
|
||||
|
||||
/// <summary>
|
||||
/// Triggered whenever the prim count may have been altered, or prior
|
||||
/// to an action that requires the current prim count to be accurate.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerParcelPrimCountUpdate"/> in
|
||||
/// <see cref="OpenSim.OpenSimBase.CreateRegion"/>,
|
||||
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandManagementModule.EventManagerOnRequestParcelPrimCountUpdate"/>,
|
||||
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandManagementModule.ClientOnParcelObjectOwnerRequest"/>,
|
||||
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandObject.GetPrimsFree"/>,
|
||||
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandObject.UpdateLandSold"/>,
|
||||
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandObject.DeedToGroup"/>,
|
||||
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandObject.SendLandUpdateToClient"/>
|
||||
/// </remarks>
|
||||
public event OnParcelPrimCountUpdateDelegate OnParcelPrimCountUpdate;
|
||||
|
||||
public delegate void OnParcelPrimCountAddDelegate(SceneObjectGroup obj);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered in response to <see cref="OnParcelPrimCountUpdate"/> for
|
||||
/// objects that actually contribute to parcel prim count.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerParcelPrimCountAdd"/> in
|
||||
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandManagementModule.EventManagerOnParcelPrimCountUpdate"/>
|
||||
/// </remarks>
|
||||
public event OnParcelPrimCountAddDelegate OnParcelPrimCountAdd;
|
||||
|
||||
public delegate void OnPluginConsoleDelegate(string[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered after <see cref="OpenSim.IApplicationPlugin.PostInitialise"/>
|
||||
/// has been called for all <see cref="OpenSim.IApplicationPlugin"/>
|
||||
/// loaded via <see cref="OpenSim.OpenSimBase.LoadPlugins"/>.
|
||||
/// Handlers for this event are typically used to parse the arguments
|
||||
/// from <see cref="OnPluginConsoleDelegate"/> in order to process or
|
||||
/// filter the arguments and pass them onto <see cref="OpenSim.Region.CoreModules.Framework.InterfaceCommander.Commander.ProcessConsoleCommand"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerOnPluginConsole"/> in
|
||||
/// <see cref="Scene.SendCommandToPlugins"/> via
|
||||
/// <see cref="SceneManager.SendCommandToPluginModules"/> via
|
||||
/// <see cref="OpenSim.OpenSimBase.HandleCommanderCommand"/> via
|
||||
/// <see cref="OpenSim.OpenSimBase.AddPluginCommands"/> via
|
||||
/// <see cref="OpenSim.OpenSimBase.StartupSpecific"/>
|
||||
/// </remarks>
|
||||
public event OnPluginConsoleDelegate OnPluginConsole;
|
||||
|
||||
/// <summary>
|
||||
@@ -117,8 +214,28 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
|
||||
public delegate void OnSetRootAgentSceneDelegate(UUID agentID, Scene scene);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered before the grunt work for adding a root agent to a
|
||||
/// scene has been performed (resuming attachment scripts, physics,
|
||||
/// animations etc.)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered before <see cref="OnMakeRootAgent"/>
|
||||
/// by <see cref="TriggerSetRootAgentScene"/>
|
||||
/// in <see cref="ScenePresence.MakeRootAgent"/>
|
||||
/// via <see cref="Scene.AgentCrossing"/>
|
||||
/// and <see cref="ScenePresence.CompleteMovement"/>
|
||||
/// </remarks>
|
||||
public event OnSetRootAgentSceneDelegate OnSetRootAgentScene;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered after parcel properties have been updated.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerOnParcelPropertiesUpdateRequest"/> in
|
||||
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandManagementModule.ClientOnParcelPropertiesUpdateRequest"/>,
|
||||
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandManagementModule.ProcessPropertiesUpdate"/>
|
||||
/// </remarks>
|
||||
public event ParcelPropertiesUpdateRequest OnParcelPropertiesUpdateRequest;
|
||||
|
||||
/// <summary>
|
||||
@@ -133,13 +250,45 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
/// <summary>
|
||||
/// Fired when an object is touched/grabbed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The originalID is the local ID of the part that was actually touched. The localID itself is always that of
|
||||
/// the root part.
|
||||
/// Triggerd in response to <see cref="OpenSim.Framework.IClientAPI.OnGrabObject"/>
|
||||
/// via <see cref="TriggerObjectGrab"/>
|
||||
/// in <see cref="Scene.ProcessObjectGrab"/>
|
||||
/// </remarks>
|
||||
public event ObjectGrabDelegate OnObjectGrab;
|
||||
public delegate void ObjectGrabDelegate(uint localID, uint originalID, Vector3 offsetPos, IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object is being touched/grabbed continuously.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered in response to <see cref="OpenSim.Framework.IClientAPI.OnGrabUpdate"/>
|
||||
/// via <see cref="TriggerObjectGrabbing"/>
|
||||
/// in <see cref="Scene.ProcessObjectGrabUpdate"/>
|
||||
/// </remarks>
|
||||
public event ObjectGrabDelegate OnObjectGrabbing;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object stops being touched/grabbed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered in response to <see cref="OpenSim.Framework.IClientAPI.OnDeGrabObject"/>
|
||||
/// via <see cref="TriggerObjectDeGrab"/>
|
||||
/// in <see cref="Scene.ProcessObjectDeGrab"/>
|
||||
/// </remarks>
|
||||
public event ObjectDeGrabDelegate OnObjectDeGrab;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a script resets.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerScriptReset"/>
|
||||
/// in <see cref="Scene.ProcessScriptReset"/>
|
||||
/// via <see cref="OpenSim.Framework.IClientAPI.OnScriptReset"/>
|
||||
/// via <see cref="OpenSim.Region.ClientStack.LindenUDP.LLClientView.HandleScriptReset"/>
|
||||
/// </remarks>
|
||||
public event ScriptResetDelegate OnScriptReset;
|
||||
|
||||
public event OnPermissionErrorDelegate OnPermissionError;
|
||||
@@ -149,29 +298,105 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Occurs after OnNewScript.
|
||||
/// Triggered by <see cref="TriggerRezScript"/>
|
||||
/// in <see cref="SceneObjectPartInventory.CreateScriptInstance"/>
|
||||
/// </remarks>
|
||||
public event NewRezScript OnRezScript;
|
||||
public delegate void NewRezScript(uint localID, UUID itemID, string script, int startParam, bool postOnRez, string engine, int stateSource);
|
||||
|
||||
public delegate void RemoveScript(uint localID, UUID itemID);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a script is removed from an object.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerRemoveScript"/>
|
||||
/// in <see cref="Scene.RemoveTaskInventory"/>,
|
||||
/// <see cref="Scene.CreateAgentInventoryItemFromTask"/>,
|
||||
/// <see cref="SceneObjectPartInventory.RemoveScriptInstance"/>,
|
||||
/// <see cref="SceneObjectPartInventory.RemoveInventoryItem"/>
|
||||
/// </remarks>
|
||||
public event RemoveScript OnRemoveScript;
|
||||
|
||||
public delegate void StartScript(uint localID, UUID itemID);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a script starts.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerStartScript"/>
|
||||
/// in <see cref="Scene.SetScriptRunning"/>
|
||||
/// via <see cref="OpenSim.Framework.IClientAPI.OnSetScriptRunning"/>,
|
||||
/// via <see cref="OpenSim.Region.ClientStack.LindenUDP.HandleSetScriptRunning"/>
|
||||
/// </remarks>
|
||||
public event StartScript OnStartScript;
|
||||
|
||||
public delegate void StopScript(uint localID, UUID itemID);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a script stops.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerStopScript"/>,
|
||||
/// in <see cref="SceneObjectPartInventory.CreateScriptInstance"/>,
|
||||
/// <see cref="SceneObjectPartInventory.StopScriptInstance"/>,
|
||||
/// <see cref="Scene.SetScriptRunning"/>
|
||||
/// </remarks>
|
||||
public event StopScript OnStopScript;
|
||||
|
||||
public delegate bool SceneGroupMoved(UUID groupID, Vector3 delta);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object is moved.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerGroupMove"/>
|
||||
/// in <see cref="SceneObjectGroup.UpdateGroupPosition"/>,
|
||||
/// <see cref="SceneObjectGroup.GrabMovement"/>
|
||||
/// </remarks>
|
||||
public event SceneGroupMoved OnSceneGroupMove;
|
||||
|
||||
public delegate void SceneGroupGrabed(UUID groupID, Vector3 offset, UUID userID);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object is grabbed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerGroupGrab"/>
|
||||
/// in <see cref="SceneObjectGroup.OnGrabGroup"/>
|
||||
/// via <see cref="SceneObjectGroup.ObjectGrabHandler"/>
|
||||
/// via <see cref="Scene.ProcessObjectGrab"/>
|
||||
/// via <see cref="OpenSim.Framework.IClientAPI.OnGrabObject"/>
|
||||
/// via <see cref="OpenSim.Region.ClientStack.LindenUDP.LLClientView.HandleObjectGrab"/>
|
||||
/// </remarks>
|
||||
public event SceneGroupGrabed OnSceneGroupGrab;
|
||||
|
||||
public delegate bool SceneGroupSpinStarted(UUID groupID);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object starts to spin.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerGroupSpinStart"/>
|
||||
/// in <see cref="SceneObjectGroup.SpinStart"/>
|
||||
/// via <see cref="SceneGraph.SpinStart"/>
|
||||
/// via <see cref="OpenSim.Framework.IClientAPI.OnSpinStart"/>
|
||||
/// via <see cref="OpenSim.Region.ClientStack.LindenUDP.LLClientView.HandleObjectSpinStart"/>
|
||||
/// </remarks>
|
||||
public event SceneGroupSpinStarted OnSceneGroupSpinStart;
|
||||
|
||||
public delegate bool SceneGroupSpun(UUID groupID, Quaternion rotation);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object is being spun.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerGroupSpin"/>
|
||||
/// in <see cref="SceneObjectGroup.SpinMovement"/>
|
||||
/// via <see cref="SceneGraph.SpinObject"/>
|
||||
/// via <see cref="OpenSim.Framework.IClientAPI.OnSpinUpdate"/>
|
||||
/// via <see cref="OpenSim.Region.ClientStack.LindenUDP.LLClientView.HandleObjectSpinUpdate"/>
|
||||
/// </remarks>
|
||||
public event SceneGroupSpun OnSceneGroupSpin;
|
||||
|
||||
public delegate void LandObjectAdded(ILandObject newParcel);
|
||||
@@ -210,6 +435,9 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Occurs before OnRezScript
|
||||
/// Triggered by <see cref="TriggerNewScript"/>
|
||||
/// in <see cref="Scene.RezScriptFromAgentInventory"/>,
|
||||
/// <see cref="Scene.RezNewScript"/>
|
||||
/// </remarks>
|
||||
public event NewScript OnNewScript;
|
||||
|
||||
@@ -244,6 +472,12 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered after the scene receives a client's upload of an updated script and has stored it in an asset.
|
||||
/// Triggered by <see cref="TriggerUpdateScript"/>
|
||||
/// in <see cref="Scene.CapsUpdateTaskInventoryScriptAsset"/>
|
||||
/// via <see cref="Scene.CapsUpdateTaskInventoryScriptAsset"/>
|
||||
/// via <see cref="OpenSim.Region.ClientStack.Linden.BunchOfCaps.TaskScriptUpdated"/>
|
||||
/// via <see cref="OpenSim.Region.ClientStack.Linden.TaskInventoryScriptUpdater.OnUpLoad"/>
|
||||
/// via <see cref="OpenSim.Region.ClientStack.Linden.TaskInventoryScriptUpdater.uploaderCaps"/>
|
||||
/// </remarks>
|
||||
public event UpdateScript OnUpdateScript;
|
||||
|
||||
@@ -269,48 +503,203 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when some scene object properties change.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ScriptChangedEvent is fired when a scene object property that a script might be interested
|
||||
/// in (such as color, scale or inventory) changes. Only enough information sent is for the LSL changed event.
|
||||
/// This is not an indication that the script has changed (see OnUpdateScript for that).
|
||||
/// This event is sent to a script to tell it that some property changed on
|
||||
/// the object the script is in. See http://lslwiki.net/lslwiki/wakka.php?wakka=changed .
|
||||
/// </summary>
|
||||
/// Triggered by <see cref="TriggerOnScriptChangedEvent"/>
|
||||
/// in <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.EntityTransferModule.TeleportAgentWithinRegion"/>,
|
||||
/// <see cref="SceneObjectPart.TriggerScriptChangedEvent"/>
|
||||
/// </remarks>
|
||||
public event ScriptChangedEvent OnScriptChangedEvent;
|
||||
public delegate void ScriptChangedEvent(uint localID, uint change);
|
||||
|
||||
public delegate void ScriptControlEvent(UUID item, UUID avatarID, uint held, uint changed);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a script receives control input from an agent.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerControlEvent"/>
|
||||
/// in <see cref="ScenePresence.SendControlsToScripts"/>
|
||||
/// via <see cref="ScenePresence.HandleAgentUpdate"/>
|
||||
/// via <see cref="OpenSim.Framework.IClientAPI.OnAgentUpdate"/>
|
||||
/// via <see cref="OpenSim.Region.ClientStack.LindenUDP.LLClientView.HandleAgentUpdate"/>
|
||||
/// </remarks>
|
||||
public event ScriptControlEvent OnScriptControlEvent;
|
||||
|
||||
public delegate void ScriptAtTargetEvent(uint localID, uint handle, Vector3 targetpos, Vector3 atpos);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object has arrived within a tolerance distance
|
||||
/// of a motion target.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerAtTargetEvent"/>
|
||||
/// in <see cref="SceneObjectGroup.checkAtTargets"/>
|
||||
/// via <see cref="SceneObjectGroup.ScheduleGroupForFullUpdate"/>,
|
||||
/// <see cref="Scene.CheckAtTargets"/> via <see cref="Scene.Update"/>
|
||||
/// </remarks>
|
||||
public event ScriptAtTargetEvent OnScriptAtTargetEvent;
|
||||
|
||||
public delegate void ScriptNotAtTargetEvent(uint localID);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object has a motion target but has not arrived
|
||||
/// within a tolerance distance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerNotAtTargetEvent"/>
|
||||
/// in <see cref="SceneObjectGroup.checkAtTargets"/>
|
||||
/// via <see cref="SceneObjectGroup.ScheduleGroupForFullUpdate"/>,
|
||||
/// <see cref="Scene.CheckAtTargets"/> via <see cref="Scene.Update"/>
|
||||
/// </remarks>
|
||||
public event ScriptNotAtTargetEvent OnScriptNotAtTargetEvent;
|
||||
|
||||
public delegate void ScriptAtRotTargetEvent(uint localID, uint handle, Quaternion targetrot, Quaternion atrot);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object has arrived within a tolerance rotation
|
||||
/// of a rotation target.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerAtRotTargetEvent"/>
|
||||
/// in <see cref="SceneObjectGroup.checkAtTargets"/>
|
||||
/// via <see cref="SceneObjectGroup.ScheduleGroupForFullUpdate"/>,
|
||||
/// <see cref="Scene.CheckAtTargets"/> via <see cref="Scene.Update"/>
|
||||
/// </remarks>
|
||||
public event ScriptAtRotTargetEvent OnScriptAtRotTargetEvent;
|
||||
|
||||
public delegate void ScriptNotAtRotTargetEvent(uint localID);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object has a rotation target but has not arrived
|
||||
/// within a tolerance rotation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerNotAtRotTargetEvent"/>
|
||||
/// in <see cref="SceneObjectGroup.checkAtTargets"/>
|
||||
/// via <see cref="SceneObjectGroup.ScheduleGroupForFullUpdate"/>,
|
||||
/// <see cref="Scene.CheckAtTargets"/> via <see cref="Scene.Update"/>
|
||||
/// </remarks>
|
||||
public event ScriptNotAtRotTargetEvent OnScriptNotAtRotTargetEvent;
|
||||
|
||||
public delegate void ScriptColliding(uint localID, ColliderArgs colliders);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a physical collision has started between a prim
|
||||
/// and something other than the region terrain.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerScriptCollidingStart"/>
|
||||
/// in <see cref="SceneObjectPart.SendCollisionEvent"/>
|
||||
/// via <see cref="SceneObjectPart.PhysicsCollision"/>
|
||||
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.OnCollisionUpdate"/>
|
||||
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.SendCollisionUpdate"/>
|
||||
/// </remarks>
|
||||
public event ScriptColliding OnScriptColliderStart;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when something that previously collided with a prim has
|
||||
/// not stopped colliding with it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <seealso cref="OnScriptColliderStart"/>
|
||||
/// Triggered by <see cref="TriggerScriptColliding"/>
|
||||
/// in <see cref="SceneObjectPart.SendCollisionEvent"/>
|
||||
/// via <see cref="SceneObjectPart.PhysicsCollision"/>
|
||||
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.OnCollisionUpdate"/>
|
||||
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.SendCollisionUpdate"/>
|
||||
/// </remarks>
|
||||
public event ScriptColliding OnScriptColliding;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when something that previously collided with a prim has
|
||||
/// stopped colliding with it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerScriptCollidingEnd"/>
|
||||
/// in <see cref="SceneObjectPart.SendCollisionEvent"/>
|
||||
/// via <see cref="SceneObjectPart.PhysicsCollision"/>
|
||||
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.OnCollisionUpdate"/>
|
||||
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.SendCollisionUpdate"/>
|
||||
/// </remarks>
|
||||
public event ScriptColliding OnScriptCollidingEnd;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a physical collision has started between an object
|
||||
/// and the region terrain.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerScriptLandCollidingStart"/>
|
||||
/// in <see cref="SceneObjectPart.SendLandCollisionEvent"/>
|
||||
/// via <see cref="SceneObjectPart.PhysicsCollision"/>
|
||||
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.OnCollisionUpdate"/>
|
||||
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.SendCollisionUpdate"/>
|
||||
/// </remarks>
|
||||
public event ScriptColliding OnScriptLandColliderStart;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object that previously collided with the region
|
||||
/// terrain has not yet stopped colliding with it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerScriptLandColliding"/>
|
||||
/// in <see cref="SceneObjectPart.SendLandCollisionEvent"/>
|
||||
/// via <see cref="SceneObjectPart.PhysicsCollision"/>
|
||||
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.OnCollisionUpdate"/>
|
||||
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.SendCollisionUpdate"/>
|
||||
/// </remarks>
|
||||
public event ScriptColliding OnScriptLandColliding;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object that previously collided with the region
|
||||
/// terrain has stopped colliding with it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerScriptLandCollidingEnd"/>
|
||||
/// in <see cref="SceneObjectPart.SendLandCollisionEvent"/>
|
||||
/// via <see cref="SceneObjectPart.PhysicsCollision"/>
|
||||
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.OnCollisionUpdate"/>
|
||||
/// via <see cref="OpenSim.Region.Physics.Manager.PhysicsActor.SendCollisionUpdate"/>
|
||||
/// </remarks>
|
||||
public event ScriptColliding OnScriptLandColliderEnd;
|
||||
|
||||
public delegate void OnMakeChildAgentDelegate(ScenePresence presence);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an agent has been made a child agent of a scene.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerOnMakeChildAgent"/>
|
||||
/// in <see cref="ScenePresence.MakeChildAgent"/>
|
||||
/// via <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.EntityTransferModule.CrossAgentToNewRegionAsync"/>,
|
||||
/// <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.EntityTransferModule.DoTeleport"/>,
|
||||
/// <see cref="OpenSim.Region.CoreModules.InterGrid.KillAUser.ShutdownNoLogout"/>
|
||||
/// </remarks>
|
||||
public event OnMakeChildAgentDelegate OnMakeChildAgent;
|
||||
|
||||
public delegate void OnSaveNewWindlightProfileDelegate();
|
||||
public delegate void OnSendNewWindlightProfileTargetedDelegate(RegionLightShareData wl, UUID user);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered after the grunt work for adding a root agent to a
|
||||
/// scene has been performed (resuming attachment scripts, physics,
|
||||
/// animations etc.)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This event is on the critical path for transferring an avatar from one region to another. Try and do
|
||||
/// as little work on this event as possible, or do work asynchronously.
|
||||
/// </summary>
|
||||
/// Triggered after <see cref="OnSetRootAgentScene"/>
|
||||
/// by <see cref="TriggerOnMakeRootAgent"/>
|
||||
/// in <see cref="ScenePresence.MakeRootAgent"/>
|
||||
/// via <see cref="Scene.AgentCrossing"/>
|
||||
/// and <see cref="ScenePresence.CompleteMovement"/>
|
||||
/// </remarks>
|
||||
public event Action<ScenePresence> OnMakeRootAgent;
|
||||
|
||||
public event OnSendNewWindlightProfileTargetedDelegate OnSendNewWindlightProfileTargeted;
|
||||
@@ -336,9 +725,17 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
public event AvatarKillData OnAvatarKilled;
|
||||
public delegate void AvatarKillData(uint KillerLocalID, ScenePresence avatar);
|
||||
|
||||
// public delegate void ScriptTimerEvent(uint localID, double timerinterval);
|
||||
|
||||
// public event ScriptTimerEvent OnScriptTimerEvent;
|
||||
/*
|
||||
public delegate void ScriptTimerEvent(uint localID, double timerinterval);
|
||||
/// <summary>
|
||||
/// Used to be triggered when the LSL timer event fires.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerTimerEvent"/>
|
||||
/// via <see cref="SceneObjectPart.handleTimerAccounting"/>
|
||||
/// </remarks>
|
||||
public event ScriptTimerEvent OnScriptTimerEvent;
|
||||
*/
|
||||
|
||||
public delegate void EstateToolsSunUpdate(ulong regionHandle, bool FixedTime, bool EstateSun, float LindenHour);
|
||||
public delegate void GetScriptRunning(IClientAPI controllingClient, UUID objectID, UUID itemID);
|
||||
@@ -348,12 +745,27 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
/// <summary>
|
||||
/// Triggered when an object is added to the scene.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerObjectAddedToScene"/>
|
||||
/// in <see cref="Scene.AddNewSceneObject"/>,
|
||||
/// <see cref="Scene.DuplicateObject"/>,
|
||||
/// <see cref="Scene.doObjectDuplicateOnRay"/>
|
||||
/// </remarks>
|
||||
public event Action<SceneObjectGroup> OnObjectAddedToScene;
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for <see cref="OnObjectBeingRemovedFromScene"/>
|
||||
/// </summary>
|
||||
/// <param name="obj">The object being removed from the scene</param>
|
||||
public delegate void ObjectBeingRemovedFromScene(SceneObjectGroup obj);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an object is removed from the scene.
|
||||
/// </summary>
|
||||
public delegate void ObjectBeingRemovedFromScene(SceneObjectGroup obj);
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerObjectBeingRemovedFromScene"/>
|
||||
/// in <see cref="Scene.DeleteSceneObject"/>
|
||||
/// </remarks>
|
||||
public event ObjectBeingRemovedFromScene OnObjectBeingRemovedFromScene;
|
||||
|
||||
public delegate void NoticeNoLandDataFromStorage();
|
||||
@@ -369,6 +781,20 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
public event RequestParcelPrimCountUpdate OnRequestParcelPrimCountUpdate;
|
||||
|
||||
public delegate void ParcelPrimCountTainted();
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the parcel prim count has been altered.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerParcelPrimCountTainted"/> in
|
||||
/// <see cref="OpenSim.Region.CoreModules.Avatar.Attachments.AttachmentsModule.DetachSingleAttachmentToGround"/>,
|
||||
/// <see cref="OpenSim.Region.CoreModules.Avatar.Attachments.AttachmentsModule.AttachToAgent"/>,
|
||||
/// <see cref="Scene.DeleteSceneObject"/>,
|
||||
/// <see cref="Scene.SelectPrim"/>,
|
||||
/// <see cref="Scene.DeselectPrim"/>,
|
||||
/// <see cref="SceneObjectGroup.UpdatePrimFlags"/>,
|
||||
/// <see cref="SceneObjectGroup.AbsolutePosition"/>
|
||||
/// </remarks>
|
||||
public event ParcelPrimCountTainted OnParcelPrimCountTainted;
|
||||
public event GetScriptRunning OnGetScriptRunning;
|
||||
|
||||
@@ -428,7 +854,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
/// the scripts may not have started yet
|
||||
/// Message is non empty string if there were problems loading the oar file
|
||||
/// </summary>
|
||||
public delegate void OarFileLoaded(Guid guid, string message);
|
||||
public delegate void OarFileLoaded(Guid guid, List<UUID> loadedScenes, string message);
|
||||
public event OarFileLoaded OnOarFileLoaded;
|
||||
|
||||
/// <summary>
|
||||
@@ -481,6 +907,9 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
/// <param name="copy"></param>
|
||||
/// <param name="original"></param>
|
||||
/// <param name="userExposed">True if the duplicate will immediately be in the scene, false otherwise</param>
|
||||
/// <remarks>
|
||||
/// Triggered in <see cref="OpenSim.Region.Framework.Scenes.SceneObjectPart.Copy"/>
|
||||
/// </remarks>
|
||||
public event SceneObjectPartCopyDelegate OnSceneObjectPartCopy;
|
||||
public delegate void SceneObjectPartCopyDelegate(SceneObjectPart copy, SceneObjectPart original, bool userExposed);
|
||||
|
||||
@@ -522,9 +951,28 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
public event PrimsLoaded OnPrimsLoaded;
|
||||
|
||||
public delegate void TeleportStart(IClientAPI client, GridRegion destination, GridRegion finalDestination, uint teleportFlags, bool gridLogout);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a teleport starts
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerTeleportStart"/>
|
||||
/// in <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.EntityTransferModule.CreateAgent"/>
|
||||
/// and <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.HGEntityTransferModule.CreateAgent"/>
|
||||
/// via <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.EntityTransferModule.DoTeleport"/>
|
||||
/// </remarks>
|
||||
public event TeleportStart OnTeleportStart;
|
||||
|
||||
public delegate void TeleportFail(IClientAPI client, bool gridLogout);
|
||||
|
||||
/// <summary>
|
||||
/// Trigered when a teleport fails.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered by <see cref="TriggerTeleportFail"/>
|
||||
/// in <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.EntityTransferModule.Fail"/>
|
||||
/// via <see cref="OpenSim.Region.CoreModules.Framework.EntityTransfer.EntityTransferModule.DoTeleport"/>
|
||||
/// </remarks>
|
||||
public event TeleportFail OnTeleportFail;
|
||||
|
||||
public class MoneyTransferArgs : EventArgs
|
||||
@@ -532,7 +980,9 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
public UUID sender;
|
||||
public UUID receiver;
|
||||
|
||||
// Always false. The SL protocol sucks.
|
||||
/// <summary>
|
||||
/// Always false. The SL protocol sucks.
|
||||
/// </summary>
|
||||
public bool authenticated = false;
|
||||
|
||||
public int amount;
|
||||
@@ -589,8 +1039,29 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
|
||||
public delegate void LandBuy(Object sender, LandBuyArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an attempt to transfer grid currency occurs
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.ProcessMoneyTransferRequest"/>
|
||||
/// via <see cref="OpenSim.Region.Framework.Scenes.Scene.SubscribeToClientGridEvents"/>
|
||||
/// via <see cref="OpenSim.Region.Framework.Scenes.Scene.SubscribeToClientEvents"/>
|
||||
/// via <see cref="OpenSim.Region.Framework.Scenes.Scene.AddNewClient"/>
|
||||
/// </remarks>
|
||||
public event MoneyTransferEvent OnMoneyTransfer;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered after after <see cref="OnValidateLandBuy"/>
|
||||
/// </summary>
|
||||
public event LandBuy OnLandBuy;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered to allow or prevent a real estate transaction
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.ProcessParcelBuy"/>
|
||||
/// <seealso cref="OpenSim.Region.OptionalModules.World.MoneyModule.SampleMoneyModule.ValidateLandBuy"/>
|
||||
/// </remarks>
|
||||
public event LandBuy OnValidateLandBuy;
|
||||
|
||||
public void TriggerOnAttach(uint localID, UUID itemID, UUID avatarID)
|
||||
@@ -2006,7 +2477,11 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
}
|
||||
}
|
||||
|
||||
// this lets us keep track of nasty script events like timer, etc.
|
||||
/// <summary>
|
||||
/// this lets us keep track of nasty script events like timer, etc.
|
||||
/// </summary>
|
||||
/// <param name="objLocalID"></param>
|
||||
/// <param name="Interval"></param>
|
||||
public void TriggerTimerEvent(uint objLocalID, double Interval)
|
||||
{
|
||||
throw new NotImplementedException("TriggerTimerEvent was thought to be not used anymore and the registration for the event from scene object part has been commented out due to a memory leak");
|
||||
@@ -2068,7 +2543,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
return 6;
|
||||
}
|
||||
|
||||
public void TriggerOarFileLoaded(Guid requestId, string message)
|
||||
public void TriggerOarFileLoaded(Guid requestId, List<UUID> loadedScenes, string message)
|
||||
{
|
||||
OarFileLoaded handlerOarFileLoaded = OnOarFileLoaded;
|
||||
if (handlerOarFileLoaded != null)
|
||||
@@ -2077,7 +2552,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
{
|
||||
try
|
||||
{
|
||||
d(requestId, message);
|
||||
d(requestId, loadedScenes, message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
/// <summary>
|
||||
/// Minimum value of the size of a non-physical prim in each axis
|
||||
/// </summary>
|
||||
public float m_minNonphys = 0.01f;
|
||||
public float m_minNonphys = 0.001f;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum value of the size of a non-physical prim in each axis
|
||||
@@ -123,6 +123,11 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
/// </summary>
|
||||
public float m_maxPhys = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Max prims an object will hold
|
||||
/// </summary>
|
||||
public int m_linksetCapacity = 0;
|
||||
|
||||
public bool m_clampPrimSize;
|
||||
public bool m_trustBinaries;
|
||||
public bool m_allowScriptCrossings;
|
||||
@@ -146,7 +151,8 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
// TODO: need to figure out how allow client agents but deny
|
||||
// root agents when ACL denies access to root agent
|
||||
public bool m_strictAccessControl = true;
|
||||
public int MaxUndoCount = 5;
|
||||
|
||||
public int MaxUndoCount { get; set; }
|
||||
|
||||
// Using this for RegionReady module to prevent LoginsDisabled from changing under our feet;
|
||||
public bool LoginLock = false;
|
||||
@@ -736,16 +742,18 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
//Animation states
|
||||
m_useFlySlow = startupConfig.GetBoolean("enableflyslow", false);
|
||||
|
||||
MaxUndoCount = startupConfig.GetInt("MaxPrimUndos", 20);
|
||||
|
||||
PhysicalPrims = startupConfig.GetBoolean("physical_prim", PhysicalPrims);
|
||||
CollidablePrims = startupConfig.GetBoolean("collidable_prim", CollidablePrims);
|
||||
|
||||
m_minNonphys = startupConfig.GetFloat("NonphysicalPrimMin", m_minNonphys);
|
||||
m_minNonphys = startupConfig.GetFloat("NonPhysicalPrimMin", m_minNonphys);
|
||||
if (RegionInfo.NonphysPrimMin > 0)
|
||||
{
|
||||
m_minNonphys = RegionInfo.NonphysPrimMin;
|
||||
}
|
||||
|
||||
m_maxNonphys = startupConfig.GetFloat("NonphysicalPrimMax", m_maxNonphys);
|
||||
m_maxNonphys = startupConfig.GetFloat("NonPhysicalPrimMax", m_maxNonphys);
|
||||
if (RegionInfo.NonphysPrimMax > 0)
|
||||
{
|
||||
m_maxNonphys = RegionInfo.NonphysPrimMax;
|
||||
@@ -772,6 +780,12 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
m_clampPrimSize = true;
|
||||
}
|
||||
|
||||
m_linksetCapacity = startupConfig.GetInt("LinksetPrims", m_linksetCapacity);
|
||||
if (RegionInfo.LinksetCapacity > 0)
|
||||
{
|
||||
m_linksetCapacity = RegionInfo.LinksetCapacity;
|
||||
}
|
||||
|
||||
m_useTrashOnDelete = startupConfig.GetBoolean("UseTrashOnDelete", m_useTrashOnDelete);
|
||||
m_trustBinaries = startupConfig.GetBoolean("TrustBinaries", m_trustBinaries);
|
||||
m_allowScriptCrossings = startupConfig.GetBoolean("AllowScriptCrossing", m_allowScriptCrossings);
|
||||
@@ -787,13 +801,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
SpawnPointRouting = startupConfig.GetString("SpawnPointRouting", "closest");
|
||||
TelehubAllowLandmarks = startupConfig.GetBoolean("TelehubAllowLandmark", false);
|
||||
|
||||
IConfig packetConfig = m_config.Configs["PacketPool"];
|
||||
if (packetConfig != null)
|
||||
{
|
||||
PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true);
|
||||
PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true);
|
||||
}
|
||||
|
||||
m_strictAccessControl = startupConfig.GetBoolean("StrictAccessControl", m_strictAccessControl);
|
||||
|
||||
m_generateMaptiles = startupConfig.GetBoolean("GenerateMaptiles", true);
|
||||
@@ -850,6 +857,8 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
}
|
||||
|
||||
// FIXME: Ultimately this should be in a module.
|
||||
SendPeriodicAppearanceUpdates = true;
|
||||
|
||||
IConfig appearanceConfig = m_config.Configs["Appearance"];
|
||||
if (appearanceConfig != null)
|
||||
{
|
||||
@@ -4334,6 +4343,16 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
return LandChannel.GetLandObject(x, y).LandData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get LandData by position.
|
||||
/// </summary>
|
||||
/// <param name="pos"></param>
|
||||
/// <returns></returns>
|
||||
public LandData GetLandData(Vector3 pos)
|
||||
{
|
||||
return GetLandData(pos.X, pos.Y);
|
||||
}
|
||||
|
||||
public LandData GetLandData(uint x, uint y)
|
||||
{
|
||||
m_log.DebugFormat("[SCENE]: returning land for {0},{1}", x, y);
|
||||
@@ -4581,6 +4600,18 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
return m_sceneGraph.GetSceneObjectGroup(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to get the SOG via its UUID
|
||||
/// </summary>
|
||||
/// <param name="fullID"></param>
|
||||
/// <param name="sog"></param>
|
||||
/// <returns></returns>
|
||||
public bool TryGetSceneObjectGroup(UUID fullID, out SceneObjectGroup sog)
|
||||
{
|
||||
sog = GetSceneObjectGroup(fullID);
|
||||
return sog != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a prim by name from the scene (will return the first
|
||||
/// found, if there are more than one prim with the same name)
|
||||
@@ -4612,6 +4643,18 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
return m_sceneGraph.GetSceneObjectPart(fullID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to get a prim via its UUID
|
||||
/// </summary>
|
||||
/// <param name="fullID"></param>
|
||||
/// <param name="sop"></param>
|
||||
/// <returns></returns>
|
||||
public bool TryGetSceneObjectPart(UUID fullID, out SceneObjectPart sop)
|
||||
{
|
||||
sop = GetSceneObjectPart(fullID);
|
||||
return sop != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a scene object group that contains the prim with the given local id
|
||||
/// </summary>
|
||||
@@ -5482,6 +5525,9 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
|
||||
public string GetExtraSetting(string name)
|
||||
{
|
||||
if (m_extraSettings == null)
|
||||
return String.Empty;
|
||||
|
||||
string val;
|
||||
|
||||
if (!m_extraSettings.TryGetValue(name, out val))
|
||||
@@ -5492,6 +5538,9 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
|
||||
public void StoreExtraSetting(string name, string val)
|
||||
{
|
||||
if (m_extraSettings == null)
|
||||
return;
|
||||
|
||||
string oldVal;
|
||||
|
||||
if (m_extraSettings.TryGetValue(name, out oldVal))
|
||||
@@ -5509,6 +5558,9 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
|
||||
public void RemoveExtraSetting(string name)
|
||||
{
|
||||
if (m_extraSettings == null)
|
||||
return;
|
||||
|
||||
if (!m_extraSettings.ContainsKey(name))
|
||||
return;
|
||||
|
||||
|
||||
@@ -92,7 +92,11 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
private static SceneManager m_instance = null;
|
||||
public static SceneManager Instance
|
||||
{
|
||||
get { return m_instance; }
|
||||
get {
|
||||
if (m_instance == null)
|
||||
m_instance = new SceneManager();
|
||||
return m_instance;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<Scene> m_localScenes = new List<Scene>();
|
||||
|
||||
@@ -1119,14 +1119,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
parts[i].UUID = UUID.Random();
|
||||
}
|
||||
|
||||
// helper provided for parts.
|
||||
public int GetSceneMaxUndo()
|
||||
{
|
||||
if (m_scene != null)
|
||||
return m_scene.MaxUndoCount;
|
||||
return 5;
|
||||
}
|
||||
|
||||
// justincc: I don't believe this hack is needed any longer, especially since the physics
|
||||
// parts of set AbsolutePosition were already commented out. By changing HasGroupChanged to false
|
||||
// this method was preventing proper reload of scene objects.
|
||||
@@ -2014,6 +2006,25 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
if (objectGroup == this)
|
||||
return;
|
||||
|
||||
// If the configured linkset capacity is greater than zero,
|
||||
// and the new linkset would have a prim count higher than this
|
||||
// value, do not link it.
|
||||
if (m_scene.m_linksetCapacity > 0 &&
|
||||
(PrimCount + objectGroup.PrimCount) >
|
||||
m_scene.m_linksetCapacity)
|
||||
{
|
||||
m_log.DebugFormat(
|
||||
"[SCENE OBJECT GROUP]: Cannot link group with root" +
|
||||
" part {0}, {1} ({2} prims) to group with root part" +
|
||||
" {3}, {4} ({5} prims) because the new linkset" +
|
||||
" would exceed the configured maximum of {6}",
|
||||
objectGroup.RootPart.Name, objectGroup.RootPart.UUID,
|
||||
objectGroup.PrimCount, RootPart.Name, RootPart.UUID,
|
||||
PrimCount, m_scene.m_linksetCapacity);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 'linkPart' == the root of the group being linked into this group
|
||||
SceneObjectPart linkPart = objectGroup.m_rootPart;
|
||||
|
||||
@@ -2684,29 +2695,32 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE OBJECT GROUP]: Group resizing {0} {1} from {2} to {3}", Name, LocalId, RootPart.Scale, scale);
|
||||
|
||||
RootPart.StoreUndoState(true);
|
||||
|
||||
scale.X = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.X));
|
||||
scale.Y = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.Y));
|
||||
scale.Z = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.Z));
|
||||
|
||||
PhysicsActor pa = m_rootPart.PhysActor;
|
||||
|
||||
if (pa != null && pa.IsPhysical)
|
||||
RootPart.StoreUndoState(true);
|
||||
|
||||
if (Scene != null)
|
||||
{
|
||||
scale.X = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.X));
|
||||
scale.Y = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.Y));
|
||||
scale.Z = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.Z));
|
||||
scale.X = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.X));
|
||||
scale.Y = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.Y));
|
||||
scale.Z = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.Z));
|
||||
|
||||
if (pa != null && pa.IsPhysical)
|
||||
{
|
||||
scale.X = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.X));
|
||||
scale.Y = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.Y));
|
||||
scale.Z = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.Z));
|
||||
}
|
||||
}
|
||||
|
||||
float x = (scale.X / RootPart.Scale.X);
|
||||
float y = (scale.Y / RootPart.Scale.Y);
|
||||
float z = (scale.Z / RootPart.Scale.Z);
|
||||
|
||||
SceneObjectPart[] parts;
|
||||
if (x > 1.0f || y > 1.0f || z > 1.0f)
|
||||
SceneObjectPart[] parts = m_parts.GetArray();
|
||||
|
||||
if (Scene != null & (x > 1.0f || y > 1.0f || z > 1.0f))
|
||||
{
|
||||
parts = m_parts.GetArray();
|
||||
for (int i = 0; i < parts.Length; i++)
|
||||
{
|
||||
SceneObjectPart obPart = parts[i];
|
||||
@@ -2720,7 +2734,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
|
||||
if (pa != null && pa.IsPhysical)
|
||||
{
|
||||
if (oldSize.X * x > m_scene.m_maxPhys)
|
||||
if (oldSize.X * x > Scene.m_maxPhys)
|
||||
{
|
||||
f = m_scene.m_maxPhys / oldSize.X;
|
||||
a = f / x;
|
||||
@@ -2728,7 +2742,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
y *= a;
|
||||
z *= a;
|
||||
}
|
||||
else if (oldSize.X * x < m_scene.m_minPhys)
|
||||
else if (oldSize.X * x < Scene.m_minPhys)
|
||||
{
|
||||
f = m_scene.m_minPhys / oldSize.X;
|
||||
a = f / x;
|
||||
@@ -2737,7 +2751,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
z *= a;
|
||||
}
|
||||
|
||||
if (oldSize.Y * y > m_scene.m_maxPhys)
|
||||
if (oldSize.Y * y > Scene.m_maxPhys)
|
||||
{
|
||||
f = m_scene.m_maxPhys / oldSize.Y;
|
||||
a = f / y;
|
||||
@@ -2745,7 +2759,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
y *= a;
|
||||
z *= a;
|
||||
}
|
||||
else if (oldSize.Y * y < m_scene.m_minPhys)
|
||||
else if (oldSize.Y * y < Scene.m_minPhys)
|
||||
{
|
||||
f = m_scene.m_minPhys / oldSize.Y;
|
||||
a = f / y;
|
||||
@@ -2754,7 +2768,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
z *= a;
|
||||
}
|
||||
|
||||
if (oldSize.Z * z > m_scene.m_maxPhys)
|
||||
if (oldSize.Z * z > Scene.m_maxPhys)
|
||||
{
|
||||
f = m_scene.m_maxPhys / oldSize.Z;
|
||||
a = f / z;
|
||||
@@ -2762,7 +2776,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
y *= a;
|
||||
z *= a;
|
||||
}
|
||||
else if (oldSize.Z * z < m_scene.m_minPhys)
|
||||
else if (oldSize.Z * z < Scene.m_minPhys)
|
||||
{
|
||||
f = m_scene.m_minPhys / oldSize.Z;
|
||||
a = f / z;
|
||||
@@ -2773,7 +2787,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
}
|
||||
else
|
||||
{
|
||||
if (oldSize.X * x > m_scene.m_maxNonphys)
|
||||
if (oldSize.X * x > Scene.m_maxNonphys)
|
||||
{
|
||||
f = m_scene.m_maxNonphys / oldSize.X;
|
||||
a = f / x;
|
||||
@@ -2781,7 +2795,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
y *= a;
|
||||
z *= a;
|
||||
}
|
||||
else if (oldSize.X * x < m_scene.m_minNonphys)
|
||||
else if (oldSize.X * x < Scene.m_minNonphys)
|
||||
{
|
||||
f = m_scene.m_minNonphys / oldSize.X;
|
||||
a = f / x;
|
||||
@@ -2790,7 +2804,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
z *= a;
|
||||
}
|
||||
|
||||
if (oldSize.Y * y > m_scene.m_maxNonphys)
|
||||
if (oldSize.Y * y > Scene.m_maxNonphys)
|
||||
{
|
||||
f = m_scene.m_maxNonphys / oldSize.Y;
|
||||
a = f / y;
|
||||
@@ -2798,7 +2812,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
y *= a;
|
||||
z *= a;
|
||||
}
|
||||
else if (oldSize.Y * y < m_scene.m_minNonphys)
|
||||
else if (oldSize.Y * y < Scene.m_minNonphys)
|
||||
{
|
||||
f = m_scene.m_minNonphys / oldSize.Y;
|
||||
a = f / y;
|
||||
@@ -2807,7 +2821,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
z *= a;
|
||||
}
|
||||
|
||||
if (oldSize.Z * z > m_scene.m_maxNonphys)
|
||||
if (oldSize.Z * z > Scene.m_maxNonphys)
|
||||
{
|
||||
f = m_scene.m_maxNonphys / oldSize.Z;
|
||||
a = f / z;
|
||||
@@ -2815,7 +2829,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
y *= a;
|
||||
z *= a;
|
||||
}
|
||||
else if (oldSize.Z * z < m_scene.m_minNonphys)
|
||||
else if (oldSize.Z * z < Scene.m_minNonphys)
|
||||
{
|
||||
f = m_scene.m_minNonphys / oldSize.Z;
|
||||
a = f / z;
|
||||
@@ -2839,7 +2853,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
RootPart.Resize(prevScale);
|
||||
// RootPart.IgnoreUndoUpdate = false;
|
||||
|
||||
parts = m_parts.GetArray();
|
||||
for (int i = 0; i < parts.Length; i++)
|
||||
{
|
||||
SceneObjectPart obPart = parts[i];
|
||||
|
||||
@@ -266,8 +266,8 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
private string m_sitAnimation = "SIT";
|
||||
private string m_text = String.Empty;
|
||||
private string m_touchName = String.Empty;
|
||||
private readonly Stack<UndoState> m_undo = new Stack<UndoState>(5);
|
||||
private readonly Stack<UndoState> m_redo = new Stack<UndoState>(5);
|
||||
private readonly List<UndoState> m_undo = new List<UndoState>(5);
|
||||
private readonly List<UndoState> m_redo = new List<UndoState>(5);
|
||||
|
||||
private bool m_passTouches = false;
|
||||
private bool m_passCollisions = false;
|
||||
@@ -2368,16 +2368,20 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
/// <param name="scale"></param>
|
||||
public void Resize(Vector3 scale)
|
||||
{
|
||||
scale.X = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.X));
|
||||
scale.Y = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.Y));
|
||||
scale.Z = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.Z));
|
||||
|
||||
PhysicsActor pa = PhysActor;
|
||||
if (pa != null && pa.IsPhysical)
|
||||
|
||||
if (ParentGroup.Scene != null)
|
||||
{
|
||||
scale.X = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.X));
|
||||
scale.Y = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.Y));
|
||||
scale.Z = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.Z));
|
||||
scale.X = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.X));
|
||||
scale.Y = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.Y));
|
||||
scale.Z = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.Z));
|
||||
|
||||
if (pa != null && pa.IsPhysical)
|
||||
{
|
||||
scale.X = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.X));
|
||||
scale.Y = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.Y));
|
||||
scale.Z = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.Z));
|
||||
}
|
||||
}
|
||||
|
||||
// m_log.DebugFormat("[SCENE OBJECT PART]: Resizing {0} {1} to {2}", Name, LocalId, scale);
|
||||
@@ -3166,61 +3170,62 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
|
||||
public void StoreUndoState(bool forGroup)
|
||||
{
|
||||
if (!Undoing)
|
||||
if (ParentGroup == null || ParentGroup.Scene == null)
|
||||
return;
|
||||
|
||||
if (Undoing)
|
||||
{
|
||||
if (!IgnoreUndoUpdate)
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE OBJECT PART]: Ignoring undo store for {0} {1} since already undoing", Name, LocalId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IgnoreUndoUpdate)
|
||||
{
|
||||
// m_log.DebugFormat("[SCENE OBJECT PART]: Ignoring undo store for {0} {1}", Name, LocalId);
|
||||
return;
|
||||
}
|
||||
|
||||
lock (m_undo)
|
||||
{
|
||||
if (m_undo.Count > 0)
|
||||
{
|
||||
if (ParentGroup != null)
|
||||
UndoState last = m_undo[m_undo.Count - 1];
|
||||
if (last != null)
|
||||
{
|
||||
lock (m_undo)
|
||||
// TODO: May need to fix for group comparison
|
||||
if (last.Compare(this))
|
||||
{
|
||||
if (m_undo.Count > 0)
|
||||
{
|
||||
UndoState last = m_undo.Peek();
|
||||
if (last != null)
|
||||
{
|
||||
// TODO: May need to fix for group comparison
|
||||
if (last.Compare(this))
|
||||
{
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE OBJECT PART]: Not storing undo for {0} {1} since current state is same as last undo state, initial stack size {2}",
|
||||
// Name, LocalId, m_undo.Count);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE OBJECT PART]: Storing undo state for {0} {1}, forGroup {2}, initial stack size {3}",
|
||||
// Name, LocalId, forGroup, m_undo.Count);
|
||||
|
||||
if (ParentGroup.GetSceneMaxUndo() > 0)
|
||||
{
|
||||
UndoState nUndo = new UndoState(this, forGroup);
|
||||
|
||||
m_undo.Push(nUndo);
|
||||
|
||||
if (m_redo.Count > 0)
|
||||
m_redo.Clear();
|
||||
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE OBJECT PART]: Stored undo state for {0} {1}, forGroup {2}, stack size now {3}",
|
||||
// Name, LocalId, forGroup, m_undo.Count);
|
||||
}
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE OBJECT PART]: Not storing undo for {0} {1} since current state is same as last undo state, initial stack size {2}",
|
||||
// Name, LocalId, m_undo.Count);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// m_log.DebugFormat("[SCENE OBJECT PART]: Ignoring undo store for {0} {1}", Name, LocalId);
|
||||
// }
|
||||
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE OBJECT PART]: Storing undo state for {0} {1}, forGroup {2}, initial stack size {3}",
|
||||
// Name, LocalId, forGroup, m_undo.Count);
|
||||
|
||||
if (ParentGroup.Scene.MaxUndoCount > 0)
|
||||
{
|
||||
UndoState nUndo = new UndoState(this, forGroup);
|
||||
|
||||
m_undo.Add(nUndo);
|
||||
|
||||
if (m_undo.Count > ParentGroup.Scene.MaxUndoCount)
|
||||
m_undo.RemoveAt(0);
|
||||
|
||||
if (m_redo.Count > 0)
|
||||
m_redo.Clear();
|
||||
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE OBJECT PART]: Stored undo state for {0} {1}, forGroup {2}, stack size now {3}",
|
||||
// Name, LocalId, forGroup, m_undo.Count);
|
||||
}
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE OBJECT PART]: Ignoring undo store for {0} {1} since already undoing", Name, LocalId);
|
||||
// }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -3245,21 +3250,24 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
|
||||
if (m_undo.Count > 0)
|
||||
{
|
||||
UndoState goback = m_undo.Pop();
|
||||
UndoState goback = m_undo[m_undo.Count - 1];
|
||||
m_undo.RemoveAt(m_undo.Count - 1);
|
||||
|
||||
if (goback != null)
|
||||
UndoState nUndo = null;
|
||||
|
||||
if (ParentGroup.Scene.MaxUndoCount > 0)
|
||||
{
|
||||
UndoState nUndo = null;
|
||||
|
||||
if (ParentGroup.GetSceneMaxUndo() > 0)
|
||||
{
|
||||
nUndo = new UndoState(this, goback.ForGroup);
|
||||
}
|
||||
nUndo = new UndoState(this, goback.ForGroup);
|
||||
}
|
||||
|
||||
goback.PlaybackState(this);
|
||||
goback.PlaybackState(this);
|
||||
|
||||
if (nUndo != null)
|
||||
m_redo.Push(nUndo);
|
||||
if (nUndo != null)
|
||||
{
|
||||
m_redo.Add(nUndo);
|
||||
|
||||
if (m_redo.Count > ParentGroup.Scene.MaxUndoCount)
|
||||
m_redo.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3279,20 +3287,21 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
|
||||
if (m_redo.Count > 0)
|
||||
{
|
||||
UndoState gofwd = m_redo.Pop();
|
||||
|
||||
if (gofwd != null)
|
||||
UndoState gofwd = m_redo[m_redo.Count - 1];
|
||||
m_redo.RemoveAt(m_redo.Count - 1);
|
||||
|
||||
if (ParentGroup.Scene.MaxUndoCount > 0)
|
||||
{
|
||||
if (ParentGroup.GetSceneMaxUndo() > 0)
|
||||
{
|
||||
UndoState nUndo = new UndoState(this, gofwd.ForGroup);
|
||||
|
||||
m_undo.Push(nUndo);
|
||||
}
|
||||
|
||||
gofwd.PlayfwdState(this);
|
||||
UndoState nUndo = new UndoState(this, gofwd.ForGroup);
|
||||
|
||||
m_undo.Add(nUndo);
|
||||
|
||||
if (m_undo.Count > ParentGroup.Scene.MaxUndoCount)
|
||||
m_undo.RemoveAt(0);
|
||||
}
|
||||
|
||||
gofwd.PlayfwdState(this);
|
||||
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE OBJECT PART]: Handled redo request for {0} {1}, stack size now {2}",
|
||||
// Name, LocalId, m_redo.Count);
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
= log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
public const string LastReportedObjectUpdateStatName = "LastReportedObjectUpdates";
|
||||
public const string SlowFramesStatName = "SlowFrames";
|
||||
|
||||
public delegate void SendStatResult(SimStats stats);
|
||||
|
||||
@@ -128,6 +129,16 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
get { return lastReportedSimStats; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of frames that have taken longer to process than Scene.MIN_FRAME_TIME
|
||||
/// </summary>
|
||||
public Stat SlowFramesStat { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The threshold at which we log a slow frame.
|
||||
/// </summary>
|
||||
public int SlowFramesStatReportThreshold { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Extra sim statistics that are used by monitors but not sent to the client.
|
||||
/// </summary>
|
||||
@@ -225,6 +236,22 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
|
||||
if (StatsManager.SimExtraStats != null)
|
||||
OnSendStatsResult += StatsManager.SimExtraStats.ReceiveClassicSimStatsPacket;
|
||||
|
||||
/// At the moment, we'll only report if a frame is over 120% of target, since commonly frames are a bit
|
||||
/// longer than ideal (which in itself is a concern).
|
||||
SlowFramesStatReportThreshold = (int)Math.Ceiling(m_scene.MinFrameTime * 1000 * 1.2);
|
||||
|
||||
SlowFramesStat
|
||||
= new Stat(
|
||||
"SlowFrames",
|
||||
"Slow Frames",
|
||||
" frames",
|
||||
"scene",
|
||||
m_scene.Name,
|
||||
StatVerbosity.Info,
|
||||
"Number of frames where frame time has been significantly longer than the desired frame time.");
|
||||
|
||||
StatsManager.RegisterStat(SlowFramesStat);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
@@ -418,6 +445,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
lock (m_lastReportedExtraSimStats)
|
||||
{
|
||||
m_lastReportedExtraSimStats[LastReportedObjectUpdateStatName] = m_objectUpdates / m_statsUpdateFactor;
|
||||
m_lastReportedExtraSimStats[SlowFramesStat.ShortName] = (float)SlowFramesStat.Value;
|
||||
|
||||
Dictionary<string, float> physicsStats = m_scene.PhysicsScene.GetStats();
|
||||
|
||||
@@ -535,6 +563,11 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
public void addFrameMS(int ms)
|
||||
{
|
||||
m_frameMS += ms;
|
||||
|
||||
// At the moment, we'll only report if a frame is over 120% of target, since commonly frames are a bit
|
||||
// longer than ideal due to the inaccuracy of the Sleep in Scene.Update() (which in itself is a concern).
|
||||
if (ms > SlowFramesStatReportThreshold)
|
||||
SlowFramesStat.Value++;
|
||||
}
|
||||
|
||||
public void AddSpareMS(int ms)
|
||||
|
||||
@@ -62,8 +62,6 @@ namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
Assert.That(g1Post.RootPart.Scale.X, Is.EqualTo(2));
|
||||
Assert.That(g1Post.RootPart.Scale.Y, Is.EqualTo(3));
|
||||
Assert.That(g1Post.RootPart.Scale.Z, Is.EqualTo(4));
|
||||
|
||||
Assert.That(g1Post.RootPart.UndoCount, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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.Reflection;
|
||||
using NUnit.Framework;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Communications;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for undo/redo
|
||||
/// </summary>
|
||||
public class SceneObjectUndoRedoTests : OpenSimTestCase
|
||||
{
|
||||
[Test]
|
||||
public void TestUndoRedoResizeSceneObject()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
Vector3 firstSize = new Vector3(2, 3, 4);
|
||||
Vector3 secondSize = new Vector3(5, 6, 7);
|
||||
|
||||
Scene scene = new SceneHelpers().SetupScene();
|
||||
scene.MaxUndoCount = 20;
|
||||
SceneObjectGroup g1 = SceneHelpers.AddSceneObject(scene);
|
||||
|
||||
// TODO: It happens to be the case that we are not storing undo states for SOPs which are not yet in a SOG,
|
||||
// which is the way that AddSceneObject() sets up the object (i.e. it creates the SOP first). However,
|
||||
// this is somewhat by chance. Really, we shouldn't be storing undo states at all if the object is not
|
||||
// in a scene.
|
||||
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(0));
|
||||
|
||||
g1.GroupResize(firstSize);
|
||||
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(1));
|
||||
|
||||
g1.GroupResize(secondSize);
|
||||
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(2));
|
||||
|
||||
g1.RootPart.Undo();
|
||||
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(1));
|
||||
Assert.That(g1.GroupScale, Is.EqualTo(firstSize));
|
||||
|
||||
g1.RootPart.Redo();
|
||||
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(2));
|
||||
Assert.That(g1.GroupScale, Is.EqualTo(secondSize));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUndoLimit()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
|
||||
Vector3 firstSize = new Vector3(2, 3, 4);
|
||||
Vector3 secondSize = new Vector3(5, 6, 7);
|
||||
Vector3 thirdSize = new Vector3(8, 9, 10);
|
||||
Vector3 fourthSize = new Vector3(11, 12, 13);
|
||||
|
||||
Scene scene = new SceneHelpers().SetupScene();
|
||||
scene.MaxUndoCount = 2;
|
||||
SceneObjectGroup g1 = SceneHelpers.AddSceneObject(scene);
|
||||
|
||||
g1.GroupResize(firstSize);
|
||||
g1.GroupResize(secondSize);
|
||||
g1.GroupResize(thirdSize);
|
||||
g1.GroupResize(fourthSize);
|
||||
|
||||
g1.RootPart.Undo();
|
||||
g1.RootPart.Undo();
|
||||
g1.RootPart.Undo();
|
||||
|
||||
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(0));
|
||||
Assert.That(g1.GroupScale, Is.EqualTo(secondSize));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNoUndoOnObjectsNotInScene()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
|
||||
Vector3 firstSize = new Vector3(2, 3, 4);
|
||||
Vector3 secondSize = new Vector3(5, 6, 7);
|
||||
Vector3 thirdSize = new Vector3(8, 9, 10);
|
||||
Vector3 fourthSize = new Vector3(11, 12, 13);
|
||||
|
||||
Scene scene = new SceneHelpers().SetupScene();
|
||||
scene.MaxUndoCount = 20;
|
||||
SceneObjectGroup g1 = SceneHelpers.CreateSceneObject(1, TestHelpers.ParseTail(0x1));
|
||||
|
||||
g1.GroupResize(firstSize);
|
||||
g1.GroupResize(secondSize);
|
||||
|
||||
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(0));
|
||||
|
||||
g1.RootPart.Undo();
|
||||
|
||||
Assert.That(g1.GroupScale, Is.EqualTo(secondSize));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUndoBeyondAvailable()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
|
||||
Vector3 newSize = new Vector3(2, 3, 4);
|
||||
|
||||
Scene scene = new SceneHelpers().SetupScene();
|
||||
scene.MaxUndoCount = 20;
|
||||
SceneObjectGroup g1 = SceneHelpers.AddSceneObject(scene);
|
||||
Vector3 originalSize = g1.GroupScale;
|
||||
|
||||
g1.RootPart.Undo();
|
||||
|
||||
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(0));
|
||||
Assert.That(g1.GroupScale, Is.EqualTo(originalSize));
|
||||
|
||||
g1.GroupResize(newSize);
|
||||
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(1));
|
||||
Assert.That(g1.GroupScale, Is.EqualTo(newSize));
|
||||
|
||||
g1.RootPart.Undo();
|
||||
g1.RootPart.Undo();
|
||||
|
||||
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(0));
|
||||
Assert.That(g1.GroupScale, Is.EqualTo(originalSize));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRedoBeyondAvailable()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
|
||||
Vector3 newSize = new Vector3(2, 3, 4);
|
||||
|
||||
Scene scene = new SceneHelpers().SetupScene();
|
||||
scene.MaxUndoCount = 20;
|
||||
SceneObjectGroup g1 = SceneHelpers.AddSceneObject(scene);
|
||||
Vector3 originalSize = g1.GroupScale;
|
||||
|
||||
g1.RootPart.Redo();
|
||||
|
||||
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(0));
|
||||
Assert.That(g1.GroupScale, Is.EqualTo(originalSize));
|
||||
|
||||
g1.GroupResize(newSize);
|
||||
g1.RootPart.Undo();
|
||||
g1.RootPart.Redo();
|
||||
g1.RootPart.Redo();
|
||||
|
||||
Assert.That(g1.RootPart.UndoCount, Is.EqualTo(1));
|
||||
Assert.That(g1.GroupScale, Is.EqualTo(newSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ using OpenSim.Tests.Common.Mock;
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class SceneObjectUserGroupTests
|
||||
public class SceneObjectUserGroupTests : OpenSimTestCase
|
||||
{
|
||||
/// <summary>
|
||||
/// Test share with group object functionality
|
||||
@@ -54,7 +54,6 @@ namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
public void TestShareWithGroup()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
// log4net.Config.XmlConfigurator.Configure();
|
||||
|
||||
UUID userId = UUID.Parse("10000000-0000-0000-0000-000000000001");
|
||||
|
||||
|
||||
@@ -52,26 +52,23 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
public class UuidGatherer
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
/// <summary>
|
||||
/// Asset cache used for gathering assets
|
||||
/// </summary>
|
||||
protected IAssetService m_assetCache;
|
||||
|
||||
/// <summary>
|
||||
/// Used as a temporary store of an asset which represents an object. This can be a null if no appropriate
|
||||
/// asset was found by the asset service.
|
||||
/// </summary>
|
||||
private AssetBase m_requestedObjectAsset;
|
||||
|
||||
/// <summary>
|
||||
/// Signal whether we are currently waiting for the asset service to deliver an asset.
|
||||
/// </summary>
|
||||
private bool m_waitingForObjectAsset;
|
||||
protected IAssetService m_assetService;
|
||||
|
||||
// /// <summary>
|
||||
// /// Used as a temporary store of an asset which represents an object. This can be a null if no appropriate
|
||||
// /// asset was found by the asset service.
|
||||
// /// </summary>
|
||||
// private AssetBase m_requestedObjectAsset;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Signal whether we are currently waiting for the asset service to deliver an asset.
|
||||
// /// </summary>
|
||||
// private bool m_waitingForObjectAsset;
|
||||
|
||||
public UuidGatherer(IAssetService assetCache)
|
||||
{
|
||||
m_assetCache = assetCache;
|
||||
m_assetService = assetCache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -195,18 +192,18 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The callback made when we request the asset for an object from the asset service.
|
||||
/// </summary>
|
||||
private void AssetReceived(string id, Object sender, AssetBase asset)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
m_requestedObjectAsset = asset;
|
||||
m_waitingForObjectAsset = false;
|
||||
Monitor.Pulse(this);
|
||||
}
|
||||
}
|
||||
// /// <summary>
|
||||
// /// The callback made when we request the asset for an object from the asset service.
|
||||
// /// </summary>
|
||||
// private void AssetReceived(string id, Object sender, AssetBase asset)
|
||||
// {
|
||||
// lock (this)
|
||||
// {
|
||||
// m_requestedObjectAsset = asset;
|
||||
// m_waitingForObjectAsset = false;
|
||||
// Monitor.Pulse(this);
|
||||
// }
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Get an asset synchronously, potentially using an asynchronous callback. If the
|
||||
@@ -216,25 +213,29 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
/// <returns></returns>
|
||||
protected virtual AssetBase GetAsset(UUID uuid)
|
||||
{
|
||||
m_waitingForObjectAsset = true;
|
||||
m_assetCache.Get(uuid.ToString(), this, AssetReceived);
|
||||
return m_assetService.Get(uuid.ToString());
|
||||
|
||||
// The asset cache callback can either
|
||||
//
|
||||
// 1. Complete on the same thread (if the asset is already in the cache) or
|
||||
// 2. Come in via a different thread (if we need to go fetch it).
|
||||
//
|
||||
// The code below handles both these alternatives.
|
||||
lock (this)
|
||||
{
|
||||
if (m_waitingForObjectAsset)
|
||||
{
|
||||
Monitor.Wait(this);
|
||||
m_waitingForObjectAsset = false;
|
||||
}
|
||||
}
|
||||
|
||||
return m_requestedObjectAsset;
|
||||
// XXX: Switching to do this synchronously where the call was async before but we always waited for it
|
||||
// to complete anyway!
|
||||
// m_waitingForObjectAsset = true;
|
||||
// m_assetCache.Get(uuid.ToString(), this, AssetReceived);
|
||||
//
|
||||
// // The asset cache callback can either
|
||||
// //
|
||||
// // 1. Complete on the same thread (if the asset is already in the cache) or
|
||||
// // 2. Come in via a different thread (if we need to go fetch it).
|
||||
// //
|
||||
// // The code below handles both these alternatives.
|
||||
// lock (this)
|
||||
// {
|
||||
// if (m_waitingForObjectAsset)
|
||||
// {
|
||||
// Monitor.Wait(this);
|
||||
// m_waitingForObjectAsset = false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return m_requestedObjectAsset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -365,4 +366,47 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class HGUuidGatherer : UuidGatherer
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
protected string m_assetServerURL;
|
||||
|
||||
public HGUuidGatherer(IAssetService assetService, string assetServerURL)
|
||||
: base(assetService)
|
||||
{
|
||||
m_assetServerURL = assetServerURL;
|
||||
if (!m_assetServerURL.EndsWith("/") && !m_assetServerURL.EndsWith("="))
|
||||
m_assetServerURL = m_assetServerURL + "/";
|
||||
}
|
||||
|
||||
protected override AssetBase GetAsset(UUID uuid)
|
||||
{
|
||||
if (string.Empty == m_assetServerURL)
|
||||
return base.GetAsset(uuid);
|
||||
else
|
||||
return FetchAsset(uuid);
|
||||
}
|
||||
|
||||
public AssetBase FetchAsset(UUID assetID)
|
||||
{
|
||||
|
||||
// Test if it's already here
|
||||
AssetBase asset = m_assetService.Get(assetID.ToString());
|
||||
if (asset == null)
|
||||
{
|
||||
// It's not, so fetch it from abroad
|
||||
asset = m_assetService.Get(m_assetServerURL + assetID.ToString());
|
||||
if (asset != null)
|
||||
m_log.DebugFormat("[HGUUIDGatherer]: Copied asset {0} from {1} to local asset server", assetID, m_assetServerURL);
|
||||
else
|
||||
m_log.DebugFormat("[HGUUIDGatherer]: Failed to fetch asset {0} from {1}", assetID, m_assetServerURL);
|
||||
}
|
||||
//else
|
||||
// m_log.DebugFormat("[HGUUIDGatherer]: Asset {0} from {1} was already here", assetID, m_assetServerURL);
|
||||
|
||||
return asset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,37 +130,37 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
|
||||
SendConsoleOutput(agentID, String.Format("auto_grant_attach_perms set to {0}", val));
|
||||
}
|
||||
|
||||
private void llAttachToAvatarTemp(UUID host, UUID script, int attachmentPoint)
|
||||
private int llAttachToAvatarTemp(UUID host, UUID script, int attachmentPoint)
|
||||
{
|
||||
SceneObjectPart hostPart = m_scene.GetSceneObjectPart(host);
|
||||
|
||||
if (hostPart == null)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
if (hostPart.ParentGroup.IsAttachment)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
IAttachmentsModule attachmentsModule = m_scene.RequestModuleInterface<IAttachmentsModule>();
|
||||
if (attachmentsModule == null)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
TaskInventoryItem item = hostPart.Inventory.GetInventoryItem(script);
|
||||
if (item == null)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
if ((item.PermsMask & 32) == 0) // PERMISSION_ATTACH
|
||||
return;
|
||||
return 0;
|
||||
|
||||
ScenePresence target;
|
||||
if (!m_scene.TryGetScenePresence(item.PermsGranter, out target))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
if (target.UUID != hostPart.ParentGroup.OwnerID)
|
||||
{
|
||||
uint effectivePerms = hostPart.ParentGroup.GetEffectivePermissions();
|
||||
|
||||
if ((effectivePerms & (uint)PermissionMask.Transfer) == 0)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
hostPart.ParentGroup.SetOwnerId(target.UUID);
|
||||
hostPart.ParentGroup.SetRootPartOwner(hostPart.ParentGroup.RootPart, target.UUID, target.ControllingClient.ActiveGroupId);
|
||||
@@ -183,7 +183,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
|
||||
hostPart.ParentGroup.RootPart.ScheduleFullUpdate();
|
||||
}
|
||||
|
||||
attachmentsModule.AttachObject(target, hostPart.ParentGroup, (uint)attachmentPoint, false, true);
|
||||
return attachmentsModule.AttachObject(target, hostPart.ParentGroup, (uint)attachmentPoint, false, true) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,12 +231,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||
if (m_server == null || m_baseNick == null || m_ircChannel == null || m_user == null)
|
||||
throw new Exception("Invalid connector configuration");
|
||||
|
||||
// Generate an initial nickname if randomizing is enabled
|
||||
// Generate an initial nickname
|
||||
|
||||
if (m_randomizeNick)
|
||||
{
|
||||
m_nick = m_baseNick + Util.RandomClass.Next(1, 99);
|
||||
}
|
||||
else
|
||||
m_nick = m_baseNick;
|
||||
|
||||
m_log.InfoFormat("[IRC-Connector-{0}]: Initialization complete", idn);
|
||||
|
||||
|
||||
@@ -447,7 +447,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice
|
||||
// settings allow voice, then whether parcel allows
|
||||
// voice, if all do retrieve or obtain the parcel
|
||||
// voice channel
|
||||
LandData land = scene.GetLandData(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y);
|
||||
LandData land = scene.GetLandData(avatar.AbsolutePosition);
|
||||
|
||||
//m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": Parcel \"{1}\" ({2}): avatar \"{3}\": request: {4}, path: {5}, param: {6}",
|
||||
// scene.RegionInfo.RegionName, land.Name, land.LocalID, avatarName, request, path, param);
|
||||
|
||||
@@ -623,7 +623,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.VivoxVoice
|
||||
// settings allow voice, then whether parcel allows
|
||||
// voice, if all do retrieve or obtain the parcel
|
||||
// voice channel
|
||||
LandData land = scene.GetLandData(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y);
|
||||
LandData land = scene.GetLandData(avatar.AbsolutePosition);
|
||||
|
||||
m_log.DebugFormat("[VivoxVoice][PARCELVOICE]: region \"{0}\": Parcel \"{1}\" ({2}): avatar \"{3}\": request: {4}, path: {5}, param: {6}",
|
||||
scene.RegionInfo.RegionName, land.Name, land.LocalID, avatarName, request, path, param);
|
||||
|
||||
@@ -175,14 +175,15 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore
|
||||
///
|
||||
/// </summary>
|
||||
// -----------------------------------------------------------------
|
||||
public bool CreateStore(string value, out UUID result)
|
||||
public bool CreateStore(string value, ref UUID result)
|
||||
{
|
||||
result = UUID.Zero;
|
||||
if (result == UUID.Zero)
|
||||
result = UUID.Random();
|
||||
|
||||
JsonStore map = null;
|
||||
|
||||
if (! m_enabled) return false;
|
||||
|
||||
UUID uuid = UUID.Random();
|
||||
JsonStore map = null;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -195,9 +196,8 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore
|
||||
}
|
||||
|
||||
lock (m_JsonValueStore)
|
||||
m_JsonValueStore.Add(uuid,map);
|
||||
m_JsonValueStore.Add(result,map);
|
||||
|
||||
result = uuid;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore
|
||||
if (! m_JsonValueStore.TryGetValue(storeID,out map))
|
||||
{
|
||||
m_log.InfoFormat("[JsonStore] Missing store {0}",storeID);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore
|
||||
protected UUID JsonCreateStore(UUID hostID, UUID scriptID, string value)
|
||||
{
|
||||
UUID uuid = UUID.Zero;
|
||||
if (! m_store.CreateStore(value, out uuid))
|
||||
if (! m_store.CreateStore(value, ref uuid))
|
||||
GenerateRuntimeError("Failed to create Json store");
|
||||
|
||||
return uuid;
|
||||
|
||||
@@ -181,7 +181,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady
|
||||
}
|
||||
}
|
||||
|
||||
void OnOarFileLoaded(Guid requestId, string message)
|
||||
void OnOarFileLoaded(Guid requestId, List<UUID> loadedScenes, string message)
|
||||
{
|
||||
m_oarFileLoading = true;
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
|
||||
public class BS6DofConstraint : BSConstraint
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM 6DOF CONSTRAINT]";
|
||||
|
||||
public override ConstraintType Type { get { return ConstraintType.D6_CONSTRAINT_TYPE; } }
|
||||
|
||||
// Create a btGeneric6DofConstraint
|
||||
public BS6DofConstraint(BulletSim world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 frame1, Quaternion frame1rot,
|
||||
@@ -44,11 +48,14 @@ public class BS6DofConstraint : BSConstraint
|
||||
m_body1 = obj1;
|
||||
m_body2 = obj2;
|
||||
m_constraint = new BulletConstraint(
|
||||
BulletSimAPI.Create6DofConstraint2(m_world.Ptr, m_body1.Ptr, m_body2.Ptr,
|
||||
BulletSimAPI.Create6DofConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
|
||||
frame1, frame1rot,
|
||||
frame2, frame2rot,
|
||||
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
|
||||
m_enabled = true;
|
||||
world.physicsScene.DetailLog("{0},BS6DofConstraint,createFrame,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
|
||||
BSScene.DetailLogZero, world.worldID,
|
||||
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
|
||||
}
|
||||
|
||||
public BS6DofConstraint(BulletSim world, BulletBody obj1, BulletBody obj2,
|
||||
@@ -58,11 +65,36 @@ public class BS6DofConstraint : BSConstraint
|
||||
m_world = world;
|
||||
m_body1 = obj1;
|
||||
m_body2 = obj2;
|
||||
m_constraint = new BulletConstraint(
|
||||
BulletSimAPI.Create6DofConstraintToPoint2(m_world.Ptr, m_body1.Ptr, m_body2.Ptr,
|
||||
joinPoint,
|
||||
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
|
||||
m_enabled = true;
|
||||
if (obj1.ptr == IntPtr.Zero || obj2.ptr == IntPtr.Zero)
|
||||
{
|
||||
world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
|
||||
BSScene.DetailLogZero, world.worldID,
|
||||
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
|
||||
world.physicsScene.Logger.ErrorFormat("{0} Attempt to build 6DOF constraint with missing bodies: wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
|
||||
"[BULLETSIM 6DOF CONSTRAINT]", world.worldID,
|
||||
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
|
||||
m_enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_constraint = new BulletConstraint(
|
||||
BulletSimAPI.Create6DofConstraintToPoint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
|
||||
joinPoint,
|
||||
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
|
||||
world.physicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}",
|
||||
BSScene.DetailLogZero, world.worldID, m_constraint.ptr.ToString("X"),
|
||||
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
|
||||
if (m_constraint.ptr == IntPtr.Zero)
|
||||
{
|
||||
world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}",
|
||||
LogHeader, obj1.ID, obj2.ID);
|
||||
m_enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot)
|
||||
@@ -70,7 +102,7 @@ public class BS6DofConstraint : BSConstraint
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
{
|
||||
BulletSimAPI.SetFrames2(m_constraint.Ptr, frameA, frameArot, frameB, frameBrot);
|
||||
BulletSimAPI.SetFrames2(m_constraint.ptr, frameA, frameArot, frameB, frameBrot);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
@@ -81,9 +113,9 @@ public class BS6DofConstraint : BSConstraint
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
{
|
||||
BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
|
||||
BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL);
|
||||
BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
|
||||
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
|
||||
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL);
|
||||
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
@@ -94,7 +126,7 @@ public class BS6DofConstraint : BSConstraint
|
||||
bool ret = false;
|
||||
float onOff = useOffset ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
|
||||
if (m_enabled)
|
||||
ret = BulletSimAPI.UseFrameOffset2(m_constraint.Ptr, onOff);
|
||||
ret = BulletSimAPI.UseFrameOffset2(m_constraint.ptr, onOff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -103,7 +135,7 @@ public class BS6DofConstraint : BSConstraint
|
||||
bool ret = false;
|
||||
float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
|
||||
if (m_enabled)
|
||||
ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.Ptr, onOff, targetVelocity, maxMotorForce);
|
||||
ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.ptr, onOff, targetVelocity, maxMotorForce);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -111,7 +143,7 @@ public class BS6DofConstraint : BSConstraint
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
ret = BulletSimAPI.SetBreakingImpulseThreshold2(m_constraint.Ptr, threshold);
|
||||
ret = BulletSimAPI.SetBreakingImpulseThreshold2(m_constraint.ptr, threshold);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using log4net;
|
||||
using OpenMetaverse;
|
||||
using OMV = OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
|
||||
@@ -39,47 +39,35 @@ public class BSCharacter : BSPhysObject
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
private static readonly string LogHeader = "[BULLETS CHAR]";
|
||||
|
||||
public BSScene Scene { get; private set; }
|
||||
private String _avName;
|
||||
// private bool _stopped;
|
||||
private Vector3 _size;
|
||||
private Vector3 _scale;
|
||||
private OMV.Vector3 _size;
|
||||
private OMV.Vector3 _scale;
|
||||
private PrimitiveBaseShape _pbs;
|
||||
private uint _localID = 0;
|
||||
private bool _grabbed;
|
||||
private bool _selected;
|
||||
private Vector3 _position;
|
||||
private OMV.Vector3 _position;
|
||||
private float _mass;
|
||||
public float _density;
|
||||
public float _avatarVolume;
|
||||
private Vector3 _force;
|
||||
private Vector3 _velocity;
|
||||
private Vector3 _torque;
|
||||
private float _avatarDensity;
|
||||
private float _avatarVolume;
|
||||
private OMV.Vector3 _force;
|
||||
private OMV.Vector3 _velocity;
|
||||
private OMV.Vector3 _torque;
|
||||
private float _collisionScore;
|
||||
private Vector3 _acceleration;
|
||||
private Quaternion _orientation;
|
||||
private OMV.Vector3 _acceleration;
|
||||
private OMV.Quaternion _orientation;
|
||||
private int _physicsActorType;
|
||||
private bool _isPhysical;
|
||||
private bool _flying;
|
||||
private bool _setAlwaysRun;
|
||||
private bool _throttleUpdates;
|
||||
private bool _isColliding;
|
||||
private long _collidingStep;
|
||||
private bool _collidingGround;
|
||||
private long _collidingGroundStep;
|
||||
private bool _collidingObj;
|
||||
private bool _floatOnWater;
|
||||
private Vector3 _rotationalVelocity;
|
||||
private OMV.Vector3 _rotationalVelocity;
|
||||
private bool _kinematic;
|
||||
private float _buoyancy;
|
||||
|
||||
public override BulletBody Body { get; set; }
|
||||
public override BSLinkset Linkset { get; set; }
|
||||
|
||||
private int _subscribedEventsMs = 0;
|
||||
private int _nextCollisionOkTime = 0;
|
||||
|
||||
private Vector3 _PIDTarget;
|
||||
private OMV.Vector3 _PIDTarget;
|
||||
private bool _usePID;
|
||||
private float _PIDTau;
|
||||
private bool _useHoverPID;
|
||||
@@ -87,27 +75,26 @@ public class BSCharacter : BSPhysObject
|
||||
private PIDHoverType _PIDHoverType;
|
||||
private float _PIDHoverTao;
|
||||
|
||||
public BSCharacter(uint localID, String avName, BSScene parent_scene, Vector3 pos, Vector3 size, bool isFlying)
|
||||
public BSCharacter(uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, bool isFlying)
|
||||
{
|
||||
_localID = localID;
|
||||
_avName = avName;
|
||||
Scene = parent_scene;
|
||||
base.BaseInitialize(parent_scene, localID, avName, "BSCharacter");
|
||||
_physicsActorType = (int)ActorTypes.Agent;
|
||||
_position = pos;
|
||||
_size = size;
|
||||
_flying = isFlying;
|
||||
_orientation = Quaternion.Identity;
|
||||
_velocity = Vector3.Zero;
|
||||
_orientation = OMV.Quaternion.Identity;
|
||||
_velocity = OMV.Vector3.Zero;
|
||||
_buoyancy = ComputeBuoyancyFromFlying(isFlying);
|
||||
|
||||
// The dimensions of the avatar capsule are kept in the scale.
|
||||
// Physics creates a unit capsule which is scaled by the physics engine.
|
||||
_scale = new Vector3(Scene.Params.avatarCapsuleRadius, Scene.Params.avatarCapsuleRadius, size.Z);
|
||||
_density = Scene.Params.avatarDensity;
|
||||
ComputeAvatarVolumeAndMass(); // set _avatarVolume and _mass based on capsule size, _density and _scale
|
||||
|
||||
Linkset = new BSLinkset(Scene, this);
|
||||
ComputeAvatarScale(_size);
|
||||
_avatarDensity = PhysicsScene.Params.avatarDensity;
|
||||
// set _avatarVolume and _mass based on capsule size, _density and _scale
|
||||
ComputeAvatarVolumeAndMass();
|
||||
|
||||
ShapeData shapeData = new ShapeData();
|
||||
shapeData.ID = _localID;
|
||||
shapeData.ID = LocalID;
|
||||
shapeData.Type = ShapeData.PhysicsShapeType.SHAPE_AVATAR;
|
||||
shapeData.Position = _position;
|
||||
shapeData.Rotation = _orientation;
|
||||
@@ -116,23 +103,25 @@ public class BSCharacter : BSPhysObject
|
||||
shapeData.Mass = _mass;
|
||||
shapeData.Buoyancy = _buoyancy;
|
||||
shapeData.Static = ShapeData.numericFalse;
|
||||
shapeData.Friction = Scene.Params.avatarFriction;
|
||||
shapeData.Restitution = Scene.Params.avatarRestitution;
|
||||
shapeData.Friction = PhysicsScene.Params.avatarFriction;
|
||||
shapeData.Restitution = PhysicsScene.Params.avatarRestitution;
|
||||
|
||||
// do actual create at taint time
|
||||
Scene.TaintedObject("BSCharacter.create", delegate()
|
||||
PhysicsScene.TaintedObject("BSCharacter.create", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.create", _localID);
|
||||
BulletSimAPI.CreateObject(Scene.WorldID, shapeData);
|
||||
DetailLog("{0},BSCharacter.create,taint", LocalID);
|
||||
BulletSimAPI.CreateObject(PhysicsScene.WorldID, shapeData);
|
||||
|
||||
// Set the buoyancy for flying. This will be refactored when all the settings happen in C#
|
||||
BulletSimAPI.SetObjectBuoyancy(Scene.WorldID, LocalID, _buoyancy);
|
||||
// Set the buoyancy for flying. This will be refactored when all the settings happen in C#.
|
||||
// If not set at creation, the avatar will stop flying when created after crossing a region boundry.
|
||||
BulletSimAPI.SetObjectBuoyancy(PhysicsScene.WorldID, LocalID, _buoyancy);
|
||||
|
||||
Body = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(Scene.World.Ptr, LocalID));
|
||||
// avatars get all collisions no matter what (makes walking on ground and such work)
|
||||
BulletSimAPI.AddToCollisionFlags2(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||
BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(PhysicsScene.World.ptr, LocalID));
|
||||
|
||||
// This works here because CreateObject has already put the character into the physical world.
|
||||
BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr,
|
||||
(uint)CollisionFilterGroups.AvatarFilter, (uint)CollisionFilterGroups.AvatarMask);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -140,9 +129,9 @@ public class BSCharacter : BSPhysObject
|
||||
public override void Destroy()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.Destroy", LocalID);
|
||||
Scene.TaintedObject("BSCharacter.destroy", delegate()
|
||||
PhysicsScene.TaintedObject("BSCharacter.destroy", delegate()
|
||||
{
|
||||
BulletSimAPI.DestroyObject(Scene.WorldID, _localID);
|
||||
BulletSimAPI.DestroyObject(PhysicsScene.WorldID, LocalID);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -151,71 +140,96 @@ public class BSCharacter : BSPhysObject
|
||||
base.RequestPhysicsterseUpdate();
|
||||
}
|
||||
// No one calls this method so I don't know what it could possibly mean
|
||||
public override bool Stopped {
|
||||
get { return false; }
|
||||
public override bool Stopped {
|
||||
get { return false; }
|
||||
}
|
||||
public override Vector3 Size {
|
||||
public override OMV.Vector3 Size {
|
||||
get
|
||||
{
|
||||
// Avatar capsule size is kept in the scale parameter.
|
||||
return new Vector3(_scale.X * 2, _scale.Y * 2, _scale.Z);
|
||||
return new OMV.Vector3(_scale.X * 2, _scale.Y * 2, _scale.Z);
|
||||
}
|
||||
|
||||
set {
|
||||
set {
|
||||
// When an avatar's size is set, only the height is changed
|
||||
// and that really only depends on the radius.
|
||||
_size = value;
|
||||
_scale.Z = (_size.Z * 1.15f) - (_scale.X + _scale.Y);
|
||||
ComputeAvatarScale(_size);
|
||||
|
||||
// TODO: something has to be done with the avatar's vertical position
|
||||
|
||||
ComputeAvatarVolumeAndMass();
|
||||
|
||||
Scene.TaintedObject("BSCharacter.setSize", delegate()
|
||||
PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
|
||||
{
|
||||
BulletSimAPI.SetObjectScaleMass(Scene.WorldID, LocalID, _scale, _mass, true);
|
||||
BulletSimAPI.SetObjectScaleMass(PhysicsScene.WorldID, LocalID, _scale, _mass, true);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
public override PrimitiveBaseShape Shape {
|
||||
set { _pbs = value;
|
||||
}
|
||||
}
|
||||
public override uint LocalID {
|
||||
set { _localID = value;
|
||||
}
|
||||
get { return _localID; }
|
||||
}
|
||||
public override bool Grabbed {
|
||||
set { _grabbed = value;
|
||||
}
|
||||
public override PrimitiveBaseShape Shape {
|
||||
set { _pbs = value;
|
||||
}
|
||||
}
|
||||
public override bool Selected {
|
||||
set { _selected = value;
|
||||
}
|
||||
public override bool Grabbed {
|
||||
set { _grabbed = value;
|
||||
}
|
||||
}
|
||||
public override bool Selected {
|
||||
set { _selected = value;
|
||||
}
|
||||
}
|
||||
public override void CrossingFailure() { return; }
|
||||
public override void link(PhysicsActor obj) { return; }
|
||||
public override void delink() { return; }
|
||||
public override void LockAngularMotion(Vector3 axis) { return; }
|
||||
|
||||
public override Vector3 Position {
|
||||
// Set motion values to zero.
|
||||
// Do it to the properties so the values get set in the physics engine.
|
||||
// Push the setting of the values to the viewer.
|
||||
// Called at taint time!
|
||||
public override void ZeroMotion()
|
||||
{
|
||||
_velocity = OMV.Vector3.Zero;
|
||||
_acceleration = OMV.Vector3.Zero;
|
||||
_rotationalVelocity = OMV.Vector3.Zero;
|
||||
|
||||
// Zero some other properties directly into the physics engine
|
||||
BulletSimAPI.SetLinearVelocity2(BSBody.ptr, OMV.Vector3.Zero);
|
||||
BulletSimAPI.SetAngularVelocity2(BSBody.ptr, OMV.Vector3.Zero);
|
||||
BulletSimAPI.SetInterpolationVelocity2(BSBody.ptr, OMV.Vector3.Zero, OMV.Vector3.Zero);
|
||||
BulletSimAPI.ClearForces2(BSBody.ptr);
|
||||
}
|
||||
|
||||
public override void LockAngularMotion(OMV.Vector3 axis) { return; }
|
||||
|
||||
public override OMV.Vector3 Position {
|
||||
get {
|
||||
// _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID);
|
||||
return _position;
|
||||
}
|
||||
return _position;
|
||||
}
|
||||
set {
|
||||
_position = value;
|
||||
PositionSanityCheck();
|
||||
|
||||
Scene.TaintedObject("BSCharacter.setPosition", delegate()
|
||||
PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
|
||||
BulletSimAPI.SetObjectTranslation(Scene.WorldID, _localID, _position, _orientation);
|
||||
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
public override OMV.Vector3 ForcePosition {
|
||||
get {
|
||||
_position = BulletSimAPI.GetPosition2(BSBody.ptr);
|
||||
return _position;
|
||||
}
|
||||
set {
|
||||
_position = value;
|
||||
PositionSanityCheck();
|
||||
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check that the current position is sane and, if not, modify the position to make it so.
|
||||
// Check for being below terrain and being out of bounds.
|
||||
@@ -223,15 +237,24 @@ public class BSCharacter : BSPhysObject
|
||||
private bool PositionSanityCheck()
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
|
||||
// If below the ground, move the avatar up
|
||||
float terrainHeight = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position);
|
||||
float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
|
||||
if (Position.Z < terrainHeight)
|
||||
{
|
||||
DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
|
||||
_position.Z = terrainHeight + 2.0f;
|
||||
ret = true;
|
||||
}
|
||||
if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
|
||||
{
|
||||
float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position);
|
||||
if (Position.Z < waterHeight)
|
||||
{
|
||||
_position.Z = waterHeight;
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check for out of bounds
|
||||
return ret;
|
||||
@@ -240,207 +263,234 @@ public class BSCharacter : BSPhysObject
|
||||
// A version of the sanity check that also makes sure a new position value is
|
||||
// pushed back to the physics engine. This routine would be used by anyone
|
||||
// who is not already pushing the value.
|
||||
private bool PositionSanityCheck2()
|
||||
private bool PositionSanityCheck2(bool atTaintTime)
|
||||
{
|
||||
bool ret = false;
|
||||
if (PositionSanityCheck())
|
||||
{
|
||||
// The new position value must be pushed into the physics engine but we can't
|
||||
// just assign to "Position" because of potential call loops.
|
||||
Scene.TaintedObject("BSCharacter.PositionSanityCheck", delegate()
|
||||
BSScene.TaintCallback sanityOperation = delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
|
||||
BulletSimAPI.SetObjectTranslation(Scene.WorldID, _localID, _position, _orientation);
|
||||
});
|
||||
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation);
|
||||
};
|
||||
if (atTaintTime)
|
||||
sanityOperation();
|
||||
else
|
||||
PhysicsScene.TaintedObject("BSCharacter.PositionSanityCheck", sanityOperation);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override float Mass {
|
||||
get {
|
||||
return _mass;
|
||||
}
|
||||
public override float Mass {
|
||||
get {
|
||||
return _mass;
|
||||
}
|
||||
}
|
||||
|
||||
// used when we only want this prim's mass and not the linkset thing
|
||||
public override float MassRaw { get {return _mass; } }
|
||||
|
||||
public override Vector3 Force {
|
||||
get { return _force; }
|
||||
public override OMV.Vector3 Force {
|
||||
get { return _force; }
|
||||
set {
|
||||
_force = value;
|
||||
// m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force);
|
||||
Scene.TaintedObject("BSCharacter.SetForce", delegate()
|
||||
PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
|
||||
BulletSimAPI.SetObjectForce(Scene.WorldID, LocalID, _force);
|
||||
BulletSimAPI.SetObjectForce(PhysicsScene.WorldID, LocalID, _force);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int VehicleType {
|
||||
get { return 0; }
|
||||
set { return; }
|
||||
public override int VehicleType {
|
||||
get { return 0; }
|
||||
set { return; }
|
||||
}
|
||||
public override void VehicleFloatParam(int param, float value) { }
|
||||
public override void VehicleVectorParam(int param, Vector3 value) {}
|
||||
public override void VehicleRotationParam(int param, Quaternion rotation) { }
|
||||
public override void VehicleVectorParam(int param, OMV.Vector3 value) {}
|
||||
public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { }
|
||||
public override void VehicleFlags(int param, bool remove) { }
|
||||
|
||||
// Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
|
||||
public override void SetVolumeDetect(int param) { return; }
|
||||
|
||||
public override Vector3 GeometricCenter { get { return Vector3.Zero; } }
|
||||
public override Vector3 CenterOfMass { get { return Vector3.Zero; } }
|
||||
public override Vector3 Velocity {
|
||||
get { return _velocity; }
|
||||
public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } }
|
||||
public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } }
|
||||
public override OMV.Vector3 Velocity {
|
||||
get { return _velocity; }
|
||||
set {
|
||||
_velocity = value;
|
||||
// m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity);
|
||||
Scene.TaintedObject("BSCharacter.setVelocity", delegate()
|
||||
PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity);
|
||||
BulletSimAPI.SetObjectVelocity(Scene.WorldID, _localID, _velocity);
|
||||
BulletSimAPI.SetObjectVelocity(PhysicsScene.WorldID, LocalID, _velocity);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
public override Vector3 Torque {
|
||||
get { return _torque; }
|
||||
set { _torque = value;
|
||||
}
|
||||
public override OMV.Vector3 Torque {
|
||||
get { return _torque; }
|
||||
set { _torque = value;
|
||||
}
|
||||
}
|
||||
public override float CollisionScore {
|
||||
get { return _collisionScore; }
|
||||
set { _collisionScore = value;
|
||||
}
|
||||
public override float CollisionScore {
|
||||
get { return _collisionScore; }
|
||||
set { _collisionScore = value;
|
||||
}
|
||||
}
|
||||
public override Vector3 Acceleration {
|
||||
public override OMV.Vector3 Acceleration {
|
||||
get { return _acceleration; }
|
||||
set { _acceleration = value; }
|
||||
}
|
||||
public override Quaternion Orientation {
|
||||
get { return _orientation; }
|
||||
public override OMV.Quaternion Orientation {
|
||||
get { return _orientation; }
|
||||
set {
|
||||
_orientation = value;
|
||||
// m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
|
||||
Scene.TaintedObject("BSCharacter.setOrientation", delegate()
|
||||
PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
|
||||
{
|
||||
// _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID);
|
||||
BulletSimAPI.SetObjectTranslation(Scene.WorldID, _localID, _position, _orientation);
|
||||
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
public override int PhysicsActorType {
|
||||
get { return _physicsActorType; }
|
||||
set { _physicsActorType = value;
|
||||
}
|
||||
// Go directly to Bullet to get/set the value.
|
||||
public override OMV.Quaternion ForceOrientation
|
||||
{
|
||||
get
|
||||
{
|
||||
_orientation = BulletSimAPI.GetOrientation2(BSBody.ptr);
|
||||
return _orientation;
|
||||
}
|
||||
set
|
||||
{
|
||||
_orientation = value;
|
||||
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
|
||||
}
|
||||
}
|
||||
public override bool IsPhysical {
|
||||
get { return _isPhysical; }
|
||||
public override int PhysicsActorType {
|
||||
get { return _physicsActorType; }
|
||||
set { _physicsActorType = value;
|
||||
}
|
||||
}
|
||||
public override bool IsPhysical {
|
||||
get { return _isPhysical; }
|
||||
set { _isPhysical = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
public override bool Flying {
|
||||
get { return _flying; }
|
||||
public override bool Flying {
|
||||
get { return _flying; }
|
||||
set {
|
||||
_flying = value;
|
||||
// simulate flying by changing the effect of gravity
|
||||
this.Buoyancy = ComputeBuoyancyFromFlying(_flying);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Flying is implimented by changing the avatar's buoyancy.
|
||||
// Would this be done better with a vehicle type?
|
||||
private float ComputeBuoyancyFromFlying(bool ifFlying) {
|
||||
return ifFlying ? 1f : 0f;
|
||||
}
|
||||
public override bool
|
||||
SetAlwaysRun {
|
||||
get { return _setAlwaysRun; }
|
||||
set { _setAlwaysRun = value; }
|
||||
public override bool
|
||||
SetAlwaysRun {
|
||||
get { return _setAlwaysRun; }
|
||||
set { _setAlwaysRun = value; }
|
||||
}
|
||||
public override bool ThrottleUpdates {
|
||||
get { return _throttleUpdates; }
|
||||
set { _throttleUpdates = value; }
|
||||
public override bool ThrottleUpdates {
|
||||
get { return _throttleUpdates; }
|
||||
set { _throttleUpdates = value; }
|
||||
}
|
||||
public override bool IsColliding {
|
||||
get { return (_collidingStep == Scene.SimulationStep); }
|
||||
set { _isColliding = value; }
|
||||
get { return (CollidingStep == PhysicsScene.SimulationStep); }
|
||||
set { _isColliding = value; }
|
||||
}
|
||||
public override bool CollidingGround {
|
||||
get { return (_collidingGroundStep == Scene.SimulationStep); }
|
||||
set { _collidingGround = value; }
|
||||
get { return (CollidingGroundStep == PhysicsScene.SimulationStep); }
|
||||
set { CollidingGround = value; }
|
||||
}
|
||||
public override bool CollidingObj {
|
||||
get { return _collidingObj; }
|
||||
set { _collidingObj = value; }
|
||||
public override bool CollidingObj {
|
||||
get { return _collidingObj; }
|
||||
set { _collidingObj = value; }
|
||||
}
|
||||
public override bool FloatOnWater {
|
||||
set { _floatOnWater = value; }
|
||||
public override bool FloatOnWater {
|
||||
set {
|
||||
_floatOnWater = value;
|
||||
PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate()
|
||||
{
|
||||
if (_floatOnWater)
|
||||
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
|
||||
else
|
||||
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
|
||||
});
|
||||
}
|
||||
}
|
||||
public override Vector3 RotationalVelocity {
|
||||
get { return _rotationalVelocity; }
|
||||
set { _rotationalVelocity = value; }
|
||||
public override OMV.Vector3 RotationalVelocity {
|
||||
get { return _rotationalVelocity; }
|
||||
set { _rotationalVelocity = value; }
|
||||
}
|
||||
public override bool Kinematic {
|
||||
get { return _kinematic; }
|
||||
set { _kinematic = value; }
|
||||
public override bool Kinematic {
|
||||
get { return _kinematic; }
|
||||
set { _kinematic = value; }
|
||||
}
|
||||
// neg=fall quickly, 0=1g, 1=0g, pos=float up
|
||||
public override float Buoyancy {
|
||||
get { return _buoyancy; }
|
||||
set { _buoyancy = value;
|
||||
Scene.TaintedObject("BSCharacter.setBuoyancy", delegate()
|
||||
public override float Buoyancy {
|
||||
get { return _buoyancy; }
|
||||
set { _buoyancy = value;
|
||||
PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
|
||||
BulletSimAPI.SetObjectBuoyancy(Scene.WorldID, LocalID, _buoyancy);
|
||||
BulletSimAPI.SetObjectBuoyancy(PhysicsScene.WorldID, LocalID, _buoyancy);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used for MoveTo
|
||||
public override Vector3 PIDTarget {
|
||||
set { _PIDTarget = value; }
|
||||
public override OMV.Vector3 PIDTarget {
|
||||
set { _PIDTarget = value; }
|
||||
}
|
||||
public override bool PIDActive {
|
||||
set { _usePID = value; }
|
||||
public override bool PIDActive {
|
||||
set { _usePID = value; }
|
||||
}
|
||||
public override float PIDTau {
|
||||
set { _PIDTau = value; }
|
||||
public override float PIDTau {
|
||||
set { _PIDTau = value; }
|
||||
}
|
||||
|
||||
// Used for llSetHoverHeight and maybe vehicle height
|
||||
// Hover Height will override MoveTo target's Z
|
||||
public override bool PIDHoverActive {
|
||||
public override bool PIDHoverActive {
|
||||
set { _useHoverPID = value; }
|
||||
}
|
||||
public override float PIDHoverHeight {
|
||||
public override float PIDHoverHeight {
|
||||
set { _PIDHoverHeight = value; }
|
||||
}
|
||||
public override PIDHoverType PIDHoverType {
|
||||
public override PIDHoverType PIDHoverType {
|
||||
set { _PIDHoverType = value; }
|
||||
}
|
||||
public override float PIDHoverTau {
|
||||
public override float PIDHoverTau {
|
||||
set { _PIDHoverTao = value; }
|
||||
}
|
||||
|
||||
// For RotLookAt
|
||||
public override Quaternion APIDTarget { set { return; } }
|
||||
public override OMV.Quaternion APIDTarget { set { return; } }
|
||||
public override bool APIDActive { set { return; } }
|
||||
public override float APIDStrength { set { return; } }
|
||||
public override float APIDDamping { set { return; } }
|
||||
|
||||
public override void AddForce(Vector3 force, bool pushforce) {
|
||||
public override void AddForce(OMV.Vector3 force, bool pushforce) {
|
||||
if (force.IsFinite())
|
||||
{
|
||||
_force.X += force.X;
|
||||
_force.Y += force.Y;
|
||||
_force.Z += force.Z;
|
||||
// m_log.DebugFormat("{0}: AddForce. adding={1}, newForce={2}", LogHeader, force, _force);
|
||||
Scene.TaintedObject("BSCharacter.AddForce", delegate()
|
||||
PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force);
|
||||
BulletSimAPI.AddObjectForce2(Body.Ptr, _force);
|
||||
BulletSimAPI.SetObjectForce2(BSBody.ptr, _force);
|
||||
});
|
||||
}
|
||||
else
|
||||
@@ -450,43 +500,19 @@ public class BSCharacter : BSPhysObject
|
||||
//m_lastUpdateSent = false;
|
||||
}
|
||||
|
||||
public override void AddAngularForce(Vector3 force, bool pushforce) {
|
||||
public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
|
||||
}
|
||||
public override void SetMomentum(Vector3 momentum) {
|
||||
public override void SetMomentum(OMV.Vector3 momentum) {
|
||||
}
|
||||
|
||||
// Turn on collision events at a rate no faster than one every the given milliseconds
|
||||
public override void SubscribeEvents(int ms) {
|
||||
_subscribedEventsMs = ms;
|
||||
if (ms > 0)
|
||||
{
|
||||
// make sure first collision happens
|
||||
_nextCollisionOkTime = Util.EnvironmentTickCount() - _subscribedEventsMs;
|
||||
|
||||
Scene.TaintedObject("BSCharacter.SubscribeEvents", delegate()
|
||||
{
|
||||
BulletSimAPI.AddToCollisionFlags2(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override void ZeroMotion()
|
||||
private void ComputeAvatarScale(OMV.Vector3 size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_scale.X = PhysicsScene.Params.avatarCapsuleRadius;
|
||||
_scale.Y = PhysicsScene.Params.avatarCapsuleRadius;
|
||||
|
||||
// Stop collision events
|
||||
public override void UnSubscribeEvents() {
|
||||
_subscribedEventsMs = 0;
|
||||
// Avatars get all their collision events
|
||||
// Scene.TaintedObject("BSCharacter.UnSubscribeEvents", delegate()
|
||||
// {
|
||||
// BulletSimAPI.RemoveFromCollisionFlags2(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||
// });
|
||||
}
|
||||
// Return 'true' if someone has subscribed to events
|
||||
public override bool SubscribedEvents() {
|
||||
return (_subscribedEventsMs > 0);
|
||||
// The 1.15 came from ODE but it seems to cause the avatar to float off the ground
|
||||
// _scale.Z = (_size.Z * 1.15f) - (_scale.X + _scale.Y);
|
||||
_scale.Z = (_size.Z) - (_scale.X + _scale.Y);
|
||||
}
|
||||
|
||||
// set _avatarVolume and _mass based on capsule size, _density and _scale
|
||||
@@ -503,7 +529,7 @@ public class BSCharacter : BSPhysObject
|
||||
* Math.Min(_scale.X, _scale.Y)
|
||||
* _scale.Y // plus the volume of the capsule end caps
|
||||
);
|
||||
_mass = _density * _avatarVolume;
|
||||
_mass = _avatarDensity * _avatarVolume;
|
||||
}
|
||||
|
||||
// The physics engine says that properties have updated. Update same and inform
|
||||
@@ -515,71 +541,14 @@ public class BSCharacter : BSPhysObject
|
||||
_velocity = entprop.Velocity;
|
||||
_acceleration = entprop.Acceleration;
|
||||
_rotationalVelocity = entprop.RotationalVelocity;
|
||||
// Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
|
||||
PositionSanityCheck2(true);
|
||||
|
||||
// Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
|
||||
// base.RequestPhysicsterseUpdate();
|
||||
|
||||
// Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
|
||||
PositionSanityCheck2();
|
||||
|
||||
float heightHere = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position); // only for debug
|
||||
DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5},terrain={6}",
|
||||
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity, heightHere);
|
||||
}
|
||||
|
||||
// Called by the scene when a collision with this object is reported
|
||||
// The collision, if it should be reported to the character, is placed in a collection
|
||||
// that will later be sent to the simulator when SendCollisions() is called.
|
||||
CollisionEventUpdate collisionCollection = null;
|
||||
public override void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth)
|
||||
{
|
||||
// m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith);
|
||||
|
||||
// The following makes IsColliding() and IsCollidingGround() work
|
||||
_collidingStep = Scene.SimulationStep;
|
||||
if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID)
|
||||
{
|
||||
_collidingGroundStep = Scene.SimulationStep;
|
||||
}
|
||||
// DetailLog("{0},BSCharacter.Collison,call,with={1}", LocalID, collidingWith);
|
||||
|
||||
// throttle collisions to the rate specified in the subscription
|
||||
if (_subscribedEventsMs != 0) {
|
||||
int nowTime = Scene.SimulationNowTime;
|
||||
if (nowTime >= _nextCollisionOkTime) {
|
||||
_nextCollisionOkTime = nowTime + _subscribedEventsMs;
|
||||
|
||||
if (collisionCollection == null)
|
||||
collisionCollection = new CollisionEventUpdate();
|
||||
collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void SendCollisions()
|
||||
{
|
||||
/*
|
||||
if (collisionCollection != null && collisionCollection.Count > 0)
|
||||
{
|
||||
base.SendCollisionUpdate(collisionCollection);
|
||||
collisionCollection = null;
|
||||
}
|
||||
*/
|
||||
// Kludge to make a collision call even if there are no collisions.
|
||||
// This causes the avatar animation to get updated.
|
||||
if (collisionCollection == null)
|
||||
collisionCollection = new CollisionEventUpdate();
|
||||
base.SendCollisionUpdate(collisionCollection);
|
||||
// If there were any collisions in the collection, make sure we don't use the
|
||||
// same instance next time.
|
||||
if (collisionCollection.Count > 0)
|
||||
collisionCollection = null;
|
||||
// End kludge
|
||||
}
|
||||
|
||||
// Invoke the detailed logger and output something if it's enabled.
|
||||
private void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
Scene.PhysicsLogging.Write(msg, args);
|
||||
DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
|
||||
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,20 +49,23 @@ public abstract class BSConstraint : IDisposable
|
||||
if (m_enabled)
|
||||
{
|
||||
m_enabled = false;
|
||||
bool success = BulletSimAPI.DestroyConstraint2(m_world.Ptr, m_constraint.Ptr);
|
||||
m_world.scene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success);
|
||||
m_constraint.Ptr = System.IntPtr.Zero;
|
||||
bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr);
|
||||
m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success);
|
||||
m_constraint.ptr = System.IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
public BulletBody Body1 { get { return m_body1; } }
|
||||
public BulletBody Body2 { get { return m_body2; } }
|
||||
public BulletConstraint Constraint { get { return m_constraint; } }
|
||||
public abstract ConstraintType Type { get; }
|
||||
|
||||
|
||||
public virtual bool SetLinearLimits(Vector3 low, Vector3 high)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
ret = BulletSimAPI.SetLinearLimits2(m_constraint.Ptr, low, high);
|
||||
ret = BulletSimAPI.SetLinearLimits2(m_constraint.ptr, low, high);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -70,7 +73,18 @@ public abstract class BSConstraint : IDisposable
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
ret = BulletSimAPI.SetAngularLimits2(m_constraint.Ptr, low, high);
|
||||
ret = BulletSimAPI.SetAngularLimits2(m_constraint.ptr, low, high);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public virtual bool SetSolverIterations(float cnt)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
{
|
||||
BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.ptr, cnt);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -80,7 +94,7 @@ public abstract class BSConstraint : IDisposable
|
||||
if (m_enabled)
|
||||
{
|
||||
// Recompute the internal transforms
|
||||
BulletSimAPI.CalculateTransforms2(m_constraint.Ptr);
|
||||
BulletSimAPI.CalculateTransforms2(m_constraint.ptr);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
@@ -96,13 +110,14 @@ public abstract class BSConstraint : IDisposable
|
||||
ret = CalculateTransforms();
|
||||
if (ret)
|
||||
{
|
||||
// m_world.scene.PhysicsLogging.Write("{0},BSConstraint.RecomputeConstraintVariables,taint,enabling,A={1},B={2}",
|
||||
// BSScene.DetailLogZero, Body1.ID, Body2.ID);
|
||||
BulletSimAPI.SetConstraintEnable2(m_constraint.Ptr, m_world.scene.NumericBool(true));
|
||||
// Setting an object's mass to zero (making it static like when it's selected)
|
||||
// automatically disables the constraints.
|
||||
// If the link is enabled, be sure to set the constraint itself to enabled.
|
||||
BulletSimAPI.SetConstraintEnable2(m_constraint.ptr, m_world.physicsScene.NumericBool(true));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_world.scene.Logger.ErrorFormat("[BULLETSIM CONSTRAINT] CalculateTransforms failed. A={0}, B={1}", Body1.ID, Body2.ID);
|
||||
m_world.physicsScene.Logger.ErrorFormat("[BULLETSIM CONSTRAINT] CalculateTransforms failed. A={0}, B={1}", Body1.ID, Body2.ID);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
@@ -54,18 +54,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
{
|
||||
public class BSDynamics
|
||||
{
|
||||
private int frcount = 0; // Used to limit dynamics debug output to
|
||||
// every 100th frame
|
||||
|
||||
private BSScene m_physicsScene;
|
||||
private BSPrim m_prim; // the prim this dynamic controller belongs to
|
||||
private BSScene PhysicsScene { get; set; }
|
||||
// the prim this dynamic controller belongs to
|
||||
private BSPrim Prim { get; set; }
|
||||
|
||||
// Vehicle properties
|
||||
private Vehicle m_type = Vehicle.TYPE_NONE; // If a 'VEHICLE', and what kind
|
||||
public Vehicle Type
|
||||
{
|
||||
get { return m_type; }
|
||||
}
|
||||
public Vehicle Type { get; set; }
|
||||
|
||||
// private Quaternion m_referenceFrame = Quaternion.Identity; // Axis modifier
|
||||
private VehicleFlag m_flags = (VehicleFlag) 0; // Boolean settings:
|
||||
// HOVER_TERRAIN_ONLY
|
||||
@@ -80,7 +75,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
// Linear properties
|
||||
private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time
|
||||
private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL
|
||||
private Vector3 m_dir = Vector3.Zero; // velocity applied to body
|
||||
private Vector3 m_newVelocity = Vector3.Zero; // velocity computed to be applied to body
|
||||
private Vector3 m_linearFrictionTimescale = Vector3.Zero;
|
||||
private float m_linearMotorDecayTimescale = 0;
|
||||
private float m_linearMotorTimescale = 0;
|
||||
@@ -97,7 +92,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate
|
||||
private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate
|
||||
private Vector3 m_lastAngularVelocity = Vector3.Zero; // what was last applied to body
|
||||
// private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body
|
||||
private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body
|
||||
|
||||
//Deflection properties
|
||||
// private float m_angularDeflectionEfficiency = 0;
|
||||
@@ -126,85 +121,72 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
|
||||
public BSDynamics(BSScene myScene, BSPrim myPrim)
|
||||
{
|
||||
m_physicsScene = myScene;
|
||||
m_prim = myPrim;
|
||||
m_type = Vehicle.TYPE_NONE;
|
||||
PhysicsScene = myScene;
|
||||
Prim = myPrim;
|
||||
Type = Vehicle.TYPE_NONE;
|
||||
}
|
||||
|
||||
internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue, float timestep)
|
||||
// Return 'true' if this vehicle is doing vehicle things
|
||||
public bool IsActive
|
||||
{
|
||||
VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue);
|
||||
get { return Type != Vehicle.TYPE_NONE; }
|
||||
}
|
||||
|
||||
internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue)
|
||||
{
|
||||
VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue);
|
||||
switch (pParam)
|
||||
{
|
||||
case Vehicle.ANGULAR_DEFLECTION_EFFICIENCY:
|
||||
if (pValue < 0.01f) pValue = 0.01f;
|
||||
// m_angularDeflectionEfficiency = pValue;
|
||||
// m_angularDeflectionEfficiency = Math.Max(pValue, 0.01f);
|
||||
break;
|
||||
case Vehicle.ANGULAR_DEFLECTION_TIMESCALE:
|
||||
if (pValue < 0.01f) pValue = 0.01f;
|
||||
// m_angularDeflectionTimescale = pValue;
|
||||
// m_angularDeflectionTimescale = Math.Max(pValue, 0.01f);
|
||||
break;
|
||||
case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE:
|
||||
if (pValue < 0.01f) pValue = 0.01f;
|
||||
m_angularMotorDecayTimescale = pValue;
|
||||
m_angularMotorDecayTimescale = Math.Max(pValue, 0.01f);
|
||||
break;
|
||||
case Vehicle.ANGULAR_MOTOR_TIMESCALE:
|
||||
if (pValue < 0.01f) pValue = 0.01f;
|
||||
m_angularMotorTimescale = pValue;
|
||||
m_angularMotorTimescale = Math.Max(pValue, 0.01f);
|
||||
break;
|
||||
case Vehicle.BANKING_EFFICIENCY:
|
||||
if (pValue < 0.01f) pValue = 0.01f;
|
||||
// m_bankingEfficiency = pValue;
|
||||
// m_bankingEfficiency = Math.Max(pValue, 0.01f);
|
||||
break;
|
||||
case Vehicle.BANKING_MIX:
|
||||
if (pValue < 0.01f) pValue = 0.01f;
|
||||
// m_bankingMix = pValue;
|
||||
// m_bankingMix = Math.Max(pValue, 0.01f);
|
||||
break;
|
||||
case Vehicle.BANKING_TIMESCALE:
|
||||
if (pValue < 0.01f) pValue = 0.01f;
|
||||
// m_bankingTimescale = pValue;
|
||||
// m_bankingTimescale = Math.Max(pValue, 0.01f);
|
||||
break;
|
||||
case Vehicle.BUOYANCY:
|
||||
if (pValue < -1f) pValue = -1f;
|
||||
if (pValue > 1f) pValue = 1f;
|
||||
m_VehicleBuoyancy = pValue;
|
||||
m_VehicleBuoyancy = Math.Max(-1f, Math.Min(pValue, 1f));
|
||||
break;
|
||||
// case Vehicle.HOVER_EFFICIENCY:
|
||||
// if (pValue < 0f) pValue = 0f;
|
||||
// if (pValue > 1f) pValue = 1f;
|
||||
// m_VhoverEfficiency = pValue;
|
||||
// m_VhoverEfficiency = Math.Max(0f, Math.Min(pValue, 1f));
|
||||
// break;
|
||||
case Vehicle.HOVER_HEIGHT:
|
||||
m_VhoverHeight = pValue;
|
||||
break;
|
||||
case Vehicle.HOVER_TIMESCALE:
|
||||
if (pValue < 0.01f) pValue = 0.01f;
|
||||
m_VhoverTimescale = pValue;
|
||||
m_VhoverTimescale = Math.Max(pValue, 0.01f);
|
||||
break;
|
||||
case Vehicle.LINEAR_DEFLECTION_EFFICIENCY:
|
||||
if (pValue < 0.01f) pValue = 0.01f;
|
||||
// m_linearDeflectionEfficiency = pValue;
|
||||
// m_linearDeflectionEfficiency = Math.Max(pValue, 0.01f);
|
||||
break;
|
||||
case Vehicle.LINEAR_DEFLECTION_TIMESCALE:
|
||||
if (pValue < 0.01f) pValue = 0.01f;
|
||||
// m_linearDeflectionTimescale = pValue;
|
||||
// m_linearDeflectionTimescale = Math.Max(pValue, 0.01f);
|
||||
break;
|
||||
case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE:
|
||||
if (pValue < 0.01f) pValue = 0.01f;
|
||||
m_linearMotorDecayTimescale = pValue;
|
||||
m_linearMotorDecayTimescale = Math.Max(pValue, 0.01f);
|
||||
break;
|
||||
case Vehicle.LINEAR_MOTOR_TIMESCALE:
|
||||
if (pValue < 0.01f) pValue = 0.01f;
|
||||
m_linearMotorTimescale = pValue;
|
||||
m_linearMotorTimescale = Math.Max(pValue, 0.01f);
|
||||
break;
|
||||
case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY:
|
||||
if (pValue < 0.1f) pValue = 0.1f; // Less goes unstable
|
||||
if (pValue > 1.0f) pValue = 1.0f;
|
||||
m_verticalAttractionEfficiency = pValue;
|
||||
m_verticalAttractionEfficiency = Math.Max(0.1f, Math.Min(pValue, 1f));
|
||||
break;
|
||||
case Vehicle.VERTICAL_ATTRACTION_TIMESCALE:
|
||||
if (pValue < 0.01f) pValue = 0.01f;
|
||||
m_verticalAttractionTimescale = pValue;
|
||||
m_verticalAttractionTimescale = Math.Max(pValue, 0.01f);
|
||||
break;
|
||||
|
||||
// These are vector properties but the engine lets you use a single float value to
|
||||
@@ -230,9 +212,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
}
|
||||
}//end ProcessFloatVehicleParam
|
||||
|
||||
internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue, float timestep)
|
||||
internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue)
|
||||
{
|
||||
VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue);
|
||||
VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue);
|
||||
switch (pParam)
|
||||
{
|
||||
case Vehicle.ANGULAR_FRICTION_TIMESCALE:
|
||||
@@ -267,7 +249,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
|
||||
internal void ProcessRotationVehicleParam(Vehicle pParam, Quaternion pValue)
|
||||
{
|
||||
VDetailLog("{0},ProcessRotationalVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue);
|
||||
VDetailLog("{0},ProcessRotationalVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue);
|
||||
switch (pParam)
|
||||
{
|
||||
case Vehicle.REFERENCE_FRAME:
|
||||
@@ -281,7 +263,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
|
||||
internal void ProcessVehicleFlags(int pParam, bool remove)
|
||||
{
|
||||
VDetailLog("{0},ProcessVehicleFlags,param={1},remove={2}", m_prim.LocalID, pParam, remove);
|
||||
VDetailLog("{0},ProcessVehicleFlags,param={1},remove={2}", Prim.LocalID, pParam, remove);
|
||||
VehicleFlag parm = (VehicleFlag)pParam;
|
||||
if (remove)
|
||||
{
|
||||
@@ -299,11 +281,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
}
|
||||
}//end ProcessVehicleFlags
|
||||
|
||||
internal void ProcessTypeChange(Vehicle pType, float stepSize)
|
||||
internal void ProcessTypeChange(Vehicle pType)
|
||||
{
|
||||
VDetailLog("{0},ProcessTypeChange,type={1}", m_prim.LocalID, pType);
|
||||
VDetailLog("{0},ProcessTypeChange,type={1}", Prim.LocalID, pType);
|
||||
// Set Defaults For Type
|
||||
m_type = pType;
|
||||
Type = pType;
|
||||
switch (pType)
|
||||
{
|
||||
case Vehicle.TYPE_NONE:
|
||||
@@ -370,8 +352,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
// m_bankingMix = 1;
|
||||
// m_bankingTimescale = 1;
|
||||
// m_referenceFrame = Quaternion.Identity;
|
||||
m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY |
|
||||
VehicleFlag.LIMIT_MOTOR_UP);
|
||||
m_flags |= (VehicleFlag.NO_DEFLECTION_UP
|
||||
| VehicleFlag.LIMIT_ROLL_ONLY
|
||||
| VehicleFlag.LIMIT_MOTOR_UP);
|
||||
m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT);
|
||||
m_flags |= (VehicleFlag.HOVER_UP_ONLY);
|
||||
break;
|
||||
@@ -398,12 +381,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
// m_bankingMix = 0.8f;
|
||||
// m_bankingTimescale = 1;
|
||||
// m_referenceFrame = Quaternion.Identity;
|
||||
m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY |
|
||||
VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY);
|
||||
m_flags &= ~(VehicleFlag.LIMIT_ROLL_ONLY);
|
||||
m_flags |= (VehicleFlag.NO_DEFLECTION_UP |
|
||||
VehicleFlag.LIMIT_MOTOR_UP);
|
||||
m_flags |= (VehicleFlag.HOVER_WATER_ONLY);
|
||||
m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY
|
||||
| VehicleFlag.HOVER_GLOBAL_HEIGHT
|
||||
| VehicleFlag.LIMIT_ROLL_ONLY
|
||||
| VehicleFlag.HOVER_UP_ONLY);
|
||||
m_flags |= (VehicleFlag.NO_DEFLECTION_UP
|
||||
| VehicleFlag.LIMIT_MOTOR_UP
|
||||
| VehicleFlag.HOVER_WATER_ONLY);
|
||||
break;
|
||||
case Vehicle.TYPE_AIRPLANE:
|
||||
m_linearFrictionTimescale = new Vector3(200, 10, 5);
|
||||
@@ -428,9 +412,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
// m_bankingMix = 0.7f;
|
||||
// m_bankingTimescale = 2;
|
||||
// m_referenceFrame = Quaternion.Identity;
|
||||
m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY |
|
||||
VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY);
|
||||
m_flags &= ~(VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_MOTOR_UP);
|
||||
m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
|
||||
| VehicleFlag.HOVER_TERRAIN_ONLY
|
||||
| VehicleFlag.HOVER_GLOBAL_HEIGHT
|
||||
| VehicleFlag.HOVER_UP_ONLY
|
||||
| VehicleFlag.NO_DEFLECTION_UP
|
||||
| VehicleFlag.LIMIT_MOTOR_UP);
|
||||
m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY);
|
||||
break;
|
||||
case Vehicle.TYPE_BALLOON:
|
||||
@@ -456,51 +443,66 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
// m_bankingMix = 0.7f;
|
||||
// m_bankingTimescale = 5;
|
||||
// m_referenceFrame = Quaternion.Identity;
|
||||
m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY |
|
||||
VehicleFlag.HOVER_UP_ONLY);
|
||||
m_flags &= ~(VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_MOTOR_UP);
|
||||
m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY);
|
||||
m_flags |= (VehicleFlag.HOVER_GLOBAL_HEIGHT);
|
||||
m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
|
||||
| VehicleFlag.HOVER_TERRAIN_ONLY
|
||||
| VehicleFlag.HOVER_UP_ONLY
|
||||
| VehicleFlag.NO_DEFLECTION_UP
|
||||
| VehicleFlag.LIMIT_MOTOR_UP);
|
||||
m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY
|
||||
| VehicleFlag.HOVER_GLOBAL_HEIGHT);
|
||||
break;
|
||||
}
|
||||
}//end SetDefaultsForType
|
||||
|
||||
// Some of the properties of this prim may have changed.
|
||||
// Do any updating needed for a vehicle
|
||||
public void Refresh()
|
||||
{
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
// Set the prim's inertia to zero. The vehicle code handles that and this
|
||||
// removes the torque action introduced by Bullet.
|
||||
Vector3 inertia = Vector3.Zero;
|
||||
BulletSimAPI.SetMassProps2(Prim.BSBody.ptr, Prim.MassRaw, inertia);
|
||||
BulletSimAPI.UpdateInertiaTensor2(Prim.BSBody.ptr);
|
||||
}
|
||||
|
||||
// One step of the vehicle properties for the next 'pTimestep' seconds.
|
||||
internal void Step(float pTimestep)
|
||||
{
|
||||
if (m_type == Vehicle.TYPE_NONE) return;
|
||||
|
||||
frcount++; // used to limit debug comment output
|
||||
if (frcount > 100)
|
||||
frcount = 0;
|
||||
if (!IsActive) return;
|
||||
|
||||
MoveLinear(pTimestep);
|
||||
MoveAngular(pTimestep);
|
||||
LimitRotation(pTimestep);
|
||||
|
||||
VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
|
||||
m_prim.LocalID, m_prim.Position, m_prim.Force, m_prim.Velocity, m_prim.RotationalVelocity);
|
||||
// remember the position so next step we can limit absolute movement effects
|
||||
m_lastPositionVector = Prim.ForcePosition;
|
||||
|
||||
VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
|
||||
Prim.LocalID, Prim.Position, Prim.Force, Prim.Velocity, Prim.RotationalVelocity);
|
||||
}// end Step
|
||||
|
||||
// Apply the effect of the linear motor.
|
||||
// Also does hover and float.
|
||||
private void MoveLinear(float pTimestep)
|
||||
{
|
||||
// requested m_linearMotorDirection is significant
|
||||
// if (!m_linearMotorDirection.ApproxEquals(Vector3.Zero, 0.01f))
|
||||
if (m_linearMotorDirection.LengthSquared() > 0.0001f)
|
||||
// m_linearMotorDirection is the direction we are moving relative to the vehicle coordinates
|
||||
// m_lastLinearVelocityVector is the speed we are moving in that direction
|
||||
if (m_linearMotorDirection.LengthSquared() > 0.001f)
|
||||
{
|
||||
Vector3 origDir = m_linearMotorDirection;
|
||||
Vector3 origVel = m_lastLinearVelocityVector;
|
||||
|
||||
// add drive to body
|
||||
// Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale/pTimestep);
|
||||
Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale);
|
||||
// lastLinearVelocityVector is the current body velocity vector?
|
||||
// Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale / pTimestep);
|
||||
Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/(m_linearMotorTimescale / pTimestep);
|
||||
// lastLinearVelocityVector is the current body velocity vector
|
||||
// RA: Not sure what the *10 is for. A correction for pTimestep?
|
||||
// m_lastLinearVelocityVector += (addAmount*10);
|
||||
m_lastLinearVelocityVector += addAmount;
|
||||
// m_lastLinearVelocityVector += (addAmount*10);
|
||||
m_lastLinearVelocityVector += addAmount;
|
||||
|
||||
// This will work temporarily, but we really need to compare speed on an axis
|
||||
// KF: Limit body velocity to applied velocity?
|
||||
// Limit the velocity vector to less than the last set linear motor direction
|
||||
if (Math.Abs(m_lastLinearVelocityVector.X) > Math.Abs(m_linearMotorDirectionLASTSET.X))
|
||||
m_lastLinearVelocityVector.X = m_linearMotorDirectionLASTSET.X;
|
||||
@@ -509,53 +511,108 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
if (Math.Abs(m_lastLinearVelocityVector.Z) > Math.Abs(m_linearMotorDirectionLASTSET.Z))
|
||||
m_lastLinearVelocityVector.Z = m_linearMotorDirectionLASTSET.Z;
|
||||
|
||||
// decay applied velocity
|
||||
Vector3 decayfraction = ((Vector3.One/(m_linearMotorDecayTimescale/pTimestep)));
|
||||
m_linearMotorDirection -= m_linearMotorDirection * decayfraction * 0.5f;
|
||||
|
||||
/*
|
||||
Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/m_linearMotorTimescale;
|
||||
m_lastLinearVelocityVector += addAmount;
|
||||
|
||||
float decayfraction = (1.0f - 1.0f / m_linearMotorDecayTimescale);
|
||||
m_linearMotorDirection *= decayfraction;
|
||||
|
||||
// decay applied velocity
|
||||
Vector3 decayfraction = Vector3.One/(m_linearMotorDecayTimescale / pTimestep);
|
||||
// (RA: do not know where the 0.5f comes from)
|
||||
m_linearMotorDirection -= m_linearMotorDirection * decayfraction * 0.5f;
|
||||
*/
|
||||
float keepfraction = 1.0f - (1.0f / (m_linearMotorDecayTimescale / pTimestep));
|
||||
m_linearMotorDirection *= keepfraction;
|
||||
|
||||
VDetailLog("{0},MoveLinear,nonZero,origdir={1},origvel={2},add={3},decay={4},dir={5},vel={6}",
|
||||
m_prim.LocalID, origDir, origVel, addAmount, decayfraction, m_linearMotorDirection, m_lastLinearVelocityVector);
|
||||
VDetailLog("{0},MoveLinear,nonZero,origdir={1},origvel={2},add={3},notDecay={4},dir={5},vel={6}",
|
||||
Prim.LocalID, origDir, origVel, addAmount, keepfraction, m_linearMotorDirection, m_lastLinearVelocityVector);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if what remains of applied is small, zero it.
|
||||
// if (m_lastLinearVelocityVector.ApproxEquals(Vector3.Zero, 0.01f))
|
||||
// m_lastLinearVelocityVector = Vector3.Zero;
|
||||
// if what remains of direction is very small, zero it.
|
||||
m_linearMotorDirection = Vector3.Zero;
|
||||
m_lastLinearVelocityVector = Vector3.Zero;
|
||||
VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID);
|
||||
}
|
||||
|
||||
// convert requested object velocity to object relative vector
|
||||
Quaternion rotq = m_prim.Orientation;
|
||||
m_dir = m_lastLinearVelocityVector * rotq;
|
||||
Quaternion rotq = Prim.ForceOrientation;
|
||||
m_newVelocity = m_lastLinearVelocityVector * rotq;
|
||||
|
||||
// Add the various forces into m_dir which will be our new direction vector (velocity)
|
||||
|
||||
// add Gravity and Buoyancy
|
||||
// KF: So far I have found no good method to combine a script-requested
|
||||
// .Z velocity and gravity. Therefore only 0g will used script-requested
|
||||
// .Z velocity. >0g (m_VehicleBuoyancy < 1) will used modified gravity only.
|
||||
Vector3 grav = Vector3.Zero;
|
||||
// There is some gravity, make a gravity force vector that is applied after object velocity.
|
||||
// m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
|
||||
grav.Z = m_prim.Scene.DefaultGravity.Z * m_prim.Mass * (1f - m_VehicleBuoyancy);
|
||||
Vector3 grav = Prim.PhysicsScene.DefaultGravity * (Prim.Mass * (1f - m_VehicleBuoyancy));
|
||||
|
||||
/*
|
||||
* RA: Not sure why one would do this
|
||||
// Preserve the current Z velocity
|
||||
Vector3 vel_now = m_prim.Velocity;
|
||||
m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity
|
||||
*/
|
||||
|
||||
Vector3 pos = m_prim.Position;
|
||||
Vector3 posChange = pos;
|
||||
Vector3 pos = Prim.ForcePosition;
|
||||
// Vector3 accel = new Vector3(-(m_dir.X - m_lastLinearVelocityVector.X / 0.1f), -(m_dir.Y - m_lastLinearVelocityVector.Y / 0.1f), m_dir.Z - m_lastLinearVelocityVector.Z / 0.1f);
|
||||
double Zchange = Math.Abs(posChange.Z);
|
||||
|
||||
// If below the terrain, move us above the ground a little.
|
||||
float terrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos);
|
||||
// Taking the rotated size doesn't work here because m_prim.Size is the size of the root prim and not the linkset.
|
||||
// Need to add a m_prim.LinkSet.Size similar to m_prim.LinkSet.Mass.
|
||||
// Vector3 rotatedSize = m_prim.Size * m_prim.ForceOrientation;
|
||||
// if (rotatedSize.Z < terrainHeight)
|
||||
if (pos.Z < terrainHeight)
|
||||
{
|
||||
pos.Z = terrainHeight + 2;
|
||||
Prim.ForcePosition = pos;
|
||||
VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", Prim.LocalID, terrainHeight, pos);
|
||||
}
|
||||
|
||||
// Check if hovering
|
||||
if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
|
||||
{
|
||||
// We should hover, get the target height
|
||||
if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
|
||||
{
|
||||
m_VhoverTargetHeight = Prim.PhysicsScene.GetWaterLevelAtXYZ(pos) + m_VhoverHeight;
|
||||
}
|
||||
if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
|
||||
{
|
||||
m_VhoverTargetHeight = terrainHeight + m_VhoverHeight;
|
||||
}
|
||||
if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
|
||||
{
|
||||
m_VhoverTargetHeight = m_VhoverHeight;
|
||||
}
|
||||
|
||||
if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0)
|
||||
{
|
||||
// If body is aready heigher, use its height as target height
|
||||
if (pos.Z > m_VhoverTargetHeight) m_VhoverTargetHeight = pos.Z;
|
||||
}
|
||||
if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0)
|
||||
{
|
||||
if ((pos.Z - m_VhoverTargetHeight) > .2 || (pos.Z - m_VhoverTargetHeight) < -.2)
|
||||
{
|
||||
Prim.ForcePosition = pos;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float herr0 = pos.Z - m_VhoverTargetHeight;
|
||||
// Replace Vertical speed with correction figure if significant
|
||||
if (Math.Abs(herr0) > 0.01f)
|
||||
{
|
||||
m_newVelocity.Z = -((herr0 * pTimestep * 50.0f) / m_VhoverTimescale);
|
||||
//KF: m_VhoverEfficiency is not yet implemented
|
||||
}
|
||||
else
|
||||
{
|
||||
m_newVelocity.Z = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", Prim.LocalID, pos, m_newVelocity, m_VhoverHeight, m_VhoverTargetHeight);
|
||||
}
|
||||
|
||||
Vector3 posChange = pos - m_lastPositionVector;
|
||||
if (m_BlockingEndPoint != Vector3.Zero)
|
||||
{
|
||||
bool changed = false;
|
||||
@@ -586,134 +643,56 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
m_prim.Position = pos;
|
||||
Prim.ForcePosition = pos;
|
||||
VDetailLog("{0},MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}",
|
||||
m_prim.LocalID, m_BlockingEndPoint, posChange, pos);
|
||||
Prim.LocalID, m_BlockingEndPoint, posChange, pos);
|
||||
}
|
||||
}
|
||||
|
||||
// If below the terrain, move us above the ground a little.
|
||||
if (pos.Z < m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos))
|
||||
{
|
||||
pos.Z = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos) + 2;
|
||||
m_prim.Position = pos;
|
||||
VDetailLog("{0},MoveLinear,terrainHeight,pos={1}", m_prim.LocalID, pos);
|
||||
}
|
||||
|
||||
// Check if hovering
|
||||
if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
|
||||
{
|
||||
// We should hover, get the target height
|
||||
if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
|
||||
{
|
||||
m_VhoverTargetHeight = m_prim.Scene.GetWaterLevel() + m_VhoverHeight;
|
||||
}
|
||||
if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
|
||||
{
|
||||
m_VhoverTargetHeight = m_prim.Scene.TerrainManager.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight;
|
||||
}
|
||||
if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
|
||||
{
|
||||
m_VhoverTargetHeight = m_VhoverHeight;
|
||||
}
|
||||
|
||||
if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0)
|
||||
{
|
||||
// If body is aready heigher, use its height as target height
|
||||
if (pos.Z > m_VhoverTargetHeight) m_VhoverTargetHeight = pos.Z;
|
||||
}
|
||||
if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0)
|
||||
{
|
||||
if ((pos.Z - m_VhoverTargetHeight) > .2 || (pos.Z - m_VhoverTargetHeight) < -.2)
|
||||
{
|
||||
m_prim.Position = pos;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float herr0 = pos.Z - m_VhoverTargetHeight;
|
||||
// Replace Vertical speed with correction figure if significant
|
||||
if (Math.Abs(herr0) > 0.01f)
|
||||
{
|
||||
m_dir.Z = -((herr0 * pTimestep * 50.0f) / m_VhoverTimescale);
|
||||
//KF: m_VhoverEfficiency is not yet implemented
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dir.Z = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", m_prim.LocalID, pos, m_dir, m_VhoverHeight, m_VhoverTargetHeight);
|
||||
|
||||
// m_VhoverEfficiency = 0f; // 0=boucy, 1=Crit.damped
|
||||
// m_VhoverTimescale = 0f; // time to acheive height
|
||||
// pTimestep is time since last frame,in secs
|
||||
}
|
||||
|
||||
// Limit absolute vertical change
|
||||
float Zchange = Math.Abs(posChange.Z);
|
||||
if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
|
||||
{
|
||||
//Start Experimental Values
|
||||
if (Zchange > .3)
|
||||
{
|
||||
grav.Z = (float)(grav.Z * 3);
|
||||
}
|
||||
if (Zchange > .15)
|
||||
{
|
||||
grav.Z = (float)(grav.Z * 2);
|
||||
}
|
||||
if (Zchange > .75)
|
||||
{
|
||||
grav.Z = (float)(grav.Z * 1.5);
|
||||
}
|
||||
if (Zchange > .05)
|
||||
{
|
||||
grav.Z = (float)(grav.Z * 1.25);
|
||||
}
|
||||
if (Zchange > .025)
|
||||
{
|
||||
grav.Z = (float)(grav.Z * 1.125);
|
||||
}
|
||||
float terraintemp = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos);
|
||||
float postemp = (pos.Z - terraintemp);
|
||||
float postemp = (pos.Z - terrainHeight);
|
||||
if (postemp > 2.5f)
|
||||
{
|
||||
grav.Z = (float)(grav.Z * 1.037125);
|
||||
}
|
||||
VDetailLog("{0},MoveLinear,limitMotorUp,grav={1}", m_prim.LocalID, grav);
|
||||
//End Experimental Values
|
||||
}
|
||||
if ((m_flags & (VehicleFlag.NO_X)) != 0)
|
||||
{
|
||||
m_dir.X = 0;
|
||||
}
|
||||
if ((m_flags & (VehicleFlag.NO_Y)) != 0)
|
||||
{
|
||||
m_dir.Y = 0;
|
||||
}
|
||||
if ((m_flags & (VehicleFlag.NO_Z)) != 0)
|
||||
{
|
||||
m_dir.Z = 0;
|
||||
VDetailLog("{0},MoveLinear,limitMotorUp,grav={1}", Prim.LocalID, grav);
|
||||
}
|
||||
|
||||
m_lastPositionVector = m_prim.Position;
|
||||
// If not changing some axis, reduce out velocity
|
||||
if ((m_flags & (VehicleFlag.NO_X)) != 0)
|
||||
m_newVelocity.X = 0;
|
||||
if ((m_flags & (VehicleFlag.NO_Y)) != 0)
|
||||
m_newVelocity.Y = 0;
|
||||
if ((m_flags & (VehicleFlag.NO_Z)) != 0)
|
||||
m_newVelocity.Z = 0;
|
||||
|
||||
// Apply velocity
|
||||
m_prim.Velocity = m_dir;
|
||||
Prim.Velocity = m_newVelocity;
|
||||
// apply gravity force
|
||||
// Why is this set here? The physics engine already does gravity.
|
||||
// m_prim.AddForce(grav, false);
|
||||
// m_prim.Force = grav;
|
||||
|
||||
// Apply friction
|
||||
Vector3 decayamount = Vector3.One / (m_linearFrictionTimescale / pTimestep);
|
||||
m_lastLinearVelocityVector -= m_lastLinearVelocityVector * decayamount;
|
||||
Vector3 keepFraction = Vector3.One - (Vector3.One / (m_linearFrictionTimescale / pTimestep));
|
||||
m_lastLinearVelocityVector *= keepFraction;
|
||||
|
||||
VDetailLog("{0},MoveLinear,done,pos={1},vel={2},force={3},decay={4}",
|
||||
m_prim.LocalID, m_lastPositionVector, m_dir, grav, decayamount);
|
||||
VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},grav={4},1Mdecay={5}",
|
||||
Prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity, grav, keepFraction);
|
||||
|
||||
} // end MoveLinear()
|
||||
|
||||
// Apply the effect of the angular motor.
|
||||
private void MoveAngular(float pTimestep)
|
||||
{
|
||||
// m_angularMotorDirection // angular velocity requested by LSL motor
|
||||
@@ -725,7 +704,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
// m_lastAngularVelocity // what was last applied to body
|
||||
|
||||
// Get what the body is doing, this includes 'external' influences
|
||||
Vector3 angularVelocity = m_prim.RotationalVelocity;
|
||||
Vector3 angularVelocity = Prim.RotationalVelocity;
|
||||
|
||||
if (m_angularMotorApply > 0)
|
||||
{
|
||||
@@ -733,19 +712,20 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
// a newly set velocity, this routine steps the value from the previous
|
||||
// value (m_angularMotorVelocity) to the requested value (m_angularMotorDirection).
|
||||
// There are m_angularMotorApply steps.
|
||||
Vector3 origAngularVelocity = m_angularMotorVelocity;
|
||||
Vector3 origVel = m_angularMotorVelocity;
|
||||
Vector3 origDir = m_angularMotorDirection;
|
||||
|
||||
// ramp up to new value
|
||||
// current velocity += error / (time to get there / step interval)
|
||||
// requested speed - last motor speed
|
||||
// new velocity += error / ( time to get there / step interval)
|
||||
// requested speed - last motor speed
|
||||
m_angularMotorVelocity.X += (m_angularMotorDirection.X - m_angularMotorVelocity.X) / (m_angularMotorTimescale / pTimestep);
|
||||
m_angularMotorVelocity.Y += (m_angularMotorDirection.Y - m_angularMotorVelocity.Y) / (m_angularMotorTimescale / pTimestep);
|
||||
m_angularMotorVelocity.Z += (m_angularMotorDirection.Z - m_angularMotorVelocity.Z) / (m_angularMotorTimescale / pTimestep);
|
||||
|
||||
VDetailLog("{0},MoveAngular,angularMotorApply,apply={1},origvel={2},dir={3},vel={4}",
|
||||
m_prim.LocalID,m_angularMotorApply,origAngularVelocity, m_angularMotorDirection, m_angularMotorVelocity);
|
||||
VDetailLog("{0},MoveAngular,angularMotorApply,apply={1},angTScale={2},timeStep={3},origvel={4},origDir={5},vel={6}",
|
||||
Prim.LocalID, m_angularMotorApply, m_angularMotorTimescale, pTimestep, origVel, origDir, m_angularMotorVelocity);
|
||||
|
||||
m_angularMotorApply--; // This is done so that if script request rate is less than phys frame rate the expected
|
||||
// velocity may still be acheived.
|
||||
m_angularMotorApply--;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -758,25 +738,32 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
|
||||
// Vertical attractor section
|
||||
Vector3 vertattr = Vector3.Zero;
|
||||
if (m_verticalAttractionTimescale < 300)
|
||||
Vector3 deflection = Vector3.Zero;
|
||||
Vector3 banking = Vector3.Zero;
|
||||
|
||||
if (m_verticalAttractionTimescale < 300 && m_lastAngularVelocity != Vector3.Zero)
|
||||
{
|
||||
float VAservo = 0.2f / (m_verticalAttractionTimescale * pTimestep);
|
||||
float VAservo = 0.2f / (m_verticalAttractionTimescale / pTimestep);
|
||||
VAservo *= (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
|
||||
|
||||
// get present body rotation
|
||||
Quaternion rotq = m_prim.Orientation;
|
||||
// make a vector pointing up
|
||||
Quaternion rotq = Prim.ForceOrientation;
|
||||
// vector pointing up
|
||||
Vector3 verterr = Vector3.Zero;
|
||||
verterr.Z = 1.0f;
|
||||
|
||||
// rotate it to Body Angle
|
||||
verterr = verterr * rotq;
|
||||
// verterr.X and .Y are the World error ammounts. They are 0 when there is no error (Vehicle Body is 'vertical'), and .Z will be 1.
|
||||
// verterr.X and .Y are the World error amounts. They are 0 when there is no error (Vehicle Body is 'vertical'), and .Z will be 1.
|
||||
// As the body leans to its side |.X| will increase to 1 and .Z fall to 0. As body inverts |.X| will fall and .Z will go
|
||||
// negative. Similar for tilt and |.Y|. .X and .Y must be modulated to prevent a stable inverted body.
|
||||
|
||||
// Error is 0 (no error) to +/- 2 (max error)
|
||||
if (verterr.Z < 0.0f)
|
||||
{
|
||||
verterr.X = 2.0f - verterr.X;
|
||||
verterr.Y = 2.0f - verterr.Y;
|
||||
}
|
||||
// Error is 0 (no error) to +/- 2 (max error)
|
||||
// scale it by VAservo
|
||||
verterr = verterr * VAservo;
|
||||
|
||||
@@ -791,12 +778,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
vertattr.X += bounce * angularVelocity.X;
|
||||
vertattr.Y += bounce * angularVelocity.Y;
|
||||
|
||||
VDetailLog("{0},MoveAngular,verticalAttraction,verterr={1},bounce={2},vertattr={3}",
|
||||
m_prim.LocalID, verterr, bounce, vertattr);
|
||||
VDetailLog("{0},MoveAngular,verticalAttraction,verterr={1},bounce={2},vertattr={3}",
|
||||
Prim.LocalID, verterr, bounce, vertattr);
|
||||
|
||||
} // else vertical attractor is off
|
||||
|
||||
// m_lastVertAttractor = vertattr;
|
||||
m_lastVertAttractor = vertattr;
|
||||
|
||||
// Bank section tba
|
||||
|
||||
@@ -804,18 +791,18 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
|
||||
// Sum velocities
|
||||
m_lastAngularVelocity = m_angularMotorVelocity + vertattr; // + bank + deflection
|
||||
|
||||
|
||||
if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
|
||||
{
|
||||
m_lastAngularVelocity.X = 0;
|
||||
m_lastAngularVelocity.Y = 0;
|
||||
VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", m_prim.LocalID, m_lastAngularVelocity);
|
||||
VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity);
|
||||
}
|
||||
|
||||
if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f))
|
||||
{
|
||||
m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero.
|
||||
VDetailLog("{0},MoveAngular,zeroSmallValues,lastAngular={1}", m_prim.LocalID, m_lastAngularVelocity);
|
||||
VDetailLog("{0},MoveAngular,zeroSmallValues,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity);
|
||||
}
|
||||
|
||||
// apply friction
|
||||
@@ -823,14 +810,14 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
m_lastAngularVelocity -= m_lastAngularVelocity * decayamount;
|
||||
|
||||
// Apply to the body
|
||||
m_prim.RotationalVelocity = m_lastAngularVelocity;
|
||||
Prim.RotationalVelocity = m_lastAngularVelocity;
|
||||
|
||||
VDetailLog("{0},MoveAngular,done,decay={1},lastAngular={2}", m_prim.LocalID, decayamount, m_lastAngularVelocity);
|
||||
VDetailLog("{0},MoveAngular,done,decay={1},lastAngular={2}", Prim.LocalID, decayamount, m_lastAngularVelocity);
|
||||
} //end MoveAngular
|
||||
|
||||
internal void LimitRotation(float timestep)
|
||||
{
|
||||
Quaternion rotq = m_prim.Orientation;
|
||||
Quaternion rotq = Prim.ForceOrientation;
|
||||
Quaternion m_rot = rotq;
|
||||
bool changed = false;
|
||||
if (m_RollreferenceFrame != Quaternion.Identity)
|
||||
@@ -863,23 +850,19 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
m_rot.Y = 0;
|
||||
changed = true;
|
||||
}
|
||||
if ((m_flags & VehicleFlag.LOCK_ROTATION) != 0)
|
||||
{
|
||||
m_rot.X = 0;
|
||||
m_rot.Y = 0;
|
||||
changed = true;
|
||||
}
|
||||
if (changed)
|
||||
m_prim.Orientation = m_rot;
|
||||
{
|
||||
Prim.ForceOrientation = m_rot;
|
||||
VDetailLog("{0},LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot);
|
||||
}
|
||||
|
||||
VDetailLog("{0},LimitRotation,done,changed={1},orig={2},new={3}", m_prim.LocalID, changed, rotq, m_rot);
|
||||
}
|
||||
|
||||
// Invoke the detailed logger and output something if it's enabled.
|
||||
private void VDetailLog(string msg, params Object[] args)
|
||||
{
|
||||
if (m_prim.Scene.VehicleLoggingEnabled)
|
||||
m_prim.Scene.PhysicsLogging.Write(msg, args);
|
||||
if (Prim.PhysicsScene.VehicleLoggingEnabled)
|
||||
Prim.PhysicsScene.PhysicsLogging.Write(msg, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,57 @@
|
||||
/*
|
||||
* 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 copyrightD
|
||||
* 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.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
{
|
||||
|
||||
class BSHingeConstraint : BSConstraint
|
||||
{
|
||||
public BSHingeConstraint(BulletSim world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 pivotInA, Vector3 pivotInB,
|
||||
Vector3 axisInA, Vector3 axisInB,
|
||||
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
|
||||
{
|
||||
m_world = world;
|
||||
m_body1 = obj1;
|
||||
m_body2 = obj2;
|
||||
m_constraint = new BulletConstraint(
|
||||
BulletSimAPI.CreateHingeConstraint2(m_world.Ptr, m_body1.Ptr, m_body2.Ptr,
|
||||
pivotInA, pivotInB,
|
||||
axisInA, axisInB,
|
||||
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
|
||||
m_enabled = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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 copyrightD
|
||||
* 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.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
{
|
||||
|
||||
class BSHingeConstraint : BSConstraint
|
||||
{
|
||||
public override ConstraintType Type { get { return ConstraintType.HINGE_CONSTRAINT_TYPE; } }
|
||||
|
||||
public BSHingeConstraint(BulletSim world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 pivotInA, Vector3 pivotInB,
|
||||
Vector3 axisInA, Vector3 axisInB,
|
||||
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
|
||||
{
|
||||
m_world = world;
|
||||
m_body1 = obj1;
|
||||
m_body2 = obj2;
|
||||
m_constraint = new BulletConstraint(
|
||||
BulletSimAPI.CreateHingeConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
|
||||
pivotInA, pivotInB,
|
||||
axisInA, axisInB,
|
||||
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
|
||||
m_enabled = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,19 +34,26 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
{
|
||||
public class BSLinkset
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM LINKSET]";
|
||||
// private static string LogHeader = "[BULLETSIM LINKSET]";
|
||||
|
||||
private BSPhysObject m_linksetRoot;
|
||||
public BSPhysObject LinksetRoot { get { return m_linksetRoot; } }
|
||||
public BSPhysObject LinksetRoot { get; protected set; }
|
||||
|
||||
private BSScene m_physicsScene;
|
||||
public BSScene PhysicsScene { get { return m_physicsScene; } }
|
||||
public BSScene PhysicsScene { get; private set; }
|
||||
|
||||
static int m_nextLinksetID = 1;
|
||||
public int LinksetID { get; private set; }
|
||||
|
||||
// The children under the root in this linkset
|
||||
// The children under the root in this linkset.
|
||||
// There are two lists of children: the current children at runtime
|
||||
// and the children at taint-time. For instance, if you delink a
|
||||
// child from the linkset, the child is removed from m_children
|
||||
// but the constraint won't be removed until taint time.
|
||||
// Two lists lets this track the 'current' children and
|
||||
// the physical 'taint' children separately.
|
||||
// After taint processing and before the simulation step, these
|
||||
// two lists must be the same.
|
||||
private List<BSPhysObject> m_children;
|
||||
private List<BSPhysObject> m_taintChildren;
|
||||
|
||||
// We lock the diddling of linkset classes to prevent any badness.
|
||||
// This locks the modification of the instances of this class. Changes
|
||||
@@ -55,9 +62,9 @@ public class BSLinkset
|
||||
|
||||
// We keep the prim's mass in the linkset structure since it could be dependent on other prims
|
||||
private float m_mass;
|
||||
public float LinksetMass
|
||||
{
|
||||
get
|
||||
public float LinksetMass
|
||||
{
|
||||
get
|
||||
{
|
||||
m_mass = ComputeLinksetMass();
|
||||
return m_mass;
|
||||
@@ -79,22 +86,26 @@ public class BSLinkset
|
||||
// A simple linkset of one (no children)
|
||||
LinksetID = m_nextLinksetID++;
|
||||
// We create LOTS of linksets.
|
||||
if (m_nextLinksetID < 0)
|
||||
if (m_nextLinksetID <= 0)
|
||||
m_nextLinksetID = 1;
|
||||
m_physicsScene = scene;
|
||||
m_linksetRoot = parent;
|
||||
PhysicsScene = scene;
|
||||
LinksetRoot = parent;
|
||||
m_children = new List<BSPhysObject>();
|
||||
m_taintChildren = new List<BSPhysObject>();
|
||||
m_mass = parent.MassRaw;
|
||||
}
|
||||
|
||||
// Link to a linkset where the child knows the parent.
|
||||
// Parent changing should not happen so do some sanity checking.
|
||||
// We return the parent's linkset so the child can track its membership.
|
||||
// Called at runtime.
|
||||
public BSLinkset AddMeToLinkset(BSPhysObject child)
|
||||
{
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
AddChildToLinkset(child);
|
||||
// Don't add the root to its own linkset
|
||||
if (!IsRoot(child))
|
||||
AddChildToLinkset(child);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -102,26 +113,18 @@ public class BSLinkset
|
||||
// Remove a child from a linkset.
|
||||
// Returns a new linkset for the child which is a linkset of one (just the
|
||||
// orphened child).
|
||||
// Called at runtime.
|
||||
public BSLinkset RemoveMeFromLinkset(BSPhysObject child)
|
||||
{
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
if (IsRoot(child))
|
||||
{
|
||||
// if root of linkset, take the linkset apart
|
||||
while (m_children.Count > 0)
|
||||
{
|
||||
// Note that we don't do a foreach because the remove routine
|
||||
// takes it out of the list.
|
||||
RemoveChildFromOtherLinkset(m_children[0]);
|
||||
}
|
||||
m_children.Clear(); // just to make sure
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just removing a child from an existing linkset
|
||||
RemoveChildFromLinkset(child);
|
||||
// Cannot remove the root from a linkset.
|
||||
return this;
|
||||
}
|
||||
|
||||
RemoveChildFromLinkset(child);
|
||||
}
|
||||
|
||||
// The child is down to a linkset of just itself
|
||||
@@ -131,7 +134,7 @@ public class BSLinkset
|
||||
// Return 'true' if the passed object is the root object of this linkset
|
||||
public bool IsRoot(BSPhysObject requestor)
|
||||
{
|
||||
return (requestor.LocalID == m_linksetRoot.LocalID);
|
||||
return (requestor.LocalID == LinksetRoot.LocalID);
|
||||
}
|
||||
|
||||
public int NumberOfChildren { get { return m_children.Count; } }
|
||||
@@ -157,56 +160,34 @@ public class BSLinkset
|
||||
return ret;
|
||||
}
|
||||
|
||||
private float ComputeLinksetMass()
|
||||
// The object is going dynamic (physical). Do any setup necessary
|
||||
// for a dynamic linkset.
|
||||
// Only the state of the passed object can be modified. The rest of the linkset
|
||||
// has not yet been fully constructed.
|
||||
// Return 'true' if any properties updated on the passed object.
|
||||
// Called at taint-time!
|
||||
public bool MakeDynamic(BSPhysObject child)
|
||||
{
|
||||
float mass = m_linksetRoot.MassRaw;
|
||||
foreach (BSPhysObject bp in m_children)
|
||||
{
|
||||
mass += bp.MassRaw;
|
||||
}
|
||||
return mass;
|
||||
// What is done for each object in BSPrim is what we want.
|
||||
return false;
|
||||
}
|
||||
|
||||
private OMV.Vector3 ComputeLinksetCenterOfMass()
|
||||
// The object is going static (non-physical). Do any setup necessary
|
||||
// for a static linkset.
|
||||
// Return 'true' if any properties updated on the passed object.
|
||||
// Called at taint-time!
|
||||
public bool MakeStatic(BSPhysObject child)
|
||||
{
|
||||
OMV.Vector3 com = m_linksetRoot.Position * m_linksetRoot.MassRaw;
|
||||
float totalMass = m_linksetRoot.MassRaw;
|
||||
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
foreach (BSPhysObject bp in m_children)
|
||||
{
|
||||
com += bp.Position * bp.MassRaw;
|
||||
totalMass += bp.MassRaw;
|
||||
}
|
||||
if (totalMass != 0f)
|
||||
com /= totalMass;
|
||||
}
|
||||
|
||||
return com;
|
||||
}
|
||||
|
||||
private OMV.Vector3 ComputeLinksetGeometricCenter()
|
||||
{
|
||||
OMV.Vector3 com = m_linksetRoot.Position;
|
||||
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
foreach (BSPhysObject bp in m_children)
|
||||
{
|
||||
com += bp.Position * bp.MassRaw;
|
||||
}
|
||||
com /= (m_children.Count + 1);
|
||||
}
|
||||
|
||||
return com;
|
||||
// What is done for each object in BSPrim is what we want.
|
||||
return false;
|
||||
}
|
||||
|
||||
// When physical properties are changed the linkset needs to recalculate
|
||||
// its internal properties.
|
||||
// Called at runtime.
|
||||
public void Refresh(BSPhysObject requestor)
|
||||
{
|
||||
// If there are no children, there aren't any constraints to recompute
|
||||
// If there are no children, there can't be any constraints to recompute
|
||||
if (!HasAnyChildren)
|
||||
return;
|
||||
|
||||
@@ -220,37 +201,125 @@ public class BSLinkset
|
||||
}
|
||||
}
|
||||
|
||||
// Call each of the constraints that make up this linkset and recompute the
|
||||
// various transforms and variables. Used when objects are added or removed
|
||||
// from a linkset to make sure the constraints know about the new mass and
|
||||
// geometry.
|
||||
// Must only be called at taint time!!
|
||||
private bool RecomputeLinksetConstraintVariables()
|
||||
// Routine used when rebuilding the body of the root of the linkset
|
||||
// Destroy all the constraints have have been made to root.
|
||||
// This is called when the root body is changing.
|
||||
// Returns 'true' of something eas actually removed and would need restoring
|
||||
// Called at taint-time!!
|
||||
public bool RemoveBodyDependencies(BSPrim child)
|
||||
{
|
||||
float linksetMass = LinksetMass;
|
||||
bool ret = false;
|
||||
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
foreach (BSPhysObject child in m_children)
|
||||
if (IsRoot(child))
|
||||
{
|
||||
BSConstraint constrain;
|
||||
if (m_physicsScene.Constraints.TryGetConstraint(LinksetRoot.Body, child.Body, out constrain))
|
||||
// If the one with the dependency is root, must undo all children
|
||||
DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},numChild={2}",
|
||||
child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count);
|
||||
foreach (BSPhysObject bpo in m_taintChildren)
|
||||
{
|
||||
// DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,taint,child={1},mass={2},A={3},B={4}",
|
||||
// LinksetRoot.LocalID, child.LocalID, linksetMass, constrain.Body1.ID, constrain.Body2.ID);
|
||||
constrain.RecomputeConstraintVariables(linksetMass);
|
||||
PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody);
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeSingleChild,rID={1},rBody={2},cID={3},cBody={4}",
|
||||
child.LocalID,
|
||||
LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
|
||||
child.LocalID, child.BSBody.ptr.ToString("X"));
|
||||
// Remove the dependency on the body of this one
|
||||
if (m_taintChildren.Contains(child))
|
||||
{
|
||||
// Non-fatal error that can happen when children are being added to the linkset but
|
||||
// their constraints have not been created yet.
|
||||
// Caused by the fact that m_children is built at run time but building constraints
|
||||
// happens at taint time.
|
||||
// m_physicsScene.Logger.ErrorFormat("[BULLETSIM LINKSET] RecomputeLinksetConstraintVariables: constraint not found for root={0}, child={1}",
|
||||
// m_linksetRoot.Body.ID, child.Body.ID);
|
||||
PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, child, child.BSBody);
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Routine used when rebuilding the body of the root of the linkset
|
||||
// This is called after RemoveAllLinksToRoot() to restore all the constraints.
|
||||
// This is called when the root body has been changed.
|
||||
// Called at taint-time!!
|
||||
public void RestoreBodyDependencies(BSPrim child)
|
||||
{
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
if (IsRoot(child))
|
||||
{
|
||||
DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreChildrenForRoot,rID={1},numChild={2}",
|
||||
child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count);
|
||||
foreach (BSPhysObject bpo in m_taintChildren)
|
||||
{
|
||||
PhysicallyLinkAChildToRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreSingleChild,rID={1},rBody={2},cID={3},cBody={4}",
|
||||
LinksetRoot.LocalID,
|
||||
LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
|
||||
child.LocalID, child.BSBody.ptr.ToString("X"));
|
||||
PhysicallyLinkAChildToRoot(LinksetRoot, LinksetRoot.BSBody, child, child.BSBody);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Below this point is internal magic
|
||||
|
||||
private float ComputeLinksetMass()
|
||||
{
|
||||
float mass;
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
mass = LinksetRoot.MassRaw;
|
||||
foreach (BSPhysObject bp in m_taintChildren)
|
||||
{
|
||||
mass += bp.MassRaw;
|
||||
}
|
||||
}
|
||||
return mass;
|
||||
}
|
||||
|
||||
private OMV.Vector3 ComputeLinksetCenterOfMass()
|
||||
{
|
||||
OMV.Vector3 com;
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
com = LinksetRoot.Position * LinksetRoot.MassRaw;
|
||||
float totalMass = LinksetRoot.MassRaw;
|
||||
|
||||
foreach (BSPhysObject bp in m_taintChildren)
|
||||
{
|
||||
com += bp.Position * bp.MassRaw;
|
||||
totalMass += bp.MassRaw;
|
||||
}
|
||||
if (totalMass != 0f)
|
||||
com /= totalMass;
|
||||
}
|
||||
|
||||
return com;
|
||||
}
|
||||
|
||||
private OMV.Vector3 ComputeLinksetGeometricCenter()
|
||||
{
|
||||
OMV.Vector3 com;
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
com = LinksetRoot.Position;
|
||||
|
||||
foreach (BSPhysObject bp in m_taintChildren)
|
||||
{
|
||||
com += bp.Position * bp.MassRaw;
|
||||
}
|
||||
com /= (m_taintChildren.Count + 1);
|
||||
}
|
||||
|
||||
return com;
|
||||
}
|
||||
|
||||
// I am the root of a linkset and a new child is being added
|
||||
@@ -261,12 +330,22 @@ public class BSLinkset
|
||||
{
|
||||
m_children.Add(child);
|
||||
|
||||
BSPhysObject rootx = LinksetRoot; // capture the root as of now
|
||||
BSPhysObject rootx = LinksetRoot; // capture the root and body as of now
|
||||
BSPhysObject childx = child;
|
||||
m_physicsScene.TaintedObject("AddChildToLinkset", delegate()
|
||||
|
||||
DetailLog("{0},AddChildToLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
|
||||
rootx.LocalID,
|
||||
rootx.LocalID, rootx.BSBody.ptr.ToString("X"),
|
||||
childx.LocalID, childx.BSBody.ptr.ToString("X"));
|
||||
|
||||
PhysicsScene.TaintedObject("AddChildToLinkset", delegate()
|
||||
{
|
||||
DetailLog("{0},AddChildToLinkset,taint,child={1}", m_linksetRoot.LocalID, child.LocalID);
|
||||
PhysicallyLinkAChildToRoot(rootx, childx); // build the physical binding between me and the child
|
||||
DetailLog("{0},AddChildToLinkset,taint,child={1}", LinksetRoot.LocalID, child.LocalID);
|
||||
// build the physical binding between me and the child
|
||||
m_taintChildren.Add(childx);
|
||||
|
||||
// Since this is taint-time, the body and shape could have changed for the child
|
||||
PhysicallyLinkAChildToRoot(rootx, rootx.BSBody, childx, childx.BSBody);
|
||||
});
|
||||
}
|
||||
return;
|
||||
@@ -276,10 +355,10 @@ public class BSLinkset
|
||||
// This is not being called by the child so we have to make sure the child doesn't think
|
||||
// it's still connected to the linkset.
|
||||
// Normal OpenSimulator operation will never do this because other SceneObjectPart information
|
||||
// has to be updated also (like pointer to prim's parent).
|
||||
// also has to be updated (like pointer to prim's parent).
|
||||
private void RemoveChildFromOtherLinkset(BSPhysObject pchild)
|
||||
{
|
||||
pchild.Linkset = new BSLinkset(m_physicsScene, pchild);
|
||||
pchild.Linkset = new BSLinkset(PhysicsScene, pchild);
|
||||
RemoveChildFromLinkset(pchild);
|
||||
}
|
||||
|
||||
@@ -289,16 +368,23 @@ public class BSLinkset
|
||||
{
|
||||
if (m_children.Remove(child))
|
||||
{
|
||||
BSPhysObject rootx = LinksetRoot; // capture the root as of now
|
||||
BSPhysObject rootx = LinksetRoot; // capture the root and body as of now
|
||||
BSPhysObject childx = child;
|
||||
m_physicsScene.TaintedObject("RemoveChildFromLinkset", delegate()
|
||||
{
|
||||
DetailLog("{0},RemoveChildFromLinkset,taint,child={1}", m_linksetRoot.LocalID, child.LocalID);
|
||||
|
||||
PhysicallyUnlinkAChildFromRoot(rootx, childx);
|
||||
DetailLog("{0},RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
|
||||
childx.LocalID,
|
||||
rootx.LocalID, rootx.BSBody.ptr.ToString("X"),
|
||||
childx.LocalID, childx.BSBody.ptr.ToString("X"));
|
||||
|
||||
PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate()
|
||||
{
|
||||
if (m_taintChildren.Contains(childx))
|
||||
m_taintChildren.Remove(childx);
|
||||
|
||||
PhysicallyUnlinkAChildFromRoot(rootx, rootx.BSBody, childx, childx.BSBody);
|
||||
RecomputeLinksetConstraintVariables();
|
||||
});
|
||||
|
||||
RecomputeLinksetConstraintVariables();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -310,7 +396,8 @@ public class BSLinkset
|
||||
|
||||
// Create a constraint between me (root of linkset) and the passed prim (the child).
|
||||
// Called at taint time!
|
||||
private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
|
||||
private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BulletBody rootBody,
|
||||
BSPhysObject childPrim, BulletBody childBody)
|
||||
{
|
||||
// Zero motion for children so they don't interpolate
|
||||
childPrim.ZeroMotion();
|
||||
@@ -322,18 +409,36 @@ public class BSLinkset
|
||||
// real world coordinate of midpoint between the two objects
|
||||
OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);
|
||||
|
||||
DetailLog("{0},PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}",
|
||||
rootPrim.LocalID,
|
||||
rootPrim.LocalID, rootBody.ptr.ToString("X"),
|
||||
childPrim.LocalID, childBody.ptr.ToString("X"),
|
||||
rootPrim.Position, childPrim.Position, midPoint);
|
||||
|
||||
// create a constraint that allows no freedom of movement between the two objects
|
||||
// http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
|
||||
DetailLog("{0},PhysicallyLinkAChildToRoot,taint,root={1},child={2},rLoc={3},cLoc={4},midLoc={5}",
|
||||
rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID, rootPrim.Position, childPrim.Position, midPoint);
|
||||
|
||||
// There is great subtlty in these paramters. Notice the check for a ptr of zero.
|
||||
// We pass the BulletBody structure into the taint in order to capture the pointer
|
||||
// of the body at the time of constraint creation. This doesn't work for the very first
|
||||
// construction because there is no body yet. The body
|
||||
// is constructed later at taint time. Thus we use the body address at time of the
|
||||
// taint creation but, if it is zero, use what's in the prim at the moment.
|
||||
// There is a possible race condition since shape can change without a taint call
|
||||
// (like changing to a mesh that is already constructed). The fix for that would be
|
||||
// to only change BSShape at taint time thus syncronizing these operations at
|
||||
// the cost of efficiency and lag.
|
||||
BS6DofConstraint constrain = new BS6DofConstraint(
|
||||
m_physicsScene.World, rootPrim.Body, childPrim.Body,
|
||||
PhysicsScene.World,
|
||||
rootBody.ptr == IntPtr.Zero ? rootPrim.BSBody : rootBody,
|
||||
childBody.ptr == IntPtr.Zero ? childPrim.BSBody : childBody,
|
||||
midPoint,
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
/* NOTE: below is an attempt to build constraint with full frame computation, etc.
|
||||
* Using the midpoint is easier since it lets the Bullet code use the transforms
|
||||
* Using the midpoint is easier since it lets the Bullet code manipulate the transforms
|
||||
* of the objects.
|
||||
* Code left as a warning to future programmers.
|
||||
// ==================================================================================
|
||||
@@ -365,7 +470,7 @@ public class BSLinkset
|
||||
// ==================================================================================
|
||||
*/
|
||||
|
||||
m_physicsScene.Constraints.AddConstraint(constrain);
|
||||
PhysicsScene.Constraints.AddConstraint(constrain);
|
||||
|
||||
// zero linear and angular limits makes the objects unable to move in relation to each other
|
||||
constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
|
||||
@@ -377,36 +482,88 @@ public class BSLinkset
|
||||
PhysicsScene.Params.linkConstraintTransMotorMaxVel,
|
||||
PhysicsScene.Params.linkConstraintTransMotorMaxForce);
|
||||
constrain.SetCFMAndERP(PhysicsScene.Params.linkConstraintCFM, PhysicsScene.Params.linkConstraintERP);
|
||||
if (PhysicsScene.Params.linkConstraintSolverIterations != 0f)
|
||||
{
|
||||
constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations);
|
||||
}
|
||||
|
||||
RecomputeLinksetConstraintVariables();
|
||||
}
|
||||
|
||||
// Remove linkage between myself and a particular child
|
||||
// The root and child bodies are passed in because we need to remove the constraint between
|
||||
// the bodies that were at unlink time.
|
||||
// Called at taint time!
|
||||
private void PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
|
||||
private void PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BulletBody rootBody,
|
||||
BSPhysObject childPrim, BulletBody childBody)
|
||||
{
|
||||
DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID);
|
||||
DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}",
|
||||
rootPrim.LocalID,
|
||||
rootPrim.LocalID, rootBody.ptr.ToString("X"),
|
||||
childPrim.LocalID, childBody.ptr.ToString("X"));
|
||||
|
||||
// Find the constraint for this link and get rid of it from the overall collection and from my list
|
||||
m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.Body, childPrim.Body);
|
||||
PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootBody, childBody);
|
||||
|
||||
// Make the child refresh its location
|
||||
BulletSimAPI.PushUpdate2(childPrim.Body.Ptr);
|
||||
BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr);
|
||||
}
|
||||
|
||||
// Remove linkage between myself and any possible children I might have
|
||||
/*
|
||||
// Remove linkage between myself and any possible children I might have.
|
||||
// Called at taint time!
|
||||
private void PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim)
|
||||
{
|
||||
DetailLog("{0},PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID);
|
||||
|
||||
m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.Body);
|
||||
PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody);
|
||||
}
|
||||
*/
|
||||
|
||||
// Call each of the constraints that make up this linkset and recompute the
|
||||
// various transforms and variables. Used when objects are added or removed
|
||||
// from a linkset to make sure the constraints know about the new mass and
|
||||
// geometry.
|
||||
// Must only be called at taint time!!
|
||||
private void RecomputeLinksetConstraintVariables()
|
||||
{
|
||||
float linksetMass = LinksetMass;
|
||||
foreach (BSPhysObject child in m_taintChildren)
|
||||
{
|
||||
BSConstraint constrain;
|
||||
if (PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.BSBody, child.BSBody, out constrain))
|
||||
{
|
||||
// DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,taint,child={1},mass={2},A={3},B={4}",
|
||||
// LinksetRoot.LocalID, child.LocalID, linksetMass, constrain.Body1.ID, constrain.Body2.ID);
|
||||
constrain.RecomputeConstraintVariables(linksetMass);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-fatal error that happens when children are being added to the linkset but
|
||||
// their constraints have not been created yet.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the whole linkset is not here, doesn't make sense to recompute linkset wide values
|
||||
if (m_children.Count == m_taintChildren.Count)
|
||||
{
|
||||
// If this is a multiple object linkset, set everybody's center of mass to the set's center of mass
|
||||
OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass();
|
||||
BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.ptr, centerOfMass, OMV.Quaternion.Identity);
|
||||
foreach (BSPhysObject child in m_taintChildren)
|
||||
{
|
||||
BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.ptr, centerOfMass, OMV.Quaternion.Identity);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Invoke the detailed logger and output something if it's enabled.
|
||||
private void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
m_physicsScene.PhysicsLogging.Write(msg, args);
|
||||
PhysicsScene.PhysicsLogging.Write(msg, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
@@ -39,22 +39,175 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
// unless the difference is significant.
|
||||
public abstract class BSPhysObject : PhysicsActor
|
||||
{
|
||||
public abstract BSLinkset Linkset { get; set; }
|
||||
protected void BaseInitialize(BSScene parentScene, uint localID, string name, string typeName)
|
||||
{
|
||||
PhysicsScene = parentScene;
|
||||
LocalID = localID;
|
||||
PhysObjectName = name;
|
||||
TypeName = typeName;
|
||||
|
||||
public abstract void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type,
|
||||
OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth);
|
||||
public abstract void SendCollisions();
|
||||
Linkset = new BSLinkset(PhysicsScene, this);
|
||||
|
||||
// Return the object mass without calculating it or side effects
|
||||
CollisionCollection = new CollisionEventUpdate();
|
||||
SubscribedEventsMs = 0;
|
||||
CollidingStep = 0;
|
||||
CollidingGroundStep = 0;
|
||||
}
|
||||
|
||||
public BSScene PhysicsScene { get; protected set; }
|
||||
// public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor
|
||||
public string PhysObjectName { get; protected set; }
|
||||
public string TypeName { get; protected set; }
|
||||
|
||||
public BSLinkset Linkset { get; set; }
|
||||
|
||||
// Return the object mass without calculating it or having side effects
|
||||
public abstract float MassRaw { get; }
|
||||
|
||||
public abstract BulletBody Body { get; set; }
|
||||
// Reference to the physical body (btCollisionObject) of this object
|
||||
public BulletBody BSBody;
|
||||
// Reference to the physical shape (btCollisionShape) of this object
|
||||
public BulletShape BSShape;
|
||||
|
||||
// Stop all physical motion.
|
||||
public abstract void ZeroMotion();
|
||||
|
||||
// Step the vehicle simulation for this object. A NOOP if the vehicle was not configured.
|
||||
public virtual void StepVehicle(float timeStep) { }
|
||||
|
||||
// Update the physical location and motion of the object. Called with data from Bullet.
|
||||
public abstract void UpdateProperties(EntityProperties entprop);
|
||||
|
||||
// Tell the object to clean up.
|
||||
public abstract void Destroy();
|
||||
|
||||
public abstract OMV.Vector3 ForcePosition { get; set; }
|
||||
|
||||
public abstract OMV.Quaternion ForceOrientation { get; set; }
|
||||
|
||||
#region Collisions
|
||||
|
||||
// Requested number of milliseconds between collision events. Zero means disabled.
|
||||
protected int SubscribedEventsMs { get; set; }
|
||||
// Given subscription, the time that a collision may be passed up
|
||||
protected int NextCollisionOkTime { get; set; }
|
||||
// The simulation step that last had a collision
|
||||
protected long CollidingStep { get; set; }
|
||||
// The simulation step that last had a collision with the ground
|
||||
protected long CollidingGroundStep { get; set; }
|
||||
// The collision flags we think are set in Bullet
|
||||
protected CollisionFlags CurrentCollisionFlags { get; set; }
|
||||
|
||||
// The collisions that have been collected this tick
|
||||
protected CollisionEventUpdate CollisionCollection;
|
||||
|
||||
// The simulation step is telling this object about a collision.
|
||||
// Return 'true' if a collision was processed and should be sent up.
|
||||
// Called at taint time from within the Step() function
|
||||
public virtual bool Collide(uint collidingWith, BSPhysObject collidee,
|
||||
OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
// The following lines make IsColliding() and IsCollidingGround() work
|
||||
CollidingStep = PhysicsScene.SimulationStep;
|
||||
if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID)
|
||||
{
|
||||
CollidingGroundStep = PhysicsScene.SimulationStep;
|
||||
}
|
||||
|
||||
// prims in the same linkset cannot collide with each other
|
||||
if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
// if someone has subscribed for collision events....
|
||||
if (SubscribedEvents()) {
|
||||
CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
|
||||
// DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
|
||||
// LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
|
||||
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Routine to send the collected collisions into the simulator.
|
||||
// Also handles removal of this from the collection of objects with collisions if
|
||||
// there are no collisions from this object. Mechanism is create one last
|
||||
// collision event to make collision_end work.
|
||||
// Called at taint time from within the Step() function thus no locking problems
|
||||
// with CollisionCollection and ObjectsWithNoMoreCollisions.
|
||||
// Return 'true' if there were some actual collisions passed up
|
||||
public virtual bool SendCollisions()
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
// throttle the collisions to the number of milliseconds specified in the subscription
|
||||
int nowTime = PhysicsScene.SimulationNowTime;
|
||||
if (nowTime >= NextCollisionOkTime)
|
||||
{
|
||||
NextCollisionOkTime = nowTime + SubscribedEventsMs;
|
||||
|
||||
// We are called if we previously had collisions. If there are no collisions
|
||||
// this time, send up one last empty event so OpenSim can sense collision end.
|
||||
if (CollisionCollection.Count == 0)
|
||||
{
|
||||
// If I have no collisions this time, remove me from the list of objects with collisions.
|
||||
ret = false;
|
||||
}
|
||||
|
||||
// DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
|
||||
base.SendCollisionUpdate(CollisionCollection);
|
||||
|
||||
// The collisionCollection structure is passed around in the simulator.
|
||||
// Make sure we don't have a handle to that one and that a new one is used for next time.
|
||||
CollisionCollection = new CollisionEventUpdate();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Subscribe for collision events.
|
||||
// Parameter is the millisecond rate the caller wishes collision events to occur.
|
||||
public override void SubscribeEvents(int ms) {
|
||||
// DetailLog("{0},{1}.SubscribeEvents,subscribing,ms={2}", LocalID, TypeName, ms);
|
||||
SubscribedEventsMs = ms;
|
||||
if (ms > 0)
|
||||
{
|
||||
// make sure first collision happens
|
||||
NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs);
|
||||
|
||||
PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate()
|
||||
{
|
||||
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subscribing for zero or less is the same as unsubscribing
|
||||
UnSubscribeEvents();
|
||||
}
|
||||
}
|
||||
public override void UnSubscribeEvents() {
|
||||
// DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName);
|
||||
SubscribedEventsMs = 0;
|
||||
PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate()
|
||||
{
|
||||
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||
});
|
||||
}
|
||||
// Return 'true' if the simulator wants collision events
|
||||
public override bool SubscribedEvents() {
|
||||
return (SubscribedEventsMs > 0);
|
||||
}
|
||||
|
||||
#endregion // Collisions
|
||||
|
||||
// High performance detailed logging routine used by the physical objects.
|
||||
protected void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
PhysicsScene.PhysicsLogging.Write(msg, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ using OpenMetaverse;
|
||||
namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim.
|
||||
/// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim.
|
||||
/// This module interfaces to an unmanaged C++ library which makes the
|
||||
/// actual calls into the Bullet physics engine.
|
||||
/// The unmanaged library is found in opensim-libs::trunk/unmanaged/BulletSim/.
|
||||
@@ -62,7 +62,7 @@ public class BSPlugin : IPhysicsPlugin
|
||||
if (Util.IsWindows())
|
||||
Util.LoadArchSpecificWindowsDll("BulletSim.dll");
|
||||
// If not Windows, loading is performed by the
|
||||
// Mono loader as specified in
|
||||
// Mono loader as specified in
|
||||
// "bin/Physics/OpenSim.Region.Physics.BulletSPlugin.dll.config".
|
||||
|
||||
_mScene = new BSScene(sceneIdentifier);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user