diff options
author | Justin Clark-Casey (justincc) | 2012-05-08 21:31:35 +0100 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2012-05-08 21:31:35 +0100 |
commit | abbd050a13e2b4f174c37979b46cf83f6b2f62dc (patch) | |
tree | 80cf9e103b30f58bdee41b6bd7b39449f699ca78 | |
parent | Revert "Better error handling if Load OAR or Save OAR fail" (diff) | |
download | opensim-SC_OLD-abbd050a13e2b4f174c37979b46cf83f6b2f62dc.zip opensim-SC_OLD-abbd050a13e2b4f174c37979b46cf83f6b2f62dc.tar.gz opensim-SC_OLD-abbd050a13e2b4f174c37979b46cf83f6b2f62dc.tar.bz2 opensim-SC_OLD-abbd050a13e2b4f174c37979b46cf83f6b2f62dc.tar.xz |
Perform SceneGraph.DuplicateObject() under existing m_updateLock already used for link and delinking, in order to avoid race conditions.
DuplicateObject() relies on source object having correct link numbers for the duration of the dupe.
Both link and delink can change link numbers such that they are not consistent for short periods of time.
-rw-r--r-- | OpenSim/Region/Framework/Scenes/SceneGraph.cs | 164 |
1 files changed, 89 insertions, 75 deletions
diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index 67eb0fe..4815922 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs | |||
@@ -92,8 +92,12 @@ namespace OpenSim.Region.Framework.Scenes | |||
92 | protected internal Dictionary<uint, SceneObjectGroup> SceneObjectGroupsByLocalPartID = new Dictionary<uint, SceneObjectGroup>(); | 92 | protected internal Dictionary<uint, SceneObjectGroup> SceneObjectGroupsByLocalPartID = new Dictionary<uint, SceneObjectGroup>(); |
93 | 93 | ||
94 | /// <summary> | 94 | /// <summary> |
95 | /// Lock to prevent object group update, linking and delinking operations from running concurrently. | 95 | /// Lock to prevent object group update, linking, delinking and duplication operations from running concurrently. |
96 | /// </summary> | 96 | /// </summary> |
97 | /// <remarks> | ||
98 | /// These operations rely on the parts composition of the object. If allowed to run concurrently then race | ||
99 | /// conditions can occur. | ||
100 | /// </remarks> | ||
97 | private Object m_updateLock = new Object(); | 101 | private Object m_updateLock = new Object(); |
98 | 102 | ||
99 | #endregion | 103 | #endregion |
@@ -1844,96 +1848,106 @@ namespace OpenSim.Region.Framework.Scenes | |||
1844 | /// <param name="AgentID"></param> | 1848 | /// <param name="AgentID"></param> |
1845 | /// <param name="GroupID"></param> | 1849 | /// <param name="GroupID"></param> |
1846 | /// <param name="rot"></param> | 1850 | /// <param name="rot"></param> |
1847 | public SceneObjectGroup DuplicateObject(uint originalPrimID, Vector3 offset, uint flags, UUID AgentID, UUID GroupID, Quaternion rot) | 1851 | /// <returns>null if duplication fails, otherwise the duplicated object</returns> |
1852 | public SceneObjectGroup DuplicateObject( | ||
1853 | uint originalPrimID, Vector3 offset, uint flags, UUID AgentID, UUID GroupID, Quaternion rot) | ||
1848 | { | 1854 | { |
1849 | // m_log.DebugFormat( | 1855 | Monitor.Enter(m_updateLock); |
1850 | // "[SCENE]: Duplication of object {0} at offset {1} requested by agent {2}", | 1856 | |
1851 | // originalPrimID, offset, AgentID); | 1857 | try |
1852 | |||
1853 | SceneObjectGroup original = GetGroupByPrim(originalPrimID); | ||
1854 | if (original != null) | ||
1855 | { | 1858 | { |
1856 | if (m_parentScene.Permissions.CanDuplicateObject( | 1859 | // m_log.DebugFormat( |
1857 | original.PrimCount, original.UUID, AgentID, original.AbsolutePosition)) | 1860 | // "[SCENE]: Duplication of object {0} at offset {1} requested by agent {2}", |
1861 | // originalPrimID, offset, AgentID); | ||
1862 | |||
1863 | SceneObjectGroup original = GetGroupByPrim(originalPrimID); | ||
1864 | if (original == null) | ||
1858 | { | 1865 | { |
1859 | SceneObjectGroup copy = original.Copy(true); | 1866 | m_log.WarnFormat( |
1860 | copy.AbsolutePosition = copy.AbsolutePosition + offset; | 1867 | "[SCENEGRAPH]: Attempt to duplicate nonexistant prim id {0} by {1}", originalPrimID, AgentID); |
1861 | 1868 | ||
1862 | if (original.OwnerID != AgentID) | 1869 | return null; |
1863 | { | 1870 | } |
1864 | copy.SetOwnerId(AgentID); | ||
1865 | copy.SetRootPartOwner(copy.RootPart, AgentID, GroupID); | ||
1866 | 1871 | ||
1867 | SceneObjectPart[] partList = copy.Parts; | 1872 | if (!m_parentScene.Permissions.CanDuplicateObject( |
1873 | original.PrimCount, original.UUID, AgentID, original.AbsolutePosition)) | ||
1874 | return null; | ||
1868 | 1875 | ||
1869 | if (m_parentScene.Permissions.PropagatePermissions()) | 1876 | SceneObjectGroup copy = original.Copy(true); |
1870 | { | 1877 | copy.AbsolutePosition = copy.AbsolutePosition + offset; |
1871 | foreach (SceneObjectPart child in partList) | ||
1872 | { | ||
1873 | child.Inventory.ChangeInventoryOwner(AgentID); | ||
1874 | child.TriggerScriptChangedEvent(Changed.OWNER); | ||
1875 | child.ApplyNextOwnerPermissions(); | ||
1876 | } | ||
1877 | } | ||
1878 | 1878 | ||
1879 | copy.RootPart.ObjectSaleType = 0; | 1879 | if (original.OwnerID != AgentID) |
1880 | copy.RootPart.SalePrice = 10; | 1880 | { |
1881 | } | 1881 | copy.SetOwnerId(AgentID); |
1882 | copy.SetRootPartOwner(copy.RootPart, AgentID, GroupID); | ||
1882 | 1883 | ||
1883 | // FIXME: This section needs to be refactored so that it just calls AddSceneObject() | 1884 | SceneObjectPart[] partList = copy.Parts; |
1884 | Entities.Add(copy); | 1885 | |
1885 | 1886 | if (m_parentScene.Permissions.PropagatePermissions()) | |
1886 | lock (SceneObjectGroupsByFullID) | ||
1887 | SceneObjectGroupsByFullID[copy.UUID] = copy; | ||
1888 | |||
1889 | SceneObjectPart[] children = copy.Parts; | ||
1890 | |||
1891 | lock (SceneObjectGroupsByFullPartID) | ||
1892 | { | ||
1893 | SceneObjectGroupsByFullPartID[copy.UUID] = copy; | ||
1894 | foreach (SceneObjectPart part in children) | ||
1895 | SceneObjectGroupsByFullPartID[part.UUID] = copy; | ||
1896 | } | ||
1897 | |||
1898 | lock (SceneObjectGroupsByLocalPartID) | ||
1899 | { | ||
1900 | SceneObjectGroupsByLocalPartID[copy.LocalId] = copy; | ||
1901 | foreach (SceneObjectPart part in children) | ||
1902 | SceneObjectGroupsByLocalPartID[part.LocalId] = copy; | ||
1903 | } | ||
1904 | // PROBABLE END OF FIXME | ||
1905 | |||
1906 | // Since we copy from a source group that is in selected | ||
1907 | // state, but the copy is shown deselected in the viewer, | ||
1908 | // We need to clear the selection flag here, else that | ||
1909 | // prim never gets persisted at all. The client doesn't | ||
1910 | // think it's selected, so it will never send a deselect... | ||
1911 | copy.IsSelected = false; | ||
1912 | |||
1913 | m_numPrim += copy.Parts.Length; | ||
1914 | |||
1915 | if (rot != Quaternion.Identity) | ||
1916 | { | 1887 | { |
1917 | copy.UpdateGroupRotationR(rot); | 1888 | foreach (SceneObjectPart child in partList) |
1889 | { | ||
1890 | child.Inventory.ChangeInventoryOwner(AgentID); | ||
1891 | child.TriggerScriptChangedEvent(Changed.OWNER); | ||
1892 | child.ApplyNextOwnerPermissions(); | ||
1893 | } | ||
1918 | } | 1894 | } |
1919 | 1895 | ||
1920 | copy.CreateScriptInstances(0, false, m_parentScene.DefaultScriptEngine, 1); | 1896 | copy.RootPart.ObjectSaleType = 0; |
1921 | copy.HasGroupChanged = true; | 1897 | copy.RootPart.SalePrice = 10; |
1922 | copy.ScheduleGroupForFullUpdate(); | 1898 | } |
1923 | copy.ResumeScripts(); | ||
1924 | |||
1925 | // required for physics to update it's position | ||
1926 | copy.AbsolutePosition = copy.AbsolutePosition; | ||
1927 | 1899 | ||
1928 | return copy; | 1900 | // FIXME: This section needs to be refactored so that it just calls AddSceneObject() |
1901 | Entities.Add(copy); | ||
1902 | |||
1903 | lock (SceneObjectGroupsByFullID) | ||
1904 | SceneObjectGroupsByFullID[copy.UUID] = copy; | ||
1905 | |||
1906 | SceneObjectPart[] children = copy.Parts; | ||
1907 | |||
1908 | lock (SceneObjectGroupsByFullPartID) | ||
1909 | { | ||
1910 | SceneObjectGroupsByFullPartID[copy.UUID] = copy; | ||
1911 | foreach (SceneObjectPart part in children) | ||
1912 | SceneObjectGroupsByFullPartID[part.UUID] = copy; | ||
1913 | } | ||
1914 | |||
1915 | lock (SceneObjectGroupsByLocalPartID) | ||
1916 | { | ||
1917 | SceneObjectGroupsByLocalPartID[copy.LocalId] = copy; | ||
1918 | foreach (SceneObjectPart part in children) | ||
1919 | SceneObjectGroupsByLocalPartID[part.LocalId] = copy; | ||
1920 | } | ||
1921 | // PROBABLE END OF FIXME | ||
1922 | |||
1923 | // Since we copy from a source group that is in selected | ||
1924 | // state, but the copy is shown deselected in the viewer, | ||
1925 | // We need to clear the selection flag here, else that | ||
1926 | // prim never gets persisted at all. The client doesn't | ||
1927 | // think it's selected, so it will never send a deselect... | ||
1928 | copy.IsSelected = false; | ||
1929 | |||
1930 | m_numPrim += copy.Parts.Length; | ||
1931 | |||
1932 | if (rot != Quaternion.Identity) | ||
1933 | { | ||
1934 | copy.UpdateGroupRotationR(rot); | ||
1929 | } | 1935 | } |
1936 | |||
1937 | copy.CreateScriptInstances(0, false, m_parentScene.DefaultScriptEngine, 1); | ||
1938 | copy.HasGroupChanged = true; | ||
1939 | copy.ScheduleGroupForFullUpdate(); | ||
1940 | copy.ResumeScripts(); | ||
1941 | |||
1942 | // required for physics to update it's position | ||
1943 | copy.AbsolutePosition = copy.AbsolutePosition; | ||
1944 | |||
1945 | return copy; | ||
1930 | } | 1946 | } |
1931 | else | 1947 | finally |
1932 | { | 1948 | { |
1933 | m_log.WarnFormat("[SCENE]: Attempted to duplicate nonexistant prim id {0}", GroupID); | 1949 | Monitor.Exit(m_updateLock); |
1934 | } | 1950 | } |
1935 | |||
1936 | return null; | ||
1937 | } | 1951 | } |
1938 | 1952 | ||
1939 | /// <summary> | 1953 | /// <summary> |