diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index eef0c731af..edb87fff74 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -552,9 +552,12 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess // process causes some clients to fail to display the attachment properly. m_Scene.AddNewSceneObject(group, true, false); + // This is required for Group.Scene to be valid + group.SetScene(m_Scene); + // m_log.InfoFormat("ray end point for inventory rezz is {0} {1} {2} ", RayEnd.X, RayEnd.Y, RayEnd.Z); // if attachment we set it's asset id so object updates can reflect that - // if not, we set it's position in world. + // if not, we set it's position in world.); if (!attachment) { group.ScheduleGroupForFullUpdate(); diff --git a/OpenSim/Region/CoreModules/World/Objects/Components/ComponentManagerModule.cs b/OpenSim/Region/CoreModules/World/Objects/Components/ComponentManagerModule.cs new file mode 100644 index 0000000000..4ea914a965 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Objects/Components/ComponentManagerModule.cs @@ -0,0 +1,83 @@ +using System; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Scenes.Components; + +namespace OpenSim.Region.CoreModules.World.Objects.Components +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "ComponentManagerModule")] + class ComponentManagerModule : IComponentManagerModule, ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public string Name + { + get + { + return "ComponentManager"; + } + } + + public Type ReplaceableInterface + { + get { return typeof (IComponentManagerModule); } + } + + public void Initialise(IConfigSource source) + { + + } + + public void Close() + { + + } + + public void AddRegion(Scene scene) + { + scene.RegisterModuleInterface(this); + } + + public void RemoveRegion(Scene scene) + { + + } + + public void RegionLoaded(Scene scene) + { + + } + + public void PostInitialise() + { + + } + + #region Implementation of IComponentManagerModule + + public void CreateComponent(SceneObjectPart target, Type componentType, IComponentState state) + { + foreach (OnCreateComponentDelegate h in OnCreateComponent.GetInvocationList()) + { + IComponent x = h(componentType, state); + if(x != null) + { + target.SetComponent(x); + x.SetParent(target); + return; + } + } + + m_log.Warn("[Components] Unable to create component " + componentType + ". No ComponentFactory was able to recognize it. Could you be missing a region module?"); + } + + public event OnCreateComponentDelegate OnCreateComponent; + + #endregion + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IComponentManagerModule.cs b/OpenSim/Region/Framework/Interfaces/IComponentManagerModule.cs new file mode 100644 index 0000000000..763c5f4d39 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IComponentManagerModule.cs @@ -0,0 +1,14 @@ +using System; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Scenes.Components; + +namespace OpenSim.Region.Framework.Interfaces +{ + public delegate IComponent OnCreateComponentDelegate(Type componentType, IComponentState componentState); + + public interface IComponentManagerModule + { + void CreateComponent(SceneObjectPart part, Type componentType, IComponentState state); + event OnCreateComponentDelegate OnCreateComponent; + } +} diff --git a/OpenSim/Region/Framework/Scenes/Components/ComponentFactory.cs b/OpenSim/Region/Framework/Scenes/Components/ComponentFactory.cs new file mode 100644 index 0000000000..22ffbc4a32 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Components/ComponentFactory.cs @@ -0,0 +1,16 @@ +using System; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.Framework.Scenes.Components +{ + public abstract class ComponentFactory + { + public void InitComponentHandler(Scene x) + { + IComponentManagerModule cmm = x.RequestModuleInterface(); + cmm.OnCreateComponent += CreateComponent; + } + + protected abstract IComponent CreateComponent(Type componentType, IComponentState componentState); + } +} diff --git a/OpenSim/Region/Framework/Scenes/Components/ComponentState.cs b/OpenSim/Region/Framework/Scenes/Components/ComponentState.cs new file mode 100644 index 0000000000..05663aa335 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Components/ComponentState.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace OpenSim.Region.Framework.Scenes.Components +{ + public class ComponentState : IComponentState + { + private readonly Dictionary m_stateData = new Dictionary(); + + public void Set(string name, T data) + { + if (typeof(T).IsSerializable) + { + m_stateData[name] = data; + } + else + { + throw new SerializationException("Unable to set " + name + " as value because " + typeof (T) + + " is not a serializable type."); + } + } + + public bool TryGet(string name, out T val) + { + Object x; + if(m_stateData.TryGetValue(name, out x)) + { + if(x is T) + { + val = (T)x; + return true; + } + } + val = default(T); + return false; + } + + public T Get(string name) + { + return (T) m_stateData[name]; + } + + public string Serialise() + { + throw new NotImplementedException(); + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/Components/IComponent.cs b/OpenSim/Region/Framework/Scenes/Components/IComponent.cs new file mode 100644 index 0000000000..b6c6524087 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Components/IComponent.cs @@ -0,0 +1,23 @@ +using System; + +namespace OpenSim.Region.Framework.Scenes.Components +{ + /// + /// A component on an object + /// TODO: Better documentation + /// + public interface IComponent + { + /// + /// The type of the component, only one of each 'type' can be loaded. + /// + Type BaseType { get; } + + /// + /// A representation of the current state of the component, to be deserialised later. + /// + IComponentState State { get; } + + void SetParent(SceneObjectPart part); + } +} diff --git a/OpenSim/Region/Framework/Scenes/Components/IComponentState.cs b/OpenSim/Region/Framework/Scenes/Components/IComponentState.cs new file mode 100644 index 0000000000..f7cafb70af --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Components/IComponentState.cs @@ -0,0 +1,18 @@ +namespace OpenSim.Region.Framework.Scenes.Components +{ + public interface IComponentState + { + /// + /// + /// + /// + /// A serializable type + void Set(string name, T data); + + T Get(string name); + + bool TryGet(string name, out T data); + + string Serialise(); + } +} diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 117f86900b..a666ff6501 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -568,6 +568,8 @@ namespace OpenSim.Region.Framework.Scenes ApplyPhysics(m_scene.m_physicalPrim); + InitPartComponents(); + // Don't trigger the update here - otherwise some client issues occur when multiple updates are scheduled // for the same object with very different properties. The caller must schedule the update. //ScheduleGroupForFullUpdate(); @@ -1133,6 +1135,8 @@ namespace OpenSim.Region.Framework.Scenes /// public void SetScene(Scene scene) { + InitPartComponents(); + m_scene = scene; } @@ -3579,8 +3583,19 @@ namespace OpenSim.Region.Framework.Scenes } } + #region Components + + public void InitPartComponents() + { + m_log.Info("[COMPONENTS] Initialising part components"); + foreach (SceneObjectPart part in m_parts.Values) + part.InitComponents(); + } + + #endregion + #region ISceneObject - + public virtual ISceneObject CloneForNewScene() { SceneObjectGroup sog = Copy(false); diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 2ad4223670..8a30523f0e 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -30,7 +30,6 @@ using System.Collections.Generic; using System.Drawing; using System.Reflection; using System.Runtime.Serialization; -using System.Security.Permissions; using System.Xml; using System.Xml.Serialization; using log4net; @@ -38,6 +37,7 @@ using OpenMetaverse; using OpenMetaverse.Packets; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes.Components; using OpenSim.Region.Framework.Scenes.Scripting; using OpenSim.Region.Physics.Manager; @@ -3492,6 +3492,8 @@ namespace OpenSim.Region.Framework.Scenes public void SetParent(SceneObjectGroup parent) { m_parentGroup = parent; + + InitComponents(); } // Use this for attachments! LocalID should be avatar's localid @@ -4053,6 +4055,7 @@ namespace OpenSim.Region.Framework.Scenes /// public void ToXml(XmlWriter xmlWriter) { + SaveComponents(); // Gather all the component data serializer.Serialize(xmlWriter, this); } @@ -4797,5 +4800,94 @@ namespace OpenSim.Region.Framework.Scenes Color color = Color; return new Color4(color.R, color.G, color.B, (byte)(0xFF - color.A)); } + + #region Components + + [NonSerializedAttribute] // Component serialisation occurs manually. + private Dictionary m_components = new Dictionary(); + + private IDictionary m_componentStates = new Dictionary(); + + public void InitComponents(IDictionary states) + { + m_componentStates = states; + + InitComponents(); + } + + [NonSerializedAttribute] + private bool m_componentsInit = false; + + public void InitComponents() + { + if(m_componentsInit) + return; + + IDictionary states = m_componentStates; + if(ParentGroup.Scene != null) + { + m_log.Info("[COMPONENTS] Initialising components..."); + IComponentManagerModule cmm = ParentGroup.Scene.RequestModuleInterface(); + foreach(KeyValuePair kvp in states) + { + m_log.Info("[COMPONENTS] Adding component " + kvp.Key + " to SceneObjectPart."); + cmm.CreateComponent(this,kvp.Key,kvp.Value); + } + m_componentsInit = true; + } else + { + m_log.Warn("[COMPONENTS] Trying to initialise component which is not attached to a scene."); + } + } + + public void SaveComponents() + { + foreach (KeyValuePair keyValuePair in m_components) + { + IComponentState state = keyValuePair.Value.State; + Type baseType = keyValuePair.Value.BaseType; + + m_componentStates[baseType] = state; + } + } + + /// + /// Accesses a component + /// + /// + /// + public T C() + { + return Get(); + } + + public T Get() + { + return default(T); + } + + public bool TryGet(out T val) + { + if(m_components.ContainsKey(typeof(T))) + { + val = Get(); + return true; + } + + val = default(T); + return false; + } + + public bool Contains() + { + return m_components.ContainsKey(typeof (T)); + } + + public void SetComponent(IComponent val) + { + Type T = val.BaseType; + m_components[T] = val; + } + #endregion } } diff --git a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs index 5bdaa17f69..b99c71cfcc 100644 --- a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs +++ b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs @@ -113,6 +113,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization // Script state may, or may not, exist. Not having any, is NOT // ever a problem. sceneObject.LoadScriptState(doc); + sceneObject.InitPartComponents(); return sceneObject; } @@ -240,6 +241,8 @@ namespace OpenSim.Region.Framework.Scenes.Serialization // ever a problem. sceneObject.LoadScriptState(doc); + sceneObject.InitPartComponents(); + return sceneObject; } catch (Exception e) diff --git a/OpenSim/Region/OptionalModules/World/TestComponent/TestComponent.cs b/OpenSim/Region/OptionalModules/World/TestComponent/TestComponent.cs new file mode 100644 index 0000000000..7a152a7cf5 --- /dev/null +++ b/OpenSim/Region/OptionalModules/World/TestComponent/TestComponent.cs @@ -0,0 +1,46 @@ +using System; +using System.Reflection; +using log4net; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Scenes.Components; + +namespace OpenSim.Region.OptionalModules.World.TestComponent +{ + class TestComponent : IComponent + { + private int m_theAnswerToTheQuestionOfLifeTheUniverseAndEverything = 42; + + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + #region Implementation of IComponent + + public TestComponent(IComponentState state) + { + m_log.Info("Its alive!"); + } + + public Type BaseType + { + get { return typeof (TestComponent); } + } + + public IComponentState State + { + get + { + ComponentState x = new ComponentState(); + x.Set("Hello","World"); + x.Set("HitchhikersReference", m_theAnswerToTheQuestionOfLifeTheUniverseAndEverything); + return x; + } + } + + public void SetParent(SceneObjectPart part) + { + m_log.Info("My parent's name is: " + part.Name); + } + + #endregion + } +} diff --git a/OpenSim/Region/OptionalModules/World/TestComponent/TestComponentFactoryModule.cs b/OpenSim/Region/OptionalModules/World/TestComponent/TestComponentFactoryModule.cs new file mode 100644 index 0000000000..840a36dd69 --- /dev/null +++ b/OpenSim/Region/OptionalModules/World/TestComponent/TestComponentFactoryModule.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Scenes.Components; + +namespace OpenSim.Region.OptionalModules.World.TestComponent +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] + public class TestComponentFactoryModule : ComponentFactory, ISharedRegionModule + { + private List m_scenes = new List(); + + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + #region Overrides of ComponentFactory + + protected override IComponent CreateComponent(Type componentType, IComponentState componentState) + { + if(componentType == typeof(TestComponent)) + { + string tmp; + if(componentState.TryGet("Hello", out tmp)) + { + m_log.Info("[TestComponentFactory] Successfully recovered '" + tmp + "' from a component via serialisation."); + } + return new TestComponent(componentState); + } + + return null; + } + + #endregion + + #region Implementation of IRegionModuleBase + + public string Name + { + get { return "TestComponentFactoryModule"; } + } + + public Type ReplaceableInterface + { + get { return typeof (TestComponentFactoryModule); } + } + + public void Initialise(IConfigSource source) + { + m_log.Info("[TESTCOMPONENT] Loading test factory..."); + } + + public void Close() + { + + } + + public void AddRegion(Scene scene) + { + + } + + public void RemoveRegion(Scene scene) + { + + } + + public void RegionLoaded(Scene scene) + { + m_log.Info("[TESTCOMPONENT] Loading test factory for " + scene.RegionInfo.RegionName); + m_scenes.Add(scene); + } + + public void PostInitialise() + { + foreach (Scene scene in m_scenes) + { + m_log.Info("[TESTCOMPONENT] Adding new test component to Scene"); + List sogs = scene.Entities.GetAllByType(); + foreach (EntityBase entityBase in sogs) + { + SceneObjectGroup sog = (SceneObjectGroup) entityBase; + m_log.Info("[TESTCOMPONENT] Adding new test component to SOG"); + foreach (SceneObjectPart part in sog.GetParts()) + { + m_log.Info("[TESTCOMPONENT] Adding new test component to SOP"); + part.SetComponent( + new TestComponent( + new ComponentState() + ) + ); + } + } + } + + m_log.Info("[TESTCOMPONENT] Test factory loaded"); + } + + #endregion + } +}