diff --git a/OpenSim/Data/MSSQL/MSSQLSimulationData.cs b/OpenSim/Data/MSSQL/MSSQLSimulationData.cs index 17f42e1f0b..276a19002f 100644 --- a/OpenSim/Data/MSSQL/MSSQLSimulationData.cs +++ b/OpenSim/Data/MSSQL/MSSQLSimulationData.cs @@ -351,7 +351,7 @@ IF EXISTS (SELECT UUID FROM prims WHERE UUID = @UUID) ScriptAccessPin = @ScriptAccessPin, AllowedDrop = @AllowedDrop, DieAtEdge = @DieAtEdge, SalePrice = @SalePrice, SaleType = @SaleType, ColorR = @ColorR, ColorG = @ColorG, ColorB = @ColorB, ColorA = @ColorA, ParticleSystem = @ParticleSystem, ClickAction = @ClickAction, Material = @Material, CollisionSound = @CollisionSound, CollisionSoundVolume = @CollisionSoundVolume, PassTouches = @PassTouches, - LinkNumber = @LinkNumber, MediaURL = @MediaURL + LinkNumber = @LinkNumber, MediaURL = @MediaURL, DynAttrs = @DynAttrs WHERE UUID = @UUID END ELSE @@ -366,7 +366,7 @@ ELSE PayPrice, PayButton1, PayButton2, PayButton3, PayButton4, LoopedSound, LoopedSoundGain, TextureAnimation, OmegaX, OmegaY, OmegaZ, CameraEyeOffsetX, CameraEyeOffsetY, CameraEyeOffsetZ, CameraAtOffsetX, CameraAtOffsetY, CameraAtOffsetZ, ForceMouselook, ScriptAccessPin, AllowedDrop, DieAtEdge, SalePrice, SaleType, ColorR, ColorG, ColorB, ColorA, - ParticleSystem, ClickAction, Material, CollisionSound, CollisionSoundVolume, PassTouches, LinkNumber, MediaURL + ParticleSystem, ClickAction, Material, CollisionSound, CollisionSoundVolume, PassTouches, LinkNumber, MediaURL, DynAttrs ) VALUES ( @UUID, @CreationDate, @Name, @Text, @Description, @SitName, @TouchName, @ObjectFlags, @OwnerMask, @NextOwnerMask, @GroupMask, @EveryoneMask, @BaseMask, @PositionX, @PositionY, @PositionZ, @GroupPositionX, @GroupPositionY, @GroupPositionZ, @VelocityX, @@ -376,7 +376,7 @@ ELSE @PayPrice, @PayButton1, @PayButton2, @PayButton3, @PayButton4, @LoopedSound, @LoopedSoundGain, @TextureAnimation, @OmegaX, @OmegaY, @OmegaZ, @CameraEyeOffsetX, @CameraEyeOffsetY, @CameraEyeOffsetZ, @CameraAtOffsetX, @CameraAtOffsetY, @CameraAtOffsetZ, @ForceMouselook, @ScriptAccessPin, @AllowedDrop, @DieAtEdge, @SalePrice, @SaleType, @ColorR, @ColorG, @ColorB, @ColorA, - @ParticleSystem, @ClickAction, @Material, @CollisionSound, @CollisionSoundVolume, @PassTouches, @LinkNumber, @MediaURL + @ParticleSystem, @ClickAction, @Material, @CollisionSound, @CollisionSoundVolume, @PassTouches, @LinkNumber, @MediaURL, @DynAttrs ) END"; @@ -1691,6 +1691,11 @@ VALUES if (!(primRow["MediaURL"] is System.DBNull)) prim.MediaUrl = (string)primRow["MediaURL"]; + + if (!(primRow["DynAttrs"] is System.DBNull)) + prim.DynAttrs = DAMap.FromXml((string)primRow["DynAttrs"]); + else + prim.DynAttrs = new DAMap(); return prim; } @@ -1749,7 +1754,6 @@ VALUES baseShape.Media = PrimitiveBaseShape.MediaList.FromXml((string)shapeRow["Media"]); } - return baseShape; } @@ -2086,6 +2090,11 @@ VALUES parameters.Add(_Database.CreateParameter("PassTouches", 0)); parameters.Add(_Database.CreateParameter("LinkNumber", prim.LinkNum)); parameters.Add(_Database.CreateParameter("MediaURL", prim.MediaUrl)); + + if (prim.DynAttrs.Count > 0) + parameters.Add(_Database.CreateParameter("DynAttrs", prim.DynAttrs.ToXml())); + else + parameters.Add(_Database.CreateParameter("DynAttrs", null)); return parameters.ToArray(); } @@ -2143,7 +2152,6 @@ VALUES parameters.Add(_Database.CreateParameter("Media", s.Media.ToXml())); } - return parameters.ToArray(); } diff --git a/OpenSim/Data/MSSQL/Resources/RegionStore.migrations b/OpenSim/Data/MSSQL/Resources/RegionStore.migrations index 350e548a0a..92cc38ab2e 100644 --- a/OpenSim/Data/MSSQL/Resources/RegionStore.migrations +++ b/OpenSim/Data/MSSQL/Resources/RegionStore.migrations @@ -1148,3 +1148,11 @@ CREATE TABLE [dbo].[regionenvironment]( ) ON [PRIMARY] COMMIT + +:VERSION 38 #---------------- Dynamic attributes + +BEGIN TRANSACTION + +ALTER TABLE prims ADD COLUMN DynAttrs TEXT; + +COMMIT diff --git a/OpenSim/Data/MySQL/MySQLSimulationData.cs b/OpenSim/Data/MySQL/MySQLSimulationData.cs index d56278354c..c95311e6c7 100644 --- a/OpenSim/Data/MySQL/MySQLSimulationData.cs +++ b/OpenSim/Data/MySQL/MySQLSimulationData.cs @@ -171,7 +171,8 @@ namespace OpenSim.Data.MySQL "ParticleSystem, ClickAction, Material, " + "CollisionSound, CollisionSoundVolume, " + "PassTouches, " + - "LinkNumber, MediaURL) values (" + "?UUID, " + + "LinkNumber, MediaURL, DynAttrs) " + + "values (?UUID, " + "?CreationDate, ?Name, ?Text, " + "?Description, ?SitName, ?TouchName, " + "?ObjectFlags, ?OwnerMask, ?NextOwnerMask, " + @@ -202,7 +203,8 @@ namespace OpenSim.Data.MySQL "?SaleType, ?ColorR, ?ColorG, " + "?ColorB, ?ColorA, ?ParticleSystem, " + "?ClickAction, ?Material, ?CollisionSound, " + - "?CollisionSoundVolume, ?PassTouches, ?LinkNumber, ?MediaURL)"; + "?CollisionSoundVolume, ?PassTouches, ?LinkNumber, " + + "?MediaURL, ?DynAttrs)"; FillPrimCommand(cmd, prim, obj.UUID, regionUUID); @@ -219,7 +221,8 @@ namespace OpenSim.Data.MySQL "PathTaperX, PathTaperY, PathTwist, " + "PathTwistBegin, ProfileBegin, ProfileEnd, " + "ProfileCurve, ProfileHollow, Texture, " + - "ExtraParams, State, Media) values (?UUID, " + + "ExtraParams, State, Media) " + + "values (?UUID, " + "?Shape, ?ScaleX, ?ScaleY, ?ScaleZ, " + "?PCode, ?PathBegin, ?PathEnd, " + "?PathScaleX, ?PathScaleY, " + @@ -1291,6 +1294,11 @@ namespace OpenSim.Data.MySQL if (!(row["MediaURL"] is System.DBNull)) prim.MediaUrl = (string)row["MediaURL"]; + + if (!(row["DynAttrs"] is System.DBNull)) + prim.DynAttrs = DAMap.FromXml((string)row["DynAttrs"]); + else + prim.DynAttrs = new DAMap(); return prim; } @@ -1637,6 +1645,11 @@ namespace OpenSim.Data.MySQL cmd.Parameters.AddWithValue("LinkNumber", prim.LinkNum); cmd.Parameters.AddWithValue("MediaURL", prim.MediaUrl); + + if (prim.DynAttrs.Count > 0) + cmd.Parameters.AddWithValue("DynAttrs", prim.DynAttrs.ToXml()); + else + cmd.Parameters.AddWithValue("DynAttrs", null); } /// diff --git a/OpenSim/Data/MySQL/Resources/RegionStore.migrations b/OpenSim/Data/MySQL/Resources/RegionStore.migrations index 5b59779050..c48aec2798 100644 --- a/OpenSim/Data/MySQL/Resources/RegionStore.migrations +++ b/OpenSim/Data/MySQL/Resources/RegionStore.migrations @@ -902,3 +902,11 @@ BEGIN; CREATE TABLE `regionextra` (`RegionID` char(36) not null, `Name` varchar(32) not null, `value` text, primary key(`RegionID`, `Name`)); COMMIT; + +:VERSION 46 #---------------- Dynamic attributes + +BEGIN; + +ALTER TABLE prims ADD COLUMN DynAttrs TEXT; + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/RegionStore.migrations b/OpenSim/Data/SQLite/Resources/RegionStore.migrations index e872977634..e583dc2bc1 100644 --- a/OpenSim/Data/SQLite/Resources/RegionStore.migrations +++ b/OpenSim/Data/SQLite/Resources/RegionStore.migrations @@ -575,3 +575,8 @@ CREATE TABLE `regionenvironment` ( ); COMMIT; + +:VERSION 27 +BEGIN; +ALTER TABLE prims ADD COLUMN DynAttrs TEXT; +COMMIT; diff --git a/OpenSim/Data/SQLite/SQLiteSimulationData.cs b/OpenSim/Data/SQLite/SQLiteSimulationData.cs index 29cac3c2f1..91fc704e61 100644 --- a/OpenSim/Data/SQLite/SQLiteSimulationData.cs +++ b/OpenSim/Data/SQLite/SQLiteSimulationData.cs @@ -1232,6 +1232,8 @@ namespace OpenSim.Data.SQLite createCol(prims, "VolumeDetect", typeof(Int16)); createCol(prims, "MediaURL", typeof(String)); + + createCol(prims, "DynAttrs", typeof(String)); // Add in contraints prims.PrimaryKey = new DataColumn[] { prims.Columns["UUID"] }; @@ -1711,6 +1713,16 @@ namespace OpenSim.Data.SQLite // m_log.DebugFormat("[SQLITE]: MediaUrl type [{0}]", row["MediaURL"].GetType()); prim.MediaUrl = (string)row["MediaURL"]; } + + if (!(row["DynAttrs"] is System.DBNull)) + { + //m_log.DebugFormat("[SQLITE]: DynAttrs type [{0}]", row["DynAttrs"].GetType()); + prim.DynAttrs = DAMap.FromXml((string)row["DynAttrs"]); + } + else + { + prim.DynAttrs = new DAMap(); + } return prim; } @@ -2133,6 +2145,11 @@ namespace OpenSim.Data.SQLite row["VolumeDetect"] = 0; row["MediaURL"] = prim.MediaUrl; + + if (prim.DynAttrs.Count > 0) + row["DynAttrs"] = prim.DynAttrs.ToXml(); + else + row["DynAttrs"] = null; } /// @@ -2392,7 +2409,7 @@ namespace OpenSim.Data.SQLite if (!(row["Media"] is System.DBNull)) s.Media = PrimitiveBaseShape.MediaList.FromXml((string)row["Media"]); - + return s; } diff --git a/OpenSim/Framework/DAMap.cs b/OpenSim/Framework/DAMap.cs new file mode 100644 index 0000000000..291c8b8385 --- /dev/null +++ b/OpenSim/Framework/DAMap.cs @@ -0,0 +1,260 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework +{ + /// + /// This class stores and retrieves dynamic attributes. + /// + /// + /// Modules that want to use dynamic attributes need to do so in a private data store + /// which is accessed using a unique name. DAMap provides access to the data stores, + /// each of which is an OSDMap. Modules are free to store any type of data they want + /// within their data store. However, avoid storing large amounts of data because that + /// would slow down database access. + /// + public class DAMap : IDictionary, IXmlSerializable + { + private static readonly int MIN_STORE_NAME_LENGTH = 4; + + protected OSDMap m_map; + + public DAMap() { m_map = new OSDMap(); } + + public XmlSchema GetSchema() { return null; } + + public static DAMap FromXml(string rawXml) + { + DAMap map = new DAMap(); + map.ReadXml(rawXml); + return map; + } + + public void ReadXml(string rawXml) + { + // System.Console.WriteLine("Trying to deserialize [{0}]", rawXml); + + lock (this) + m_map = (OSDMap)OSDParser.DeserializeLLSDXml(rawXml); + } + + public void ReadXml(XmlReader reader) + { + ReadXml(reader.ReadInnerXml()); + } + + public string ToXml() + { + lock (this) + return OSDParser.SerializeLLSDXmlString(m_map); + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteRaw(ToXml()); + } + + public void CopyFrom(DAMap other) + { + // Deep copy + + string data = null; + lock (other) + { + if (other.Count > 0) + { + data = OSDParser.SerializeLLSDXmlString(other.m_map); + } + } + + lock (this) + { + if (data == null) + Clear(); + else + m_map = (OSDMap)OSDParser.DeserializeLLSDXml(data); + } + } + + /// + /// Returns the number of data stores. + /// + public int Count { get { lock (this) { return m_map.Count; } } } + + public bool IsReadOnly { get { return false; } } + + /// + /// Returns the names of the data stores. + /// + public ICollection Keys { get { lock (this) { return m_map.Keys; } } } + + /// + /// Returns all the data stores. + /// + public ICollection Values + { + get + { + lock (this) + { + List stores = new List(m_map.Count); + foreach (OSD llsd in m_map.Values) + stores.Add((OSDMap)llsd); + return stores; + } + } + } + + /// + /// Gets or sets one data store. + /// + /// Store name + /// + public OSDMap this[string key] + { + get + { + OSD llsd; + + lock (this) + { + if (m_map.TryGetValue(key, out llsd)) + return (OSDMap)llsd; + else + return null; + } + } + + set + { + ValidateKey(key); + lock (this) + m_map[key] = value; + } + } + + private static void ValidateKey(string key) + { + if (key.Length < MIN_STORE_NAME_LENGTH) + throw new Exception("Minimum store name length is " + MIN_STORE_NAME_LENGTH); + } + + public bool ContainsKey(string key) + { + lock (this) + return m_map.ContainsKey(key); + } + + public void Add(string key, OSDMap store) + { + ValidateKey(key); + lock (this) + m_map.Add(key, store); + } + + public void Add(KeyValuePair kvp) + { + lock (this) + m_map.Add(kvp.Key, kvp.Value); + } + + public bool Remove(string key) + { + lock (this) + return m_map.Remove(key); + } + + public bool TryGetValue(string key, out OSDMap store) + { + lock (this) + { + OSD llsd; + if (m_map.TryGetValue(key, out llsd)) + { + store = (OSDMap)llsd; + return true; + } + else + { + store = null; + return false; + } + } + } + + public void Clear() + { + lock (this) + m_map.Clear(); + } + + public bool Contains(KeyValuePair kvp) + { + lock (this) + return m_map.ContainsKey(kvp.Key); + } + + public void CopyTo(KeyValuePair[] array, int index) + { + throw new NotImplementedException(); + } + + public bool Remove(KeyValuePair kvp) + { + lock (this) + return m_map.Remove(kvp.Key); + } + + public System.Collections.IDictionaryEnumerator GetEnumerator() + { + lock (this) + return m_map.GetEnumerator(); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return null; + } + + IEnumerator IEnumerable.GetEnumerator() + { + lock (this) + return m_map.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DAExampleModule.cs b/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DAExampleModule.cs new file mode 100644 index 0000000000..d36f65acf3 --- /dev/null +++ b/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DAExampleModule.cs @@ -0,0 +1,107 @@ +/* + * 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.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.Packets; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Region.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.DynamicAttributes.DAExampleModule +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "DAExampleModule")] + public class DAExampleModule : INonSharedRegionModule + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static readonly bool ENABLED = false; // enable for testing + + protected Scene m_scene; + protected IDialogModule m_dialogMod; + + public string Name { get { return "DAExample Module"; } } + public Type ReplaceableInterface { get { return null; } } + + public void Initialise(IConfigSource source) {} + + public void AddRegion(Scene scene) + { + if (ENABLED) + { + m_scene = scene; + m_scene.EventManager.OnSceneGroupMove += OnSceneGroupMove; + m_dialogMod = m_scene.RequestModuleInterface(); + } + } + + public void RemoveRegion(Scene scene) + { + if (ENABLED) + { + m_scene.EventManager.OnSceneGroupMove -= OnSceneGroupMove; + } + } + + public void RegionLoaded(Scene scene) {} + + public void Close() + { + RemoveRegion(m_scene); + } + + protected bool OnSceneGroupMove(UUID groupId, Vector3 delta) + { + OSDMap attrs = null; + SceneObjectPart sop = m_scene.GetSceneObjectPart(groupId); + if (!sop.DynAttrs.TryGetValue(Name, out attrs)) + attrs = new OSDMap(); + + OSDInteger newValue; + + if (!attrs.ContainsKey("moves")) + newValue = new OSDInteger(1); + else + newValue = new OSDInteger(((OSDInteger)attrs["moves"]).AsInteger() + 1); + + attrs["moves"] = newValue; + + sop.DynAttrs[Name] = attrs; + + m_dialogMod.SendGeneralAlert(string.Format("{0} {1} moved {2} times", sop.Name, sop.UUID, newValue)); + + return true; + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs b/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs index bcb8e2f537..b4348c9d5a 100644 --- a/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs +++ b/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs @@ -35,6 +35,7 @@ using OpenSim.Framework; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes.Serialization; using OpenSim.Tests.Common; +using OpenMetaverse.StructuredData; namespace OpenSim.Region.CoreModules.World.Serialiser.Tests { @@ -143,6 +144,7 @@ namespace OpenSim.Region.CoreModules.World.Serialiser.Tests None 00000000-0000-0000-0000-000000000000 0 + MyStorethe answer42 @@ -331,6 +333,7 @@ namespace OpenSim.Region.CoreModules.World.Serialiser.Tests 0 2147483647 None + MyStorelast wordsRosebud 00000000-0000-0000-0000-000000000000 @@ -359,6 +362,8 @@ namespace OpenSim.Region.CoreModules.World.Serialiser.Tests Assert.That(rootPart.UUID, Is.EqualTo(new UUID("e6a5a05e-e8cc-4816-8701-04165e335790"))); Assert.That(rootPart.CreatorID, Is.EqualTo(new UUID("a6dacf01-4636-4bb9-8a97-30609438af9d"))); Assert.That(rootPart.Name, Is.EqualTo("PrimMyRide")); + OSDMap store = rootPart.DynAttrs["MyStore"]; + Assert.AreEqual(42, store["the answer"].AsInteger()); // TODO: Check other properties } @@ -409,6 +414,14 @@ namespace OpenSim.Region.CoreModules.World.Serialiser.Tests rp.CreatorID = rpCreatorId; rp.Shape = shape; + string daStoreName = "MyStore"; + string daKey = "foo"; + string daValue = "bar"; + OSDMap myStore = new OSDMap(); + myStore.Add(daKey, daValue); + rp.DynAttrs = new DAMap(); + rp.DynAttrs[daStoreName] = myStore; + SceneObjectGroup so = new SceneObjectGroup(rp); // Need to add the object to the scene so that the request to get script state succeeds @@ -424,6 +437,7 @@ namespace OpenSim.Region.CoreModules.World.Serialiser.Tests UUID uuid = UUID.Zero; string name = null; UUID creatorId = UUID.Zero; + DAMap daMap = null; while (xtr.Read() && xtr.Name != "SceneObjectPart") { @@ -449,6 +463,10 @@ namespace OpenSim.Region.CoreModules.World.Serialiser.Tests creatorId = UUID.Parse(xtr.ReadElementString("UUID")); xtr.ReadEndElement(); break; + case "DynAttrs": + daMap = new DAMap(); + daMap.ReadXml(xtr); + break; } } @@ -462,6 +480,8 @@ namespace OpenSim.Region.CoreModules.World.Serialiser.Tests Assert.That(uuid, Is.EqualTo(rpUuid)); Assert.That(name, Is.EqualTo(rpName)); Assert.That(creatorId, Is.EqualTo(rpCreatorId)); + Assert.NotNull(daMap); + Assert.AreEqual(daValue, daMap[daStoreName][daKey].AsString()); } [Test] @@ -476,6 +496,8 @@ namespace OpenSim.Region.CoreModules.World.Serialiser.Tests Assert.That(rootPart.UUID, Is.EqualTo(new UUID("9be68fdd-f740-4a0f-9675-dfbbb536b946"))); Assert.That(rootPart.CreatorID, Is.EqualTo(new UUID("b46ef588-411e-4a8b-a284-d7dcfe8e74ef"))); Assert.That(rootPart.Name, Is.EqualTo("PrimFun")); + OSDMap store = rootPart.DynAttrs["MyStore"]; + Assert.AreEqual("Rosebud", store["last words"].AsString()); // TODO: Check other properties } @@ -500,6 +522,14 @@ namespace OpenSim.Region.CoreModules.World.Serialiser.Tests rp.CreatorID = rpCreatorId; rp.Shape = shape; + string daStoreName = "MyStore"; + string daKey = "foo"; + string daValue = "bar"; + OSDMap myStore = new OSDMap(); + myStore.Add(daKey, daValue); + rp.DynAttrs = new DAMap(); + rp.DynAttrs[daStoreName] = myStore; + SceneObjectGroup so = new SceneObjectGroup(rp); // Need to add the object to the scene so that the request to get script state succeeds @@ -516,6 +546,7 @@ namespace OpenSim.Region.CoreModules.World.Serialiser.Tests UUID uuid = UUID.Zero; string name = null; UUID creatorId = UUID.Zero; + DAMap daMap = null; while (xtr.Read() && xtr.Name != "SceneObjectPart") { @@ -537,6 +568,10 @@ namespace OpenSim.Region.CoreModules.World.Serialiser.Tests creatorId = UUID.Parse(xtr.ReadElementString("Guid")); xtr.ReadEndElement(); break; + case "DynAttrs": + daMap = new DAMap(); + daMap.ReadXml(xtr); + break; } } @@ -549,6 +584,8 @@ namespace OpenSim.Region.CoreModules.World.Serialiser.Tests Assert.That(uuid, Is.EqualTo(rpUuid)); Assert.That(name, Is.EqualTo(rpName)); Assert.That(creatorId, Is.EqualTo(rpCreatorId)); + Assert.NotNull(daMap); + Assert.AreEqual(daValue, daMap[daStoreName][daKey].AsString()); } } } \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 672063555f..189d2987eb 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -37,6 +37,7 @@ using System.Xml.Serialization; using log4net; using OpenMetaverse; using OpenMetaverse.Packets; +using OpenMetaverse.StructuredData; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes.Scripting; @@ -124,6 +125,11 @@ namespace OpenSim.Region.Framework.Scenes private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + /// + /// Dynamic attributes can be created and deleted as required. + /// + public DAMap DynAttrs { get; set; } + /// /// Is this a root part? /// @@ -335,6 +341,7 @@ namespace OpenSim.Region.Framework.Scenes m_particleSystem = Utils.EmptyBytes; Rezzed = DateTime.UtcNow; Description = String.Empty; + DynAttrs = new DAMap(); // Prims currently only contain a single folder (Contents). From looking at the Second Life protocol, // this appears to have the same UUID (!) as the prim. If this isn't the case, one can't drag items from @@ -1618,6 +1625,8 @@ namespace OpenSim.Region.Framework.Scenes Array.Copy(Shape.ExtraParams, extraP, extraP.Length); dupe.Shape.ExtraParams = extraP; + dupe.DynAttrs.CopyFrom(DynAttrs); + if (userExposed) { /* @@ -4598,4 +4607,4 @@ namespace OpenSim.Region.Framework.Scenes } } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs index 2d4c60ad75..4a2a47e685 100644 --- a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs +++ b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs @@ -359,6 +359,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization m_SOPXmlProcessors.Add("CollisionSound", ProcessCollisionSound); m_SOPXmlProcessors.Add("CollisionSoundVolume", ProcessCollisionSoundVolume); m_SOPXmlProcessors.Add("MediaUrl", ProcessMediaUrl); + m_SOPXmlProcessors.Add("DynAttrs", ProcessDynAttrs); m_SOPXmlProcessors.Add("TextureAnimation", ProcessTextureAnimation); m_SOPXmlProcessors.Add("ParticleSystem", ProcessParticleSystem); m_SOPXmlProcessors.Add("PayPrice0", ProcessPayPrice0); @@ -722,6 +723,11 @@ namespace OpenSim.Region.Framework.Scenes.Serialization obj.MediaUrl = reader.ReadElementContentAsString("MediaUrl", String.Empty); } + private static void ProcessDynAttrs(SceneObjectPart obj, XmlTextReader reader) + { + obj.DynAttrs.ReadXml(reader); + } + private static void ProcessTextureAnimation(SceneObjectPart obj, XmlTextReader reader) { obj.TextureAnimation = Convert.FromBase64String(reader.ReadElementContentAsString("TextureAnimation", String.Empty)); @@ -1235,6 +1241,14 @@ namespace OpenSim.Region.Framework.Scenes.Serialization writer.WriteElementString("CollisionSoundVolume", sop.CollisionSoundVolume.ToString()); if (sop.MediaUrl != null) writer.WriteElementString("MediaUrl", sop.MediaUrl.ToString()); + + if (sop.DynAttrs.Count > 0) + { + writer.WriteStartElement("DynAttrs"); + sop.DynAttrs.WriteXml(writer); + writer.WriteEndElement(); + } + WriteBytes(writer, "TextureAnimation", sop.TextureAnimation); WriteBytes(writer, "ParticleSystem", sop.ParticleSystem); writer.WriteElementString("PayPrice0", sop.PayPrice[0].ToString()); diff --git a/prebuild.xml b/prebuild.xml index 329ff73550..74238d1302 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -3023,6 +3023,7 @@ +