From 9b66108081a8c8cf79faaa6c541554091c40850e Mon Sep 17 00:00:00 2001 From: Dr Scofield Date: Fri, 6 Feb 2009 16:55:34 +0000 Subject: This changeset is the step 1 of 2 in refactoring OpenSim.Region.Environment into a "framework" part and a modules only part. This first changeset refactors OpenSim.Region.Environment.Scenes, OpenSim.Region.Environment.Interfaces, and OpenSim.Region.Interfaces into OpenSim.Region.Framework.{Interfaces,Scenes} leaving only region modules in OpenSim.Region.Environment. The next step will be to move region modules up from OpenSim.Region.Environment.Modules to OpenSim.Region.CoreModules and then sort out which modules are really core modules and which should move out to forge. I've been very careful to NOT BREAK anything. i hope i've succeeded. as this is the work of a whole week i hope i managed to keep track with the applied patches of the last week --- could any of you that did check in stuff have a look at whether it survived? thx! --- .../Interfaces/IAgentAssetTransactions.cs | 49 + .../Region/Framework/Interfaces/IAvatarFactory.cs | 39 + .../Framework/Interfaces/ICapabilitiesModule.cs | 74 + OpenSim/Region/Framework/Interfaces/ICommand.cs | 50 + .../Framework/Interfaces/ICommandableModule.cs | 37 + OpenSim/Region/Framework/Interfaces/ICommander.cs | 47 + .../Region/Framework/Interfaces/IDialogModule.cs | 136 + .../Framework/Interfaces/IDynamicTextureManager.cs | 58 + .../Region/Framework/Interfaces/IEmailModule.cs | 48 + .../Region/Framework/Interfaces/IEntityCreator.cs | 56 + .../Framework/Interfaces/IEntityInventory.cs | 207 + .../Framework/Interfaces/IEstateDataStore.cs | 41 + .../Region/Framework/Interfaces/IEstateModule.cs | 44 + OpenSim/Region/Framework/Interfaces/IEventQueue.cs | 64 + .../Region/Framework/Interfaces/IFriendsModule.cs | 49 + .../Region/Framework/Interfaces/IGroupsModule.cs | 68 + .../Region/Framework/Interfaces/IHttpRequests.cs | 51 + .../Framework/Interfaces/IInterregionComms.cs | 81 + .../Interfaces/IInventoryTransferModule.cs | 42 + OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs | 41 + .../Region/Framework/Interfaces/ILandChannel.cs | 49 + OpenSim/Region/Framework/Interfaces/ILandObject.cs | 83 + .../Framework/Interfaces/IMessageTransferModule.cs | 40 + .../Region/Framework/Interfaces/IPresenceModule.cs | 56 + .../Framework/Interfaces/IRegionArchiverModule.cs | 75 + .../Framework/Interfaces/IRegionDataStore.cs | 109 + .../Region/Framework/Interfaces/IRegionModule.cs | 41 + .../Interfaces/IRegionSerialiserModule.cs | 122 + .../Region/Framework/Interfaces/IScriptModule.cs | 41 + .../Region/Framework/Interfaces/IServiceRequest.cs | 45 + .../Region/Framework/Interfaces/ISoundModule.cs | 41 + OpenSim/Region/Framework/Interfaces/ITerrain.cs | 78 + .../Region/Framework/Interfaces/ITerrainChannel.cs | 48 + .../Region/Framework/Interfaces/ITextureSender.cs | 58 + .../Framework/Interfaces/IVegetationModule.cs | 49 + OpenSim/Region/Framework/Interfaces/IWindModule.cs | 41 + OpenSim/Region/Framework/Interfaces/IWorldComm.cs | 64 + OpenSim/Region/Framework/Interfaces/IXMLRPC.cs | 66 + OpenSim/Region/Framework/Interfaces/IXfer.cs | 34 + OpenSim/Region/Framework/ModuleLoader.cs | 255 ++ OpenSim/Region/Framework/Scenes/Animation.cs | 59 + OpenSim/Region/Framework/Scenes/AnimationSet.cs | 155 + .../Scenes/AsyncSceneObjectGroupDeleter.cs | 165 + .../Region/Framework/Scenes/AvatarAnimations.cs | 63 + OpenSim/Region/Framework/Scenes/EntityBase.cs | 253 ++ OpenSim/Region/Framework/Scenes/EntityManager.cs | 279 ++ OpenSim/Region/Framework/Scenes/EventManager.cs | 981 +++++ .../Framework/Scenes/Hypergrid/HGAssetMapper.cs | 376 ++ .../Scenes/Hypergrid/HGScene.Inventory.cs | 152 + .../Region/Framework/Scenes/Hypergrid/HGScene.cs | 84 + .../Hypergrid/HGSceneCommunicationService.cs | 360 ++ .../Region/Framework/Scenes/IScenePresenceBody.cs | 37 + OpenSim/Region/Framework/Scenes/ReturnInfo.cs | 39 + OpenSim/Region/Framework/Scenes/Scene.Inventory.cs | 2665 ++++++++++++ .../Framework/Scenes/Scene.PacketHandlers.cs | 632 +++ .../Region/Framework/Scenes/Scene.Permissions.cs | 1334 ++++++ OpenSim/Region/Framework/Scenes/Scene.cs | 4239 ++++++++++++++++++++ OpenSim/Region/Framework/Scenes/SceneBase.cs | 419 ++ .../Framework/Scenes/SceneCommunicationService.cs | 1095 +++++ OpenSim/Region/Framework/Scenes/SceneGraph.cs | 1814 +++++++++ OpenSim/Region/Framework/Scenes/SceneManager.cs | 669 +++ .../Framework/Scenes/SceneObjectGroup.Inventory.cs | 401 ++ .../Region/Framework/Scenes/SceneObjectGroup.cs | 3012 ++++++++++++++ OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | 3826 ++++++++++++++++++ .../Framework/Scenes/SceneObjectPartInventory.cs | 891 ++++ OpenSim/Region/Framework/Scenes/ScenePresence.cs | 3649 +++++++++++++++++ OpenSim/Region/Framework/Scenes/SceneXmlLoader.cs | 289 ++ .../Framework/Scenes/Scripting/IScriptHost.cs | 46 + .../Framework/Scenes/Scripting/NullScriptHost.cs | 86 + .../Scenes/Scripting/ScriptEngineInterface.cs | 38 + .../Scenes/Scripting/ScriptEngineLoader.cs | 119 + .../Region/Framework/Scenes/SimStatsReporter.cs | 450 +++ OpenSim/Region/Framework/Scenes/TerrainChannel.cs | 242 ++ OpenSim/Region/Framework/Scenes/TerrainUtil.cs | 132 + .../Framework/Scenes/Tests/EntityManagerTests.cs | 176 + .../Scenes/Tests/SceneObjectBasicTests.cs | 139 + .../Scenes/Tests/SceneObjectLinkingTests.cs | 249 ++ .../Framework/Scenes/Tests/ScenePresenceTests.cs | 292 ++ .../Scenes/Tests/StandaloneTeleportTests.cs | 105 + .../Framework/Scenes/Types/BasicQuadTreeNode.cs | 269 ++ .../Region/Framework/Scenes/Types/UpdateQueue.cs | 134 + OpenSim/Region/Framework/Scenes/UndoState.cs | 116 + OpenSim/Region/Framework/StorageManager.cs | 96 + 83 files changed, 33304 insertions(+) create mode 100644 OpenSim/Region/Framework/Interfaces/IAgentAssetTransactions.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IAvatarFactory.cs create mode 100644 OpenSim/Region/Framework/Interfaces/ICapabilitiesModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/ICommand.cs create mode 100644 OpenSim/Region/Framework/Interfaces/ICommandableModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/ICommander.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IDialogModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IDynamicTextureManager.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IEmailModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IEntityCreator.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IEntityInventory.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IEstateDataStore.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IEstateModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IEventQueue.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IFriendsModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IGroupsModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IHttpRequests.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IInterregionComms.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IInventoryTransferModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs create mode 100644 OpenSim/Region/Framework/Interfaces/ILandChannel.cs create mode 100644 OpenSim/Region/Framework/Interfaces/ILandObject.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IMessageTransferModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IPresenceModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IRegionArchiverModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IRegionDataStore.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IRegionModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IRegionSerialiserModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IScriptModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IServiceRequest.cs create mode 100644 OpenSim/Region/Framework/Interfaces/ISoundModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/ITerrain.cs create mode 100644 OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs create mode 100644 OpenSim/Region/Framework/Interfaces/ITextureSender.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IVegetationModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IWindModule.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IWorldComm.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IXMLRPC.cs create mode 100644 OpenSim/Region/Framework/Interfaces/IXfer.cs create mode 100644 OpenSim/Region/Framework/ModuleLoader.cs create mode 100644 OpenSim/Region/Framework/Scenes/Animation.cs create mode 100644 OpenSim/Region/Framework/Scenes/AnimationSet.cs create mode 100644 OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs create mode 100644 OpenSim/Region/Framework/Scenes/AvatarAnimations.cs create mode 100644 OpenSim/Region/Framework/Scenes/EntityBase.cs create mode 100644 OpenSim/Region/Framework/Scenes/EntityManager.cs create mode 100644 OpenSim/Region/Framework/Scenes/EventManager.cs create mode 100644 OpenSim/Region/Framework/Scenes/Hypergrid/HGAssetMapper.cs create mode 100644 OpenSim/Region/Framework/Scenes/Hypergrid/HGScene.Inventory.cs create mode 100644 OpenSim/Region/Framework/Scenes/Hypergrid/HGScene.cs create mode 100644 OpenSim/Region/Framework/Scenes/Hypergrid/HGSceneCommunicationService.cs create mode 100644 OpenSim/Region/Framework/Scenes/IScenePresenceBody.cs create mode 100644 OpenSim/Region/Framework/Scenes/ReturnInfo.cs create mode 100644 OpenSim/Region/Framework/Scenes/Scene.Inventory.cs create mode 100644 OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs create mode 100644 OpenSim/Region/Framework/Scenes/Scene.Permissions.cs create mode 100644 OpenSim/Region/Framework/Scenes/Scene.cs create mode 100644 OpenSim/Region/Framework/Scenes/SceneBase.cs create mode 100644 OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs create mode 100644 OpenSim/Region/Framework/Scenes/SceneGraph.cs create mode 100644 OpenSim/Region/Framework/Scenes/SceneManager.cs create mode 100644 OpenSim/Region/Framework/Scenes/SceneObjectGroup.Inventory.cs create mode 100644 OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs create mode 100644 OpenSim/Region/Framework/Scenes/SceneObjectPart.cs create mode 100644 OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs create mode 100644 OpenSim/Region/Framework/Scenes/ScenePresence.cs create mode 100644 OpenSim/Region/Framework/Scenes/SceneXmlLoader.cs create mode 100644 OpenSim/Region/Framework/Scenes/Scripting/IScriptHost.cs create mode 100644 OpenSim/Region/Framework/Scenes/Scripting/NullScriptHost.cs create mode 100644 OpenSim/Region/Framework/Scenes/Scripting/ScriptEngineInterface.cs create mode 100644 OpenSim/Region/Framework/Scenes/Scripting/ScriptEngineLoader.cs create mode 100644 OpenSim/Region/Framework/Scenes/SimStatsReporter.cs create mode 100644 OpenSim/Region/Framework/Scenes/TerrainChannel.cs create mode 100644 OpenSim/Region/Framework/Scenes/TerrainUtil.cs create mode 100644 OpenSim/Region/Framework/Scenes/Tests/EntityManagerTests.cs create mode 100644 OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs create mode 100644 OpenSim/Region/Framework/Scenes/Tests/SceneObjectLinkingTests.cs create mode 100644 OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs create mode 100644 OpenSim/Region/Framework/Scenes/Tests/StandaloneTeleportTests.cs create mode 100644 OpenSim/Region/Framework/Scenes/Types/BasicQuadTreeNode.cs create mode 100644 OpenSim/Region/Framework/Scenes/Types/UpdateQueue.cs create mode 100644 OpenSim/Region/Framework/Scenes/UndoState.cs create mode 100644 OpenSim/Region/Framework/StorageManager.cs (limited to 'OpenSim/Region/Framework') diff --git a/OpenSim/Region/Framework/Interfaces/IAgentAssetTransactions.cs b/OpenSim/Region/Framework/Interfaces/IAgentAssetTransactions.cs new file mode 100644 index 0000000..49a5dd0 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IAgentAssetTransactions.cs @@ -0,0 +1,49 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IAgentAssetTransactions + { + void HandleItemUpdateFromTransaction(IClientAPI remoteClient, UUID transactionID, + InventoryItemBase item); + + void HandleItemCreationFromTransaction(IClientAPI remoteClient, UUID transactionID, UUID folderID, + uint callbackID, string description, string name, sbyte invType, + sbyte type, byte wearableType, uint nextOwnerMask); + + void HandleTaskItemUpdateFromTransaction( + IClientAPI remoteClient, SceneObjectPart part, UUID transactionID, TaskInventoryItem item); + + void RemoveAgentAssetTransactions(UUID userID); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IAvatarFactory.cs b/OpenSim/Region/Framework/Interfaces/IAvatarFactory.cs new file mode 100644 index 0000000..b533206 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IAvatarFactory.cs @@ -0,0 +1,39 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IAvatarFactory + { + bool TryGetAvatarAppearance(UUID avatarId, out AvatarAppearance appearance); + void UpdateDatabase(UUID userID, AvatarAppearance avatAppearance); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/ICapabilitiesModule.cs b/OpenSim/Region/Framework/Interfaces/ICapabilitiesModule.cs new file mode 100644 index 0000000..bac0b2e --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/ICapabilitiesModule.cs @@ -0,0 +1,74 @@ +/* + * 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 OpenSim 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; +using OpenSim.Framework; +using Caps = OpenSim.Framework.Communications.Capabilities.Caps; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface ICapabilitiesModule + { + void NewUserConnection(AgentCircuitData agent); + + /// + /// Add a caps handler for the given agent. If the CAPS handler already exists for this agent, + /// then it is replaced by a new CAPS handler. + /// + /// FIXME: On login this is called twice, once for the login and once when the connection is made. + /// This is somewhat innefficient and should be fixed. The initial login creation is necessary + /// since the client asks for capabilities immediately after being informed of the seed. + /// + /// + /// + void AddCapsHandler(UUID agentId); + + /// + /// Remove the caps handler for a given agent. + /// + /// + void RemoveCapsHandler(UUID agentId); + + /// + /// Will return null if the agent doesn't have a caps handler registered + /// + /// + Caps GetCapsHandlerForUser(UUID agentId); + + Dictionary GetChildrenSeeds(UUID agentID); + + string GetChildSeed(UUID agentID, ulong handle); + + void SetChildrenSeed(UUID agentID, Dictionary seeds); + + void DropChildSeed(UUID agentID, ulong handle); + + string GetCapsPath(UUID agentId); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/ICommand.cs b/OpenSim/Region/Framework/Interfaces/ICommand.cs new file mode 100644 index 0000000..1551b58 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/ICommand.cs @@ -0,0 +1,50 @@ +/* + * 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 OpenSim 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.Collections.Generic; + +namespace OpenSim.Region.Framework.Interfaces +{ + public enum CommandIntentions + { + COMMAND_STATISTICAL, + COMMAND_NON_HAZARDOUS, + COMMAND_HAZARDOUS + }; + + public interface ICommand + { + void AddArgument(string name, string helptext, string type); + Dictionary Arguments { get; } + string Help { get; } + string Name { get; } + CommandIntentions Intentions { get; } + + void Run(object[] args); + void ShowConsoleHelp(); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/ICommandableModule.cs b/OpenSim/Region/Framework/Interfaces/ICommandableModule.cs new file mode 100644 index 0000000..5d6660d --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/ICommandableModule.cs @@ -0,0 +1,37 @@ +/* + * 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 OpenSim 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. + */ + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface ICommandableModule + { + ICommander CommandInterface + { + get; + } + } +} diff --git a/OpenSim/Region/Framework/Interfaces/ICommander.cs b/OpenSim/Region/Framework/Interfaces/ICommander.cs new file mode 100644 index 0000000..17a2e4a --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/ICommander.cs @@ -0,0 +1,47 @@ +/* + * 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 OpenSim 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. + */ + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface ICommander + { + /// + /// The name of this commander + /// + string Name { get; } + + /// + /// Provide general help information about this commander. + /// + string Help { get; } + + void ProcessConsoleCommand(string function, string[] args); + void RegisterCommand(string commandName, ICommand command); + void Run(string function, object[] args); + string GenerateRuntimeAPI(); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IDialogModule.cs b/OpenSim/Region/Framework/Interfaces/IDialogModule.cs new file mode 100644 index 0000000..7247d52 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IDialogModule.cs @@ -0,0 +1,136 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSim 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 OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IDialogModule + { + /// + /// Send a non-modal alert message to a particular user. This can disappear from the user's view after a + /// small interval. + /// + /// + /// + void SendAlertToUser(IClientAPI client, string message); + + /// + /// Send an alert message to a particular user. + /// + /// + /// + /// + void SendAlertToUser(IClientAPI client, string message, bool modal); + + /// + /// Send a non-modal alert message to a particular user. + /// + /// + /// + void SendAlertToUser(UUID agentID, string message); + + /// + /// Send an alert message to a particular user. + /// + /// + /// + /// + void SendAlertToUser(UUID agentID, string message, bool modal); + + /// + /// Send an alert message to a particular user. + /// + /// + /// + /// + /// + void SendAlertToUser(string firstName, string lastName, string message, bool modal); + + /// + /// Send an alert message to all users in the scene. + /// + /// + void SendGeneralAlert(string message); + + /// + /// Send a dialog box to a particular user. + /// + /// + /// + /// + /// + /// + /// + /// + /// + void SendDialogToUser( + UUID avatarID, string objectName, UUID objectID, UUID ownerID, + string message, UUID textureID, int ch, string[] buttonlabels); + + /// + /// Send a url to a particular user. + /// + /// + /// + /// + /// + /// + /// + /// + void SendUrlToUser( + UUID avatarID, string objectName, UUID objectID, UUID ownerID, bool groupOwned, string message, string url); + + /// + /// Send a notification to all users in the scene. This notification should remain around until the + /// user explicitly dismisses it. + /// + /// + /// On the Linden Labs Second Client (as of 1.21), this is a big blue box message on the upper right of the + /// screen. + /// + /// The user sending the message + /// The name of the user doing the sending + /// The message being sent to the user + void SendNotificationToUsersInRegion(UUID fromAvatarID, string fromAvatarName, string message); + + /// + /// Send a notification to all users in the estate. This notification should remain around until the + /// user explicitly dismisses it. + /// + /// + /// On the Linden Labs Second Client (as of 1.21), this is a big blue box message on the upper right of the + /// screen. + /// + /// The user sending the message + /// The name of the user doing the sending + /// The message being sent to the user + void SendNotificationToUsersInEstate(UUID fromAvatarID, string fromAvatarName, string message); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IDynamicTextureManager.cs b/OpenSim/Region/Framework/Interfaces/IDynamicTextureManager.cs new file mode 100644 index 0000000..e119bc3 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IDynamicTextureManager.cs @@ -0,0 +1,58 @@ +/* + * 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 OpenSim 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.IO; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IDynamicTextureManager + { + void RegisterRender(string handleType, IDynamicTextureRender render); + void ReturnData(UUID id, byte[] data); + + UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url, string extraParams, + int updateTimer); + UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url, string extraParams, + int updateTimer, bool SetBlending, byte AlphaValue); + UUID AddDynamicTextureData(UUID simID, UUID primID, string contentType, string data, string extraParams, + int updateTimer); + UUID AddDynamicTextureData(UUID simID, UUID primID, string contentType, string data, string extraParams, + int updateTimer, bool SetBlending, byte AlphaValue); + } + + public interface IDynamicTextureRender + { + string GetName(); + string GetContentType(); + bool SupportsAsynchronous(); + byte[] ConvertUrl(string url, string extraParams); + byte[] ConvertStream(Stream data, string extraParams); + bool AsyncConvertUrl(UUID id, string url, string extraParams); + bool AsyncConvertData(UUID id, string bodyData, string extraParams); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IEmailModule.cs b/OpenSim/Region/Framework/Interfaces/IEmailModule.cs new file mode 100644 index 0000000..b5f885a --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IEmailModule.cs @@ -0,0 +1,48 @@ +/* + * 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 OpenSim 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 OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Interfaces +{ + public class Email + { + public string time; + public string sender; + public string subject; + public string message; + public int numLeft; + } + + public interface IEmailModule : IRegionModule + { + void SendEmail(UUID objectID, string address, string subject, string body); + Email GetNextEmail(UUID objectID, string sender, string subject); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IEntityCreator.cs b/OpenSim/Region/Framework/Interfaces/IEntityCreator.cs new file mode 100644 index 0000000..1a6e626 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IEntityCreator.cs @@ -0,0 +1,56 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Interfaces +{ + /// + /// Interface to a class that is capable of creating entities + /// + public interface IEntityCreator + { + /// + /// The entities that this class is capable of creating. These match the PCode format. + /// + /// + PCode[] CreationCapabilities { get; } + + /// + /// Create an entity + /// + /// + /// + /// + /// + /// + /// The entity created, or null if the creation failed + SceneObjectGroup CreateEntity(UUID ownerID, UUID groupID, Vector3 pos, Quaternion rot, PrimitiveBaseShape shape); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs b/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs new file mode 100644 index 0000000..20fe090 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs @@ -0,0 +1,207 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using log4net; +using OpenSim.Framework; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes.Scripting; + +namespace OpenSim.Region.Framework.Interfaces +{ + /// + /// Interface to an entity's (SceneObjectPart's) inventory + /// + /// + /// This is not a finished 1.0 candidate interface + public interface IEntityInventory + { + /// + /// Force the task inventory of this prim to persist at the next update sweep + /// + void ForceInventoryPersistence(); + + /// + /// Reset UUIDs for all the items in the prim's inventory. + /// + /// + /// This involves either generating + /// new ones or setting existing UUIDs to the correct parent UUIDs. + /// + /// If this method is called and there are inventory items, then we regard the inventory as having changed. + /// + /// Link number for the part + void ResetInventoryIDs(); + + /// + /// Change every item in this inventory to a new owner. + /// + /// + void ChangeInventoryOwner(UUID ownerId); + + /// + /// Change every item in this inventory to a new group. + /// + /// + void ChangeInventoryGroup(UUID groupID); + + /// + /// Start all the scripts contained in this entity's inventory + /// + void CreateScriptInstances(int startParam, bool postOnRez, string engine, int stateSource); + + /// + /// Stop all the scripts in this entity. + /// + void RemoveScriptInstances(); + + /// + /// Start a script which is in this entity's inventory. + /// + /// + /// + /// + /// + void CreateScriptInstance( + TaskInventoryItem item, int startParam, bool postOnRez, string engine, int stateSource); + + /// + /// Start a script which is in this entity's inventory. + /// + /// + /// + /// + /// + /// + void CreateScriptInstance(UUID itemId, int startParam, bool postOnRez, string engine, int stateSource); + + /// + /// Stop a script which is in this prim's inventory. + /// + /// + void RemoveScriptInstance(UUID itemId); + + /// + /// Add an item to this entity's inventory. If an item with the same name already exists, then an alternative + /// name is chosen. + /// + /// + void AddInventoryItem(TaskInventoryItem item, bool allowedDrop); + + /// + /// Add an item to this entity's inventory. If an item with the same name already exists, it is replaced. + /// + /// + void AddInventoryItemExclusive(TaskInventoryItem item, bool allowedDrop); + + /// + /// Restore a whole collection of items to the entity's inventory at once. + /// We assume that the items already have all their fields correctly filled out. + /// The items are not flagged for persistence to the database, since they are being restored + /// from persistence rather than being newly added. + /// + /// + void RestoreInventoryItems(ICollection items); + + /// + /// Returns an existing inventory item. Returns the original, so any changes will be live. + /// + /// + /// null if the item does not exist + TaskInventoryItem GetInventoryItem(UUID itemId); + + /// + /// Update an existing inventory item. + /// + /// The updated item. An item with the same id must already exist + /// in this prim's inventory. + /// false if the item did not exist, true if the update occurred successfully + bool UpdateInventoryItem(TaskInventoryItem item); + + /// + /// Remove an item from this entity's inventory + /// + /// + /// Numeric asset type of the item removed. Returns -1 if the item did not exist + /// in this prim's inventory. + int RemoveInventoryItem(UUID itemID); + + /// + /// Return the name with which a client can request a xfer of this prim's inventory metadata + /// + string GetInventoryFileName(); + + bool GetInventoryFileName(IClientAPI client, uint localID); + + /// + /// Serialize all the metadata for the items in this prim's inventory ready for sending to the client + /// + /// + void RequestInventoryFile(IClientAPI client, IXfer xferManager); + + /// + /// Backup the inventory to the given data store + /// + /// + void ProcessInventoryBackup(IRegionDataStore datastore); + + uint MaskEffectivePermissions(); + + void ApplyNextOwnerPermissions(); + + void ApplyGodPermissions(uint perms); + + /// + /// Returns true if this inventory contains any scripts + /// + bool ContainsScripts(); + + /// + /// Get the uuids of all items in this inventory + /// + /// + List GetInventoryList(); + + /// + /// Get the names of the assemblies associated with scripts in this inventory. + /// + /// + string[] GetScriptAssemblies(); + + /// + /// Get the xml representing the saved states of scripts in this inventory. + /// + /// + /// A + /// + Dictionary GetScriptStates(); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IEstateDataStore.cs b/OpenSim/Region/Framework/Interfaces/IEstateDataStore.cs new file mode 100644 index 0000000..4da115e --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IEstateDataStore.cs @@ -0,0 +1,41 @@ +/* + * 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 OpenSim 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 OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IEstateDataStore + { + void Initialise(string connectstring); + + EstateSettings LoadEstateSettings(UUID regionID); + void StoreEstateSettings(EstateSettings es); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IEstateModule.cs b/OpenSim/Region/Framework/Interfaces/IEstateModule.cs new file mode 100644 index 0000000..f169f61 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IEstateModule.cs @@ -0,0 +1,44 @@ +/* + * 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 OpenSim 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 OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IEstateModule : IRegionModule + { + uint GetRegionFlags(); + bool IsManager(UUID avatarID); + + /// + /// Tell all clients about the current state of the region (terrain textures, water height, etc.). + /// + void sendRegionHandshakeToAll(); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IEventQueue.cs b/OpenSim/Region/Framework/Interfaces/IEventQueue.cs new file mode 100644 index 0000000..cf609ae --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IEventQueue.cs @@ -0,0 +1,64 @@ +/* + * 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 OpenSim 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.Net; +using OpenSim.Framework; +using OpenMetaverse; +using OpenMetaverse.Packets; +using OpenMetaverse.StructuredData; +using System.Collections.Generic; +using System.Text; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IEventQueue + { + bool Enqueue(OSD o, UUID avatarID); + + // These are required to decouple Scenes from EventQueueHelper + void DisableSimulator(ulong handle, UUID avatarID); + void EnableSimulator(ulong handle, IPEndPoint endPoint, UUID avatarID); + void EstablishAgentCommunication(UUID avatarID, IPEndPoint endPoint, + string capsPath); + void TeleportFinishEvent(ulong regionHandle, byte simAccess, + IPEndPoint regionExternalEndPoint, + uint locationID, uint flags, string capsURL, + UUID agentID); + void CrossRegion(ulong handle, Vector3 pos, Vector3 lookAt, + IPEndPoint newRegionExternalEndPoint, + string capsURL, UUID avatarID, UUID sessionID); + void ChatterboxInvitation(UUID sessionID, string sessionName, + UUID fromAgent, string message, UUID toAgent, string fromName, byte dialog, + uint timeStamp, bool offline, int parentEstateID, Vector3 position, + uint ttl, UUID transactionID, bool fromGroup, byte[] binaryBucket); + void ChatterBoxSessionAgentListUpdates(UUID sessionID, UUID fromAgent, UUID toAgent, bool canVoiceChat, + bool isModerator, bool textMute); + void ParcelProperties(ParcelPropertiesPacket parcelPropertiesPacket, UUID avatarID); + void GroupMembership(AgentGroupDataUpdatePacket groupUpdate, UUID avatarID); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IFriendsModule.cs b/OpenSim/Region/Framework/Interfaces/IFriendsModule.cs new file mode 100644 index 0000000..0bdfb8f --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IFriendsModule.cs @@ -0,0 +1,49 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IFriendsModule + { + /// + /// Offer a friendship to a user from the server end rather than by direct initiation from a client. + /// + /// + /// A user with this id must existing in the user data store, but need not be logged on. + /// + /// + /// An actually logged in client to which the offer is being made. + /// FIXME: This is somewhat too tightly coupled - it should arguably be possible to offer friendships even if the + /// receiving user is not currently online. + /// + /// + void OfferFriendship(UUID fromUserId, IClientAPI toUserClient, string offerMessage); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IGroupsModule.cs b/OpenSim/Region/Framework/Interfaces/IGroupsModule.cs new file mode 100644 index 0000000..16527d1 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IGroupsModule.cs @@ -0,0 +1,68 @@ +/* + * 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 OpenSim 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 OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Interfaces +{ + public delegate void NewGroupNotice(UUID groupID, UUID noticeID); + + public interface IGroupsModule + { + event NewGroupNotice OnNewGroupNotice; + + void ActivateGroup(IClientAPI remoteClient, UUID groupID); + List GroupTitlesRequest(IClientAPI remoteClient, UUID groupID); + List GroupMembersRequest(IClientAPI remoteClient, UUID groupID); + List GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID); + List GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID); + GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID); + GroupMembershipData[] GetMembershipData(UUID UserID); + + void UpdateGroupInfo(IClientAPI remoteClient, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish); + + void SetGroupAcceptNotices(IClientAPI remoteClient, UUID groupID, bool acceptNotices, bool listInProfile); + + void GroupTitleUpdate(IClientAPI remoteClient, UUID GroupID, UUID TitleRoleID); + UUID CreateGroup(IClientAPI remoteClient, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish); + + GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID GroupID); + string GetGroupTitle(UUID avatarID); + void GroupRoleUpdate(IClientAPI remoteClient, UUID GroupID, UUID RoleID, string name, string description, string title, ulong powers, byte updateType); + void GroupRoleChanges(IClientAPI remoteClient, UUID GroupID, UUID RoleID, UUID MemberID, uint changes); + void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID); + void SendGroupNoticeToClient(IClientAPI remoteClient, UUID groupNoticeID, byte dialog); + void SendAgentGroupDataUpdate(IClientAPI remoteClient); + void JoinGroupRequest(IClientAPI remoteClient, UUID GroupID); + void LeaveGroupRequest(IClientAPI remoteClient, UUID GroupID); + void EjectGroupMemberRequest(IClientAPI remoteClient, UUID GroupID, UUID EjecteeID); + void InviteGroupRequest(IClientAPI remoteClient, UUID GroupID, UUID InviteeID, UUID RoleID); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs b/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs new file mode 100644 index 0000000..fd49757 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs @@ -0,0 +1,51 @@ +/* + * 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 OpenSim 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.Net; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Interfaces +{ + public enum HttpRequestConstants + { + HTTP_METHOD = 0, + HTTP_MIMETYPE = 1, + HTTP_BODY_MAXLENGTH = 2, + HTTP_VERIFY_CERT = 3, + } + + public interface IHttpRequestModule + { + UUID MakeHttpRequest(string url, string parameters, string body); + UUID StartHttpRequest(uint localID, UUID itemID, string url, List parameters, Dictionary headers, string body); + void StopHttpRequest(uint m_localID, UUID m_itemID); + IServiceRequest GetNextCompletedRequest(); + void RemoveCompletedRequest(UUID id); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IInterregionComms.cs b/OpenSim/Region/Framework/Interfaces/IInterregionComms.cs new file mode 100644 index 0000000..783b3f9 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IInterregionComms.cs @@ -0,0 +1,81 @@ +/* + * 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 OpenSim 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 OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Interfaces +{ + public delegate bool ChildAgentUpdateReceived(AgentData data); + + public interface IInterregionCommsOut + { + bool SendCreateChildAgent(ulong regionHandle, AgentCircuitData aCircuit); + + /// + /// Full child agent update. + /// + /// + /// + /// + bool SendChildAgentUpdate(ulong regionHandle, AgentData data); + + /// + /// Short child agent update, mostly for position. + /// + /// + /// + /// + bool SendChildAgentUpdate(ulong regionHandle, AgentPosition data); + + /// + /// Message from receiving region to departing region, telling it got contacted by the client. + /// When sent over REST, it invokes the opaque uri. + /// + /// + /// + /// + /// + bool SendReleaseAgent(ulong regionHandle, UUID id, string uri); + + /// + /// Close agent. + /// + /// + /// + /// + bool SendCloseAgent(ulong regionHandle, UUID id); + } + + // This may not be needed, but having it here for now. + public interface IInterregionCommsIn + { + event ChildAgentUpdateReceived OnChildAgentUpdate; + } + +} diff --git a/OpenSim/Region/Framework/Interfaces/IInventoryTransferModule.cs b/OpenSim/Region/Framework/Interfaces/IInventoryTransferModule.cs new file mode 100644 index 0000000..4314271 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IInventoryTransferModule.cs @@ -0,0 +1,42 @@ +/* + * 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 OpenSim 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.Collections.Generic; +using OpenMetaverse; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Interfaces +{ + /// + /// An interface for a module that manages inter-agent inventory offers and transfers. + /// + public interface IInventoryTransferModule + { + void SetRootAgentScene(UUID agentID, Scene scene); + bool NeedSceneCacheClear(UUID agentID, Scene scene); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs b/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs new file mode 100644 index 0000000..1d10896 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs @@ -0,0 +1,41 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using OpenMetaverse.Imaging; + +namespace OpenSim.Region.Framework.Interfaces +{ + + public delegate void DecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers); + + public interface IJ2KDecoder + { + void decode(UUID AssetId, byte[] assetData, DecodedCallback decodedReturn); + void syncdecode(UUID AssetId, byte[] j2kdata); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/ILandChannel.cs b/OpenSim/Region/Framework/Interfaces/ILandChannel.cs new file mode 100644 index 0000000..35c2fc1 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/ILandChannel.cs @@ -0,0 +1,49 @@ +/* + * 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 OpenSim 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.Collections.Generic; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface ILandChannel + { + List ParcelsNearPoint(Vector3 position); + List AllParcels(); + ILandObject GetLandObject(int x, int y); + ILandObject GetLandObject(float x, float y); + bool IsLandPrimCountTainted(); + bool IsForcefulBansAllowed(); + void UpdateLandObject(int localID, LandData data); + void ReturnObjectsInParcel(int localID, uint returnType, UUID[] agentIDs, UUID[] taskIDs, IClientAPI remoteClient); + void setParcelObjectMaxOverride(overrideParcelMaxPrimCountDelegate overrideDel); + void setSimulatorObjectMaxOverride(overrideSimulatorMaxPrimCountDelegate overrideDel); + void SetParcelOtherCleanTime(IClientAPI remoteClient, int localID, int otherCleanTime); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/ILandObject.cs b/OpenSim/Region/Framework/Interfaces/ILandObject.cs new file mode 100644 index 0000000..52cc6cf --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/ILandObject.cs @@ -0,0 +1,83 @@ +/* + * 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 OpenSim 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.Collections.Generic; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Interfaces +{ + public delegate int overrideParcelMaxPrimCountDelegate(ILandObject obj); + public delegate int overrideSimulatorMaxPrimCountDelegate(ILandObject obj); + + public interface ILandObject + { + int getParcelMaxPrimCount(ILandObject thisObject); + int getSimulatorMaxPrimCount(ILandObject thisObject); + + LandData landData { get; set; } + bool[,] landBitmap { get; set; } + UUID regionUUID { get; } + bool containsPoint(int x, int y); + ILandObject Copy(); + + void sendLandUpdateToAvatarsOverMe(); + + void sendLandProperties(int sequence_id, bool snap_selection, int request_result, IClientAPI remote_client); + void updateLandProperties(LandUpdateArgs args, IClientAPI remote_client); + bool isEitherBannedOrRestricted(UUID avatar); + bool isBannedFromLand(UUID avatar); + bool isRestrictedFromLand(UUID avatar); + void sendLandUpdateToClient(IClientAPI remote_client); + List createAccessListArrayByFlag(AccessList flag); + void sendAccessList(UUID agentID, UUID sessionID, uint flags, int sequenceID, IClientAPI remote_client); + void updateAccessList(uint flags, List entries, IClientAPI remote_client); + void updateLandBitmapByteArray(); + void setLandBitmapFromByteArray(); + bool[,] getLandBitmap(); + void forceUpdateLandInfo(); + void setLandBitmap(bool[,] bitmap); + + + bool[,] basicFullRegionLandBitmap(); + bool[,] getSquareLandBitmap(int start_x, int start_y, int end_x, int end_y); + bool[,] modifyLandBitmapSquare(bool[,] land_bitmap, int start_x, int start_y, int end_x, int end_y, bool set_value); + bool[,] mergeLandBitmaps(bool[,] bitmap_base, bool[,] bitmap_add); + void sendForceObjectSelect(int local_id, int request_type, List returnIDs, IClientAPI remote_client); + void sendLandObjectOwners(IClientAPI remote_client); + void returnObject(SceneObjectGroup obj); + void returnLandObjects(uint type, UUID[] owners, UUID[] tasks, IClientAPI remote_client); + void resetLandPrimCounts(); + void addPrimToCount(SceneObjectGroup obj); + void removePrimFromCount(SceneObjectGroup obj); + void updateLandSold(UUID avatarID, UUID groupID, bool groupOwned, uint AuctionID, int claimprice, int area); + + void setParcelObjectMaxOverride(overrideParcelMaxPrimCountDelegate overrideDel); + void setSimulatorObjectMaxOverride(overrideSimulatorMaxPrimCountDelegate overrideDel); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IMessageTransferModule.cs b/OpenSim/Region/Framework/Interfaces/IMessageTransferModule.cs new file mode 100644 index 0000000..f0a5473 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IMessageTransferModule.cs @@ -0,0 +1,40 @@ +/* + * 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 OpenSim 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 OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Interfaces +{ + public delegate void MessageResultNotification(bool success); + + public interface IMessageTransferModule + { + void SendInstantMessage(GridInstantMessage im, MessageResultNotification result); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IPresenceModule.cs b/OpenSim/Region/Framework/Interfaces/IPresenceModule.cs new file mode 100644 index 0000000..011f8bb --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IPresenceModule.cs @@ -0,0 +1,56 @@ +/* + * 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 OpenSim 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 OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Interfaces +{ + public struct PresenceInfo + { + public UUID userID; + public UUID regionID; + + public PresenceInfo(UUID userID, UUID regionID) + { + this.userID = userID; + this.regionID = regionID; + } + } + + public delegate void PresenceChange(PresenceInfo info); + public delegate void BulkPresenceData(PresenceInfo[] info); + + public interface IPresenceModule + { + void RequestBulkPresenceData(UUID[] users); + + event PresenceChange OnPresenceChange; + event BulkPresenceData OnBulkPresenceData; + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IRegionArchiverModule.cs b/OpenSim/Region/Framework/Interfaces/IRegionArchiverModule.cs new file mode 100644 index 0000000..97afe8d --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IRegionArchiverModule.cs @@ -0,0 +1,75 @@ +/* + * 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 OpenSim 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.IO; + +namespace OpenSim.Region.Framework.Interfaces +{ + /// + /// Interface to region archive functionality + /// + public interface IRegionArchiverModule + { + /// + /// Archive the region to the given path + /// + /// + /// This method occurs asynchronously. If you want notification of when it has completed then subscribe to + /// the EventManager.OnOarFileSaved event. + /// + /// + void ArchiveRegion(string savePath); + + /// + /// Archive the region to a stream. + /// + /// + /// This method occurs asynchronously. If you want notification of when it has completed then subscribe to + /// the EventManager.OnOarFileSaved event. + /// + /// + void ArchiveRegion(Stream saveStream); + + /// + /// Dearchive the given region archive into the scene + /// + /// + /// If you want notification of when it has completed then subscribe to the EventManager.OnOarFileLoaded event. + /// + /// + void DearchiveRegion(string loadPath); + + /// + /// Dearchive a region from a stream. + /// + /// + /// If you want notification of when it has completed then subscribe to the EventManager.OnOarFileLoaded event. + /// + /// + void DearchiveRegion(Stream loadStream); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IRegionDataStore.cs b/OpenSim/Region/Framework/Interfaces/IRegionDataStore.cs new file mode 100644 index 0000000..46d81ce --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IRegionDataStore.cs @@ -0,0 +1,109 @@ +/* + * 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 OpenSim 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.Collections.Generic; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IRegionDataStore + { + /// + /// Initialises the data storage engine + /// + /// The file to save the database to (may not be applicable). Alternatively, + /// a connection string for the database + void Initialise(string filename); + + /// + /// Dispose the database + /// + void Dispose(); + + /// + /// Stores all object's details apart from inventory + /// + /// + /// + void StoreObject(SceneObjectGroup obj, UUID regionUUID); + + /// + /// Entirely removes the object, including inventory + /// + /// + /// + /// + void RemoveObject(UUID uuid, UUID regionUUID); + + /// + /// Store a prim's inventory + /// + /// + void StorePrimInventory(UUID primID, ICollection items); + + /// + /// Load persisted objects from region storage. + /// + /// the Region UUID + /// List of loaded groups + List LoadObjects(UUID regionUUID); + + /// + /// Store a terrain revision in region storage + /// + /// HeightField data + /// region UUID + void StoreTerrain(double[,] terrain, UUID regionID); + + /// + /// Load the latest terrain revision from region storage + /// + /// the region UUID + /// Heightfield data + double[,] LoadTerrain(UUID regionID); + + void StoreLandObject(ILandObject Parcel); + + /// + /// + /// delete from land where UUID=globalID + /// delete from landaccesslist where LandUUID=globalID + /// + /// + /// + void RemoveLandObject(UUID globalID); + + List LoadLandObjects(UUID regionUUID); + + void StoreRegionSettings(RegionSettings rs); + RegionSettings LoadRegionSettings(UUID regionUUID); + + void Shutdown(); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IRegionModule.cs b/OpenSim/Region/Framework/Interfaces/IRegionModule.cs new file mode 100644 index 0000000..6a45017 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IRegionModule.cs @@ -0,0 +1,41 @@ +/* + * 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 OpenSim 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 Nini.Config; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IRegionModule + { + void Initialise(Scene scene, IConfigSource source); + void PostInitialise(); + void Close(); + string Name { get; } + bool IsSharedModule { get; } + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IRegionSerialiserModule.cs b/OpenSim/Region/Framework/Interfaces/IRegionSerialiserModule.cs new file mode 100644 index 0000000..40e4c99 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IRegionSerialiserModule.cs @@ -0,0 +1,122 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using System.Collections.Generic; +using System.IO; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IRegionSerialiserModule + { + List SerialiseRegion(Scene scene, string saveDir); + + /// + /// Load prims from the xml format + /// + /// + /// + /// + /// + void LoadPrimsFromXml(Scene scene, string fileName, bool newIDS, Vector3 loadOffset); + + /// + /// Save prims in the xml format + /// + /// + /// + void SavePrimsToXml(Scene scene, string fileName); + + /// + /// Load prims from the xml2 format + /// + /// + /// + void LoadPrimsFromXml2(Scene scene, string fileName); + + /// + /// Load prims from the xml2 format + /// + /// + /// + /// + void LoadPrimsFromXml2(Scene scene, TextReader reader, bool startScripts); + + /// + /// Save prims in the xml2 format + /// + /// + /// + void SavePrimsToXml2(Scene scene, string fileName); + + /// + /// Save prims in the xml2 format, optionally specifying a bounding box for which + /// prims should be saved. If both min and max vectors are Vector3.Zero, then all prims + /// are exported. + /// + /// + /// + /// + /// + void SavePrimsToXml2(Scene scene, TextWriter stream, Vector3 min, Vector3 max); + + /// + /// Save a set of prims in the xml2 format + /// + /// + /// + void SavePrimListToXml2(List entityList, string fileName); + + /// + /// Save a set of prims in the xml2 format, optionally specifying a bounding box for which + /// prims should be saved. If both min and max vectors are Vector3.Zero, then all prims + /// are exported. + /// + /// + /// + /// + /// + void SavePrimListToXml2(List entityList, TextWriter stream, Vector3 min, Vector3 max); + + void SaveNamedPrimsToXml2(Scene scene, string primName, string fileName); + + /// + /// Deserializes a scene object from its xml2 representation. This does not load the object into the scene. + /// + /// + /// The scene object created. null if the scene object already existed + SceneObjectGroup DeserializeGroupFromXml2(string xmlString); + + /// + /// Serialize an individual scene object into the xml2 format + /// + /// + /// + string SaveGroupToXml2(SceneObjectGroup grp); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IScriptModule.cs b/OpenSim/Region/Framework/Interfaces/IScriptModule.cs new file mode 100644 index 0000000..65620d3 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IScriptModule.cs @@ -0,0 +1,41 @@ +/* + * 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 OpenSim 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 OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IScriptModule + { + string ScriptEngineName { get; } + + string GetAssemblyName(UUID itemID); + string GetXMLState(UUID itemID); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IServiceRequest.cs b/OpenSim/Region/Framework/Interfaces/IServiceRequest.cs new file mode 100644 index 0000000..f3283c4 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IServiceRequest.cs @@ -0,0 +1,45 @@ +/* + * 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 OpenSim 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 OpenMetaverse; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IServiceRequest + { + // Status + bool Finished { get; } + + // Request info + UUID ItemID { get; set; } + uint LocalID { get; set; } + UUID ReqID { get; set; } + + void Process(); + void SendRequest(); + void Stop(); + } +} \ No newline at end of file diff --git a/OpenSim/Region/Framework/Interfaces/ISoundModule.cs b/OpenSim/Region/Framework/Interfaces/ISoundModule.cs new file mode 100644 index 0000000..91c80e3 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/ISoundModule.cs @@ -0,0 +1,41 @@ +/* + * 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 OpenSim 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 Nini.Config; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface ISoundModule + { + void PlayAttachedSound(UUID soundID, UUID ownerID, UUID objectID, double gain, Vector3 position, byte flags); + + void TriggerSound( + UUID soundId, UUID ownerID, UUID objectID, UUID parentID, double gain, Vector3 position, UInt64 handle); + } +} \ No newline at end of file diff --git a/OpenSim/Region/Framework/Interfaces/ITerrain.cs b/OpenSim/Region/Framework/Interfaces/ITerrain.cs new file mode 100644 index 0000000..6e9c98d --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/ITerrain.cs @@ -0,0 +1,78 @@ +/* + * 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 OpenSim 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 OpenSim.Framework; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface ITerrain + { + bool Tainted(); + bool Tainted(int x, int y); + void ResetTaint(); + + void ModifyTerrain(float height, float seconds, byte brushsize, byte action, float north, float west, + IClientAPI remoteUser); + + void CheckHeightValues(); + float[] GetHeights1D(); + float[,] GetHeights2D(); + double[,] GetHeights2DD(); + void GetHeights1D(float[] heights); + void SetHeights2D(float[,] heights); + void SetHeights2D(double[,] heights); + void SwapRevertMaps(); + void SaveRevertMap(); + bool RunTerrainCmd(string[] args, ref string resultText, string simName); + void SetRange(float min, float max); + void LoadFromFileF64(string filename); + void LoadFromFileF32(string filename); + void LoadFromFileF32(string filename, int dimensionX, int dimensionY, int lowerboundX, int lowerboundY); + void LoadFromFileIMG(string filename, int dimensionX, int dimensionY, int lowerboundX, int lowerboundY); + void LoadFromFileSLRAW(string filename); + void WriteToFileF64(string filename); + void WriteToFileF32(string filename); + void WriteToFileRAW(string filename); + void WriteToFileHiRAW(string filename); + void SetSeed(int val); + void RaiseTerrain(double rx, double ry, double size, double amount); + void LowerTerrain(double rx, double ry, double size, double amount); + void FlattenTerrain(double rx, double ry, double size, double amount); + void NoiseTerrain(double rx, double ry, double size, double amount); + void RevertTerrain(double rx, double ry, double size, double amount); + void SmoothTerrain(double rx, double ry, double size, double amount); + void HillsGenerator(); + double GetHeight(int x, int y); + void ExportImage(string filename, string gradientmap); + byte[] ExportJpegImage(string gradientmap); + } + + public interface IMapImageGenerator + { + byte[] WriteJpeg2000Image(string gradientmap); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs b/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs new file mode 100644 index 0000000..fc4ced2 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs @@ -0,0 +1,48 @@ +/* + * 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 OpenSim 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. + */ + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface ITerrainChannel + { + int Height { get; } + double this[int x, int y] { get; set; } + int Width { get; } + + /// + /// Squash the entire heightmap into a single dimensioned array + /// + /// + float[] GetFloatsSerialised(); + + double[,] GetDoubles(); + bool Tainted(int x, int y); + ITerrainChannel MakeCopy(); + string SaveToXmlString(); + void LoadFromXmlString(string data); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/ITextureSender.cs b/OpenSim/Region/Framework/Interfaces/ITextureSender.cs new file mode 100644 index 0000000..aab3d26 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/ITextureSender.cs @@ -0,0 +1,58 @@ +/* + * 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 OpenSim 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. + */ + +namespace OpenSim.Region.Framework.Interfaces +{ + /// + /// Interface for an object which can send texture information to a client + /// + public interface ITextureSender + { + /// + /// Are we in the process of sending the texture? + /// + bool Sending { get; set; } + + /// + /// Has the texture send been cancelled? + /// + bool Cancel { get; set; } + + /// + /// Update the non data properties of a texture request + /// + /// + /// + void UpdateRequest(int discardLevel, uint packetNumber); + + /// + /// Send a texture packet to the client. + /// + /// True if the last packet has been sent, false otherwise. + bool SendTexturePacket(); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IVegetationModule.cs b/OpenSim/Region/Framework/Interfaces/IVegetationModule.cs new file mode 100644 index 0000000..537a8dc --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IVegetationModule.cs @@ -0,0 +1,49 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IVegetationModule : IEntityCreator + { + /// + /// Add a new tree to the scene. Used by other modules. + /// + /// + /// + /// + /// + /// + /// + /// + /// + SceneObjectGroup AddTree( + UUID uuid, UUID groupID, Vector3 scale, Quaternion rotation, Vector3 position, Tree treeType, bool newTree); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IWindModule.cs b/OpenSim/Region/Framework/Interfaces/IWindModule.cs new file mode 100644 index 0000000..96b303f --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IWindModule.cs @@ -0,0 +1,41 @@ +/* + * 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 OpenSim 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 OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IWindModule : IRegionModule + { + Vector2[] WindSpeeds + { + get; + } + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IWorldComm.cs b/OpenSim/Region/Framework/Interfaces/IWorldComm.cs new file mode 100644 index 0000000..24865db --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IWorldComm.cs @@ -0,0 +1,64 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using OpenSim.Framework; +// using OpenSim.Region.Environment.Modules.Scripting.WorldComm; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IWorldCommListenerInfo + { + Object[] GetSerializationData(); + UUID GetItemID(); + UUID GetHostID(); + int GetChannel(); + uint GetLocalID(); + int GetHandle(); + string GetMessage(); + string GetName(); + bool IsActive(); + void Deactivate(); + void Activate(); + UUID GetID(); + } + + public interface IWorldComm + { + int Listen(uint LocalID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg); + void DeliverMessage(ChatTypeEnum type, int channel, string name, UUID id, string msg); + bool HasMessages(); + IWorldCommListenerInfo GetNextMessage(); + void ListenControl(UUID itemID, int handle, int active); + void ListenRemove(UUID itemID, int handle); + void DeleteListener(UUID itemID); + Object[] GetSerializationData(UUID itemID); + void CreateFromData(uint localID, UUID itemID, UUID hostID, + Object[] data); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IXMLRPC.cs b/OpenSim/Region/Framework/Interfaces/IXMLRPC.cs new file mode 100644 index 0000000..6570b0a --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IXMLRPC.cs @@ -0,0 +1,66 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using Nwc.XmlRpc; + +// using OpenSim.Region.Environment.Modules.Scripting.XMLRPC; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IXmlRpcRequestInfo + { + bool IsProcessed(); + UUID GetChannelKey(); + void SetProcessed(bool processed); + void SetStrRetval(string resp); + string GetStrRetval(); + void SetIntRetval(int resp); + int GetIntRetval(); + uint GetLocalID(); + UUID GetItemID(); + string GetStrVal(); + int GetIntValue(); + UUID GetMessageID(); + } + + public interface IXMLRPC + { + UUID OpenXMLRPCChannel(uint localID, UUID itemID, UUID channelID); + void CloseXMLRPCChannel(UUID channelKey); + bool hasRequests(); + void RemoteDataReply(string channel, string message_id, string sdata, int idata); + bool IsEnabled(); + IXmlRpcRequestInfo GetNextCompletedRequest(); + void RemoveCompletedRequest(UUID id); + void DeleteChannels(UUID itemID); + UUID SendRemoteData(uint localID, UUID itemID, string channel, string dest, int idata, string sdata); + IServiceRequest GetNextCompletedSRDRequest(); + void RemoveCompletedSRDRequest(UUID id); + void CancelSRDRequests(UUID itemID); + } +} diff --git a/OpenSim/Region/Framework/Interfaces/IXfer.cs b/OpenSim/Region/Framework/Interfaces/IXfer.cs new file mode 100644 index 0000000..a83f166 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IXfer.cs @@ -0,0 +1,34 @@ +/* + * 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 OpenSim 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. + */ + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IXfer + { + bool AddNewFile(string fileName, byte[] data); + } +} diff --git a/OpenSim/Region/Framework/ModuleLoader.cs b/OpenSim/Region/Framework/ModuleLoader.cs new file mode 100644 index 0000000..d393919 --- /dev/null +++ b/OpenSim/Region/Framework/ModuleLoader.cs @@ -0,0 +1,255 @@ +/* + * 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 OpenSim 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 log4net; +using Nini.Config; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework +{ + public class ModuleLoader + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public Dictionary LoadedAssemblys = new Dictionary(); + + private readonly List m_loadedModules = new List(); + private readonly Dictionary m_loadedSharedModules = new Dictionary(); + private readonly IConfigSource m_config; + + public ModuleLoader(IConfigSource config) + { + m_config = config; + } + + public IRegionModule[] GetLoadedSharedModules + { + get + { + IRegionModule[] regionModules = new IRegionModule[m_loadedSharedModules.Count]; + m_loadedSharedModules.Values.CopyTo(regionModules, 0); + return regionModules; + } + } + + public List PickupModules(Scene scene, string moduleDir) + { + DirectoryInfo dir = new DirectoryInfo(moduleDir); + List modules = new List(); + + foreach (FileInfo fileInfo in dir.GetFiles("*.dll")) + { + modules.AddRange(LoadRegionModules(fileInfo.FullName, scene)); + } + return modules; + } + + public void LoadDefaultSharedModule(IRegionModule module) + { + if (m_loadedSharedModules.ContainsKey(module.Name)) + { + m_log.ErrorFormat("[MODULES]: Module name \"{0}\" already exists in module list. Module not added!", module.Name); + } + else + { + m_loadedSharedModules.Add(module.Name, module); + } + } + + + public void InitialiseSharedModules(Scene scene) + { + foreach (IRegionModule module in m_loadedSharedModules.Values) + { + module.Initialise(scene, m_config); + scene.AddModule(module.Name, module); //should be doing this? + } + } + + public void InitializeModule(IRegionModule module, Scene scene) + { + module.Initialise(scene, m_config); + scene.AddModule(module.Name, module); + m_loadedModules.Add(module); + } + + /// + /// Loads/initialises a Module instance that can be used by multiple Regions + /// + /// + /// + public void LoadSharedModule(string dllName, string moduleName) + { + IRegionModule module = LoadModule(dllName, moduleName); + + if (module != null) + LoadSharedModule(module); + } + + /// + /// Loads/initialises a Module instance that can be used by multiple Regions + /// + /// + public void LoadSharedModule(IRegionModule module) + { + if (!m_loadedSharedModules.ContainsKey(module.Name)) + { + m_loadedSharedModules.Add(module.Name, module); + } + } + + public List LoadRegionModules(string dllName, Scene scene) + { + IRegionModule[] modules = LoadModules(dllName); + List initializedModules = new List(); + + if (modules.Length > 0) + { + m_log.InfoFormat("[MODULES]: Found Module Library [{0}]", dllName); + foreach (IRegionModule module in modules) + { + if (!module.IsSharedModule) + { + m_log.InfoFormat("[MODULES]: [{0}]: Initializing.", module.Name); + InitializeModule(module, scene); + initializedModules.Add(module); + } + else + { + m_log.InfoFormat("[MODULES]: [{0}]: Loading Shared Module.", module.Name); + LoadSharedModule(module); + } + } + } + return initializedModules; + } + + public void LoadRegionModule(string dllName, string moduleName, Scene scene) + { + IRegionModule module = LoadModule(dllName, moduleName); + if (module != null) + { + InitializeModule(module, scene); + } + } + + /// + /// Loads a external Module (if not already loaded) and creates a new instance of it. + /// + /// + /// + public IRegionModule LoadModule(string dllName, string moduleName) + { + IRegionModule[] modules = LoadModules(dllName); + + foreach (IRegionModule module in modules) + { + if ((module != null) && (module.Name == moduleName)) + { + return module; + } + } + + return null; + } + + public IRegionModule[] LoadModules(string dllName) + { + List modules = new List(); + + Assembly pluginAssembly; + if (!LoadedAssemblys.TryGetValue(dllName, out pluginAssembly)) + { + try + { + pluginAssembly = Assembly.LoadFrom(dllName); + LoadedAssemblys.Add(dllName, pluginAssembly); + } + catch (BadImageFormatException) + { + //m_log.InfoFormat("[MODULES]: The file [{0}] is not a module assembly.", e.FileName); + } + } + + if (pluginAssembly != null) + { + try + { + foreach (Type pluginType in pluginAssembly.GetTypes()) + { + if (pluginType.IsPublic) + { + if (!pluginType.IsAbstract) + { + if (pluginType.GetInterface("IRegionModule") != null) + { + modules.Add((IRegionModule)Activator.CreateInstance(pluginType)); + } + } + } + } + } + catch (ReflectionTypeLoadException) + { + m_log.InfoFormat("[MODULES]: Could not load types for [{0}].", pluginAssembly.FullName); + } + } + + return modules.ToArray(); + } + + public void PostInitialise() + { + foreach (IRegionModule module in m_loadedSharedModules.Values) + { + module.PostInitialise(); + } + + foreach (IRegionModule module in m_loadedModules) + { + module.PostInitialise(); + } + } + + public void ClearCache() + { + LoadedAssemblys.Clear(); + } + + public void UnloadModule(IRegionModule rm) + { + rm.Close(); + + m_loadedModules.Remove(rm); + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/Animation.cs b/OpenSim/Region/Framework/Scenes/Animation.cs new file mode 100644 index 0000000..3504717 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Animation.cs @@ -0,0 +1,59 @@ +/* + * 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 OpenSim 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 OpenMetaverse; + +namespace OpenSim.Region.Framework.Scenes +{ + public class Animation + { + private UUID animID; + public UUID AnimID + { + get { return animID; } + set { animID = value; } + } + + private int sequenceNum; + public int SequenceNum + { + get { return sequenceNum; } + set { sequenceNum = value; } + } + + public Animation() + { + } + + public Animation(UUID animID, int sequenceNum) + { + this.animID = animID; + this.sequenceNum = sequenceNum; + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/AnimationSet.cs b/OpenSim/Region/Framework/Scenes/AnimationSet.cs new file mode 100644 index 0000000..f53bb78 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/AnimationSet.cs @@ -0,0 +1,155 @@ +/* + * 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 OpenSim 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; + +namespace OpenSim.Region.Framework.Scenes +{ + public class AnimationSet + { + public static AvatarAnimations Animations = new AvatarAnimations(); + + private Animation m_defaultAnimation = new Animation(); + private List m_animations = new List(); + + public Animation DefaultAnimation + { + get { return m_defaultAnimation; } + } + public AnimationSet() + { + ResetDefaultAnimation(); + } + + public bool HasAnimation(UUID animID) + { + if (m_defaultAnimation.AnimID == animID) + return true; + + for (int i = 0; i < m_animations.Count; ++i) + { + if (m_animations[i].AnimID == animID) + return true; + } + + return false; + } + + public bool Add(UUID animID, int sequenceNum) + { + lock (m_animations) + { + if (!HasAnimation(animID)) + { + m_animations.Add(new Animation(animID, sequenceNum)); + return true; + } + } + return false; + } + + public bool Remove(UUID animID) + { + lock (m_animations) + { + if (m_defaultAnimation.AnimID == animID) + { + ResetDefaultAnimation(); + } + else if (HasAnimation(animID)) + { + for (int i = 0; i < m_animations.Count; i++) + { + if (m_animations[i].AnimID == animID) + { + m_animations.RemoveAt(i); + return true; + } + } + } + } + return false; + } + + public void Clear() + { + ResetDefaultAnimation(); + m_animations.Clear(); + } + + /// + /// The default animation is reserved for "main" animations + /// that are mutually exclusive, e.g. flying and sitting. + /// + public bool SetDefaultAnimation(UUID animID, int sequenceNum) + { + if (m_defaultAnimation.AnimID != animID) + { + m_defaultAnimation = new Animation(animID, sequenceNum); + return true; + } + return false; + } + + protected bool ResetDefaultAnimation() + { + return TrySetDefaultAnimation("STAND", 1); + } + + /// + /// Set the animation as the default animation if it's known + /// + public bool TrySetDefaultAnimation(string anim, int sequenceNum) + { + if (Animations.AnimsUUID.ContainsKey(anim)) + { + return SetDefaultAnimation(Animations.AnimsUUID[anim], sequenceNum); + } + return false; + } + + public void GetArrays(out UUID[] animIDs, out int[] sequenceNums) + { + lock (m_animations) + { + animIDs = new UUID[m_animations.Count + 1]; + sequenceNums = new int[m_animations.Count + 1]; + + animIDs[0] = m_defaultAnimation.AnimID; + sequenceNums[0] = m_defaultAnimation.SequenceNum; + + for (int i = 0; i < m_animations.Count; ++i) + { + animIDs[i + 1] = m_animations[i].AnimID; + sequenceNums[i + 1] = m_animations[i].SequenceNum; + } + } + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs b/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs new file mode 100644 index 0000000..178f1f7 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs @@ -0,0 +1,165 @@ +/* + * 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 OpenSim 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 System.Timers; +using log4net; +using OpenMetaverse; +using OpenMetaverse.Packets; +using OpenSim.Framework; + +namespace OpenSim.Region.Framework.Scenes +{ + class DeleteToInventoryHolder + { + public DeRezAction action; + public IClientAPI remoteClient; + public SceneObjectGroup objectGroup; + public UUID folderID; + public bool permissionToDelete; + } + + /// + /// Asynchronously derez objects. This is used to derez large number of objects to inventory without holding + /// up the main client thread. + /// + public class AsyncSceneObjectGroupDeleter + { + private static readonly ILog m_log + = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Is the deleter currently enabled? + /// + public bool Enabled; + + private Timer m_inventoryTicker = new Timer(2000); + private readonly Queue m_inventoryDeletes = new Queue(); + private Scene m_scene; + + public AsyncSceneObjectGroupDeleter(Scene scene) + { + m_scene = scene; + + m_inventoryTicker.AutoReset = false; + m_inventoryTicker.Elapsed += InventoryRunDeleteTimer; + } + + /// + /// Delete the given object from the scene + /// + public void DeleteToInventory(DeRezAction action, UUID folderID, + SceneObjectGroup objectGroup, IClientAPI remoteClient, + bool permissionToDelete) + { + if (Enabled) + m_inventoryTicker.Stop(); + + lock (m_inventoryDeletes) + { + DeleteToInventoryHolder dtis = new DeleteToInventoryHolder(); + dtis.action = action; + dtis.folderID = folderID; + dtis.objectGroup = objectGroup; + dtis.remoteClient = remoteClient; + dtis.permissionToDelete = permissionToDelete; + + m_inventoryDeletes.Enqueue(dtis); + } + + if (Enabled) + m_inventoryTicker.Start(); + + // Visually remove it, even if it isnt really gone yet. This means that if we crash before the object + // has gone to inventory, it will reappear in the region again on restart instead of being lost. + // This is not ideal since the object will still be available for manipulation when it should be, but it's + // better than losing the object for now. + if (permissionToDelete) + objectGroup.DeleteGroup(false); + } + + private void InventoryRunDeleteTimer(object sender, ElapsedEventArgs e) + { + m_log.Debug("[SCENE]: Starting send to inventory loop"); + + while (InventoryDeQueueAndDelete()) + { + m_log.Debug("[SCENE]: Sent item successfully to inventory, continuing..."); + } + } + + /// + /// Move the next object in the queue to inventory. Then delete it properly from the scene. + /// + /// + public bool InventoryDeQueueAndDelete() + { + DeleteToInventoryHolder x = null; + + try + { + lock (m_inventoryDeletes) + { + int left = m_inventoryDeletes.Count; + if (left > 0) + { + m_log.DebugFormat( + "[SCENE]: Sending object to user's inventory, {0} item(s) remaining.", left); + + x = m_inventoryDeletes.Dequeue(); + + try + { + m_scene.DeleteToInventory(x.action, x.folderID, x.objectGroup, x.remoteClient); + if (x.permissionToDelete) + m_scene.DeleteSceneObject(x.objectGroup, false); + } + catch (Exception e) + { + m_log.DebugFormat("Exception background sending object: " + e); + } + + return true; + } + } + } + catch (Exception e) + { + // We can't put the object group details in here since the root part may have disappeared (which is where these sit). + // FIXME: This needs to be fixed. + m_log.ErrorFormat( + "[SCENE]: Queued sending of scene object to agent {0} {1} failed: {2}", + (x != null ? x.remoteClient.Name : "unavailable"), (x != null ? x.remoteClient.AgentId.ToString() : "unavailable"), e.ToString()); + } + + m_log.Debug("[SCENE]: No objects left in inventory send queue."); + return false; + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/AvatarAnimations.cs b/OpenSim/Region/Framework/Scenes/AvatarAnimations.cs new file mode 100644 index 0000000..562fcf9 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/AvatarAnimations.cs @@ -0,0 +1,63 @@ +/* + * 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 OpenSim 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.Collections.Generic; +using System.Xml; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Scenes +{ + public class AvatarAnimations + { + public Dictionary AnimsUUID = new Dictionary(); + public Dictionary AnimsNames = new Dictionary(); + public Dictionary AnimStateNames = new Dictionary(); + + public AvatarAnimations() + { + using (XmlTextReader reader = new XmlTextReader("data/avataranimations.xml")) + { + XmlDocument doc = new XmlDocument(); + doc.Load(reader); + foreach (XmlNode nod in doc.DocumentElement.ChildNodes) + { + if (nod.Attributes["name"] != null) + { + string name = (string)nod.Attributes["name"].Value; + UUID id = (UUID)nod.InnerText; + string animState = (string)nod.Attributes["state"].Value; + + AnimsUUID.Add(name, id); + AnimsNames.Add(id, name); + if (animState != "") + AnimStateNames.Add(id, animState); + } + } + } + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/EntityBase.cs b/OpenSim/Region/Framework/Scenes/EntityBase.cs new file mode 100644 index 0000000..5055e44 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/EntityBase.cs @@ -0,0 +1,253 @@ +/* + * 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 OpenSim 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.Runtime.Serialization; +using System.Security.Permissions; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Scenes +{ + [Serializable] + public abstract class EntityBase : ISerializable + { + /// + /// The scene to which this entity belongs + /// + public Scene Scene + { + get { return m_scene; } + } + protected Scene m_scene; + + protected UUID m_uuid; + + public virtual UUID UUID + { + get { return m_uuid; } + set { m_uuid = value; } + } + + protected string m_name; + + /// + /// The name of this entity + /// + public virtual string Name + { + get { return m_name; } + set { m_name = value; } + } + + /// + /// Signals whether this entity was in a scene but has since been removed from it. + /// + public bool IsDeleted + { + get { return m_isDeleted; } + } + protected bool m_isDeleted; + + protected Vector3 m_pos; + + /// + /// + /// + public virtual Vector3 AbsolutePosition + { + get { return m_pos; } + set { m_pos = value; } + } + + protected Vector3 m_velocity; + protected Vector3 m_rotationalvelocity; + + /// + /// Current velocity of the entity. + /// + public virtual Vector3 Velocity + { + get { return m_velocity; } + set { m_velocity = value; } + } + + protected Quaternion m_rotation = new Quaternion(0f, 0f, 1f, 0f); + + public virtual Quaternion Rotation + { + get { return m_rotation; } + set { m_rotation = value; } + } + + protected uint m_localId; + + public virtual uint LocalId + { + get { return m_localId; } + set { m_localId = value; } + } + + /// + /// Creates a new Entity (should not occur on it's own) + /// + public EntityBase() + { + m_uuid = UUID.Zero; + + m_pos = Vector3.Zero; + m_velocity = Vector3.Zero; + Rotation = Quaternion.Identity; + m_name = "(basic entity)"; + m_rotationalvelocity = Vector3.Zero; + } + + /// + /// + /// + public abstract void UpdateMovement(); + + /// + /// Performs any updates that need to be done at each frame, as opposed to immediately. + /// These included scheduled updates and updates that occur due to physics processing. + /// + public abstract void Update(); + + /// + /// Copies the entity + /// + /// + public virtual EntityBase Copy() + { + return (EntityBase) MemberwiseClone(); + } + + + public abstract void SetText(string text, Vector3 color, double alpha); + + protected EntityBase(SerializationInfo info, StreamingContext context) + { + //System.Console.WriteLine("EntityBase Deserialize BGN"); + + if (info == null) + { + throw new ArgumentNullException("info"); + } + + m_uuid = new UUID((Guid)info.GetValue("m_uuid", typeof(Guid))); + m_name = (string)info.GetValue("m_name", typeof(string)); + + m_pos + = new Vector3( + (float)info.GetValue("m_pos.X", typeof(float)), + (float)info.GetValue("m_pos.Y", typeof(float)), + (float)info.GetValue("m_pos.Z", typeof(float))); + + m_velocity + = new Vector3( + (float)info.GetValue("m_velocity.X", typeof(float)), + (float)info.GetValue("m_velocity.Y", typeof(float)), + (float)info.GetValue("m_velocity.Z", typeof(float))); + + m_rotationalvelocity + = new Vector3( + (float)info.GetValue("m_rotationalvelocity.X", typeof(float)), + (float)info.GetValue("m_rotationalvelocity.Y", typeof(float)), + (float)info.GetValue("m_rotationalvelocity.Z", typeof(float))); + + m_rotation + = new Quaternion( + (float)info.GetValue("m_rotation.X", typeof(float)), + (float)info.GetValue("m_rotation.Y", typeof(float)), + (float)info.GetValue("m_rotation.Z", typeof(float)), + (float)info.GetValue("m_rotation.W", typeof(float))); + + m_localId = (uint)info.GetValue("m_localId", typeof(uint)); + + //System.Console.WriteLine("EntityBase Deserialize END"); + } + + [SecurityPermission(SecurityAction.LinkDemand, + Flags = SecurityPermissionFlag.SerializationFormatter)] + public virtual void GetObjectData( + SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException("info"); + } + + info.AddValue("m_uuid", m_uuid.Guid); + info.AddValue("m_name", m_name); + + // Vector3 + info.AddValue("m_pos.X", m_pos.X); + info.AddValue("m_pos.Y", m_pos.Y); + info.AddValue("m_pos.Z", m_pos.Z); + + // Vector3 + info.AddValue("m_velocity.X", m_velocity.X); + info.AddValue("m_velocity.Y", m_velocity.Y); + info.AddValue("m_velocity.Z", m_velocity.Z); + + // Vector3 + info.AddValue("m_rotationalvelocity.X", m_rotationalvelocity.X); + info.AddValue("m_rotationalvelocity.Y", m_rotationalvelocity.Y); + info.AddValue("m_rotationalvelocity.Z", m_rotationalvelocity.Z); + + // Quaternion + info.AddValue("m_rotation.X", m_rotation.X); + info.AddValue("m_rotation.Y", m_rotation.Y); + info.AddValue("m_rotation.Z", m_rotation.Z); + info.AddValue("m_rotation.W", m_rotation.W); + + info.AddValue("m_localId", m_localId); + } + } + + //Nested Classes + public class EntityIntersection + { + public Vector3 ipoint = new Vector3(0, 0, 0); + public Vector3 normal = new Vector3(0, 0, 0); + public Vector3 AAfaceNormal = new Vector3(0, 0, 0); + public int face = -1; + public bool HitTF = false; + public SceneObjectPart obj; + public float distance = 0; + + public EntityIntersection() + { + } + + public EntityIntersection(Vector3 _ipoint, Vector3 _normal, bool _HitTF) + { + ipoint = _ipoint; + normal = _normal; + HitTF = _HitTF; + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/EntityManager.cs b/OpenSim/Region/Framework/Scenes/EntityManager.cs new file mode 100644 index 0000000..a322766 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/EntityManager.cs @@ -0,0 +1,279 @@ +/* + * 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.Reflection; +using log4net; +using OpenMetaverse; + + +namespace OpenSim.Region.Framework.Scenes +{ + public class EntityManager : IEnumerable + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private readonly Dictionary m_eb_uuid = new Dictionary(); + private readonly Dictionary m_eb_localID = new Dictionary(); + //private readonly Dictionary m_pres_uuid = new Dictionary(); + private readonly Object m_lock = new Object(); + + [Obsolete("Use Add() instead.")] + public void Add(UUID id, EntityBase eb) + { + Add(eb); + } + + public void Add(EntityBase entity) + { + lock (m_lock) + { + try + { + m_eb_uuid.Add(entity.UUID, entity); + m_eb_localID.Add(entity.LocalId, entity); + } + catch(Exception e) + { + m_log.ErrorFormat("Add Entity failed: {0}", e.Message); + } + } + } + + public void InsertOrReplace(EntityBase entity) + { + lock (m_lock) + { + try + { + m_eb_uuid[entity.UUID] = entity; + m_eb_localID[entity.LocalId] = entity; + } + catch(Exception e) + { + m_log.ErrorFormat("Insert or Replace Entity failed: {0}", e.Message); + } + } + } + + public void Clear() + { + lock (m_lock) + { + m_eb_uuid.Clear(); + m_eb_localID.Clear(); + } + } + + public int Count + { + get + { + lock (m_lock) + { + return m_eb_uuid.Count; + } + } + } + + public bool ContainsKey(UUID id) + { + lock (m_lock) + { + try + { + return m_eb_uuid.ContainsKey(id); + } + catch + { + return false; + } + } + } + + public bool ContainsKey(uint localID) + { + lock (m_lock) + { + try + { + return m_eb_localID.ContainsKey(localID); + } + catch + { + return false; + } + } + } + + public bool Remove(uint localID) + { + lock (m_lock) + { + try + { + bool a = m_eb_uuid.Remove(m_eb_localID[localID].UUID); + bool b = m_eb_localID.Remove(localID); + return a && b; + } + catch (Exception e) + { + m_log.ErrorFormat("Remove Entity failed for {0}", localID, e); + return false; + } + } + } + + public bool Remove(UUID id) + { + lock (m_lock) + { + try + { + bool a = m_eb_localID.Remove(m_eb_uuid[id].LocalId); + bool b = m_eb_uuid.Remove(id); + return a && b; + } + catch (Exception e) + { + m_log.ErrorFormat("Remove Entity failed for {0}", id, e); + return false; + } + } + } + + public List GetAllByType() + { + List tmp = new List(); + + lock (m_lock) + { + try + { + foreach (KeyValuePair pair in m_eb_uuid) + { + if (pair.Value is T) + { + tmp.Add(pair.Value); + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("GetAllByType failed for {0}", e); + tmp = null; + } + } + + return tmp; + } + + public List GetEntities() + { + lock (m_lock) + { + return new List(m_eb_uuid.Values); + } + } + + public EntityBase this[UUID id] + { + get + { + lock (m_lock) + { + try + { + return m_eb_uuid[id]; + } + catch + { + return null; + } + } + } + set + { + InsertOrReplace(value); + } + } + + public EntityBase this[uint localID] + { + get + { + lock (m_lock) + { + try + { + return m_eb_localID[localID]; + } + catch + { + return null; + } + } + } + set + { + InsertOrReplace(value); + } + } + + public bool TryGetValue(UUID key, out EntityBase obj) + { + lock (m_lock) + { + return m_eb_uuid.TryGetValue(key, out obj); + } + } + + public bool TryGetValue(uint key, out EntityBase obj) + { + lock (m_lock) + { + return m_eb_localID.TryGetValue(key, out obj); + } + } + + /// + /// This could be optimised to work on the list 'live' rather than making a safe copy and iterating that. + /// + /// + public IEnumerator GetEnumerator() + { + return GetEntities().GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + } +} diff --git a/OpenSim/Region/Framework/Scenes/EventManager.cs b/OpenSim/Region/Framework/Scenes/EventManager.cs new file mode 100644 index 0000000..23e1278 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/EventManager.cs @@ -0,0 +1,981 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Client; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using Caps = OpenSim.Framework.Communications.Capabilities.Caps; +using System.Collections.Generic; + +namespace OpenSim.Region.Framework.Scenes +{ + /// + /// A class for triggering remote scene events. + /// + public class EventManager + { + public delegate void OnFrameDelegate(); + + public event OnFrameDelegate OnFrame; + + public delegate void ClientMovement(ScenePresence client); + + public event ClientMovement OnClientMovement; + + public delegate void OnTerrainTickDelegate(); + + public event OnTerrainTickDelegate OnTerrainTick; + + public delegate void OnBackupDelegate(IRegionDataStore datastore, bool forceBackup); + + public event OnBackupDelegate OnBackup; + + public delegate void OnClientConnectCoreDelegate(IClientCore client); + + public event OnClientConnectCoreDelegate OnClientConnect; + + public delegate void OnNewClientDelegate(IClientAPI client); + + /// + /// Depreciated in favour of OnClientConnect. + /// Will be marked Obsolete after IClientCore has 100% of IClientAPI interfaces. + /// + public event OnNewClientDelegate OnNewClient; + + public delegate void OnNewPresenceDelegate(ScenePresence presence); + + public event OnNewPresenceDelegate OnNewPresence; + + public delegate void OnRemovePresenceDelegate(UUID agentId); + + public event OnRemovePresenceDelegate OnRemovePresence; + + public delegate void OnParcelPrimCountUpdateDelegate(); + + public event OnParcelPrimCountUpdateDelegate OnParcelPrimCountUpdate; + + public delegate void OnParcelPrimCountAddDelegate(SceneObjectGroup obj); + + public event OnParcelPrimCountAddDelegate OnParcelPrimCountAdd; + + public delegate void OnPluginConsoleDelegate(string[] args); + + public event OnPluginConsoleDelegate OnPluginConsole; + + public delegate void OnShutdownDelegate(); + + public event OnShutdownDelegate OnShutdown; + + public delegate void ObjectGrabDelegate(uint localID, uint originalID, Vector3 offsetPos, IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs); + public delegate void ObjectDeGrabDelegate(uint localID, uint originalID, IClientAPI remoteClient); + public delegate void ScriptResetDelegate(uint localID, UUID itemID); + + public delegate void OnPermissionErrorDelegate(UUID user, string reason); + + public delegate void OnSetRootAgentSceneDelegate(UUID agentID, Scene scene); + + public event OnSetRootAgentSceneDelegate OnSetRootAgentScene; + + public event ObjectGrabDelegate OnObjectGrab; + public event ObjectDeGrabDelegate OnObjectDeGrab; + public event ScriptResetDelegate OnScriptReset; + + public event OnPermissionErrorDelegate OnPermissionError; + + public delegate void NewRezScript(uint localID, UUID itemID, string script, int startParam, bool postOnRez, string engine, int stateSource); + + public event NewRezScript OnRezScript; + + public delegate void RemoveScript(uint localID, UUID itemID); + + public event RemoveScript OnRemoveScript; + + public delegate void StartScript(uint localID, UUID itemID); + + public event StartScript OnStartScript; + + public delegate void StopScript(uint localID, UUID itemID); + + public event StopScript OnStopScript; + + public delegate bool SceneGroupMoved(UUID groupID, Vector3 delta); + + public event SceneGroupMoved OnSceneGroupMove; + + public delegate void SceneGroupGrabed(UUID groupID, Vector3 offset, UUID userID); + + public event SceneGroupGrabed OnSceneGroupGrab; + + public delegate void LandObjectAdded(ILandObject newParcel); + + public event LandObjectAdded OnLandObjectAdded; + + public delegate void LandObjectRemoved(UUID globalID); + + public event LandObjectRemoved OnLandObjectRemoved; + + public delegate void AvatarEnteringNewParcel(ScenePresence avatar, int localLandID, UUID regionID); + + public event AvatarEnteringNewParcel OnAvatarEnteringNewParcel; + + public delegate void SignificantClientMovement(IClientAPI remote_client); + + public event SignificantClientMovement OnSignificantClientMovement; + + public delegate void IncomingInstantMessage(GridInstantMessage message); + + public event IncomingInstantMessage OnIncomingInstantMessage; + + public event IncomingInstantMessage OnUnhandledInstantMessage; + + public delegate void ClientClosed(UUID clientID); + + public event ClientClosed OnClientClosed; + + public delegate void ScriptChangedEvent(uint localID, uint change); + + public event ScriptChangedEvent OnScriptChangedEvent; + + public delegate void ScriptControlEvent(uint localID, UUID item, UUID avatarID, uint held, uint changed); + + public event ScriptControlEvent OnScriptControlEvent; + + public delegate void ScriptAtTargetEvent(uint localID, uint handle, Vector3 targetpos, Vector3 atpos); + + public event ScriptAtTargetEvent OnScriptAtTargetEvent; + + public delegate void ScriptNotAtTargetEvent(uint localID); + + public event ScriptNotAtTargetEvent OnScriptNotAtTargetEvent; + + public delegate void ScriptColliding(uint localID, ColliderArgs colliders); + + public event ScriptColliding OnScriptColliderStart; + public event ScriptColliding OnScriptColliding; + public event ScriptColliding OnScriptCollidingEnd; + + public delegate void OnMakeChildAgentDelegate(ScenePresence presence); + public event OnMakeChildAgentDelegate OnMakeChildAgent; + + public delegate void OnMakeRootAgentDelegate(ScenePresence presence); + public event OnMakeRootAgentDelegate OnMakeRootAgent; + + public delegate void NewInventoryItemUploadComplete(UUID avatarID, UUID assetID, string name, int userlevel); + + public event NewInventoryItemUploadComplete OnNewInventoryItemUploadComplete; + + public delegate void RequestChangeWaterHeight(float height); + + public event RequestChangeWaterHeight OnRequestChangeWaterHeight; + + public delegate void AvatarKillData(uint KillerLocalID, ScenePresence avatar); + + public event AvatarKillData OnAvatarKilled; + + public delegate void ScriptTimerEvent(uint localID, double timerinterval); + + public event ScriptTimerEvent OnScriptTimerEvent; + + public delegate void EstateToolsTimeUpdate(ulong regionHandle, bool FixedTime, bool EstateSun, float LindenHour); + public delegate void GetScriptRunning(IClientAPI controllingClient, UUID objectID, UUID itemID); + + public event EstateToolsTimeUpdate OnEstateToolsTimeUpdate; + + public delegate void ObjectBeingRemovedFromScene(SceneObjectGroup obj); + public event ObjectBeingRemovedFromScene OnObjectBeingRemovedFromScene; + + public delegate void NoticeNoLandDataFromStorage(); + public event NoticeNoLandDataFromStorage OnNoticeNoLandDataFromStorage; + + public delegate void IncomingLandDataFromStorage(List data); + public event IncomingLandDataFromStorage OnIncomingLandDataFromStorage; + + public delegate void SetAllowForcefulBan(bool allow); + public event SetAllowForcefulBan OnSetAllowForcefulBan; + + public delegate void RequestParcelPrimCountUpdate(); + public event RequestParcelPrimCountUpdate OnRequestParcelPrimCountUpdate; + + public delegate void ParcelPrimCountTainted(); + public event ParcelPrimCountTainted OnParcelPrimCountTainted; + public event GetScriptRunning OnGetScriptRunning; + + /// + /// RegisterCapsEvent is called by Scene after the Caps object + /// has been instantiated and before it is return to the + /// client and provides region modules to add their caps. + /// + public delegate void RegisterCapsEvent(UUID agentID, Caps caps); + public event RegisterCapsEvent OnRegisterCaps; + + /// + /// DeregisterCapsEvent is called by Scene when the caps + /// handler for an agent are removed. + /// + public delegate void DeregisterCapsEvent(UUID agentID, Caps caps); + public event DeregisterCapsEvent OnDeregisterCaps; + + /// + /// ChatFromWorldEvent is called via Scene when a chat message + /// from world comes in. + /// + public delegate void ChatFromWorldEvent(Object sender, OSChatMessage chat); + public event ChatFromWorldEvent OnChatFromWorld; + + /// + /// ChatFromClientEvent is triggered via ChatModule (or + /// substitutes thereof) when a chat message + /// from the client comes in. + /// + public delegate void ChatFromClientEvent(Object sender, OSChatMessage chat); + public event ChatFromClientEvent OnChatFromClient; + + /// + /// ChatBroadcastEvent is called via Scene when a broadcast chat message + /// from world comes in + /// + public delegate void ChatBroadcastEvent(Object sender, OSChatMessage chat); + public event ChatBroadcastEvent OnChatBroadcast; + + public delegate float SunLindenHour(); + public event SunLindenHour OnGetSunLindenHour; + + /// + /// Called when oar file has finished loading, although + /// the scripts may not have started yet + /// Message is non empty string if there were problems loading the oar file + /// + public delegate void OarFileLoaded(string message); + public event OarFileLoaded OnOarFileLoaded; + + /// + /// Called when an oar file has finished saving + /// Message is non empty string if there were problems saving the oar file + /// + public delegate void OarFileSaved(string message); + public event OarFileSaved OnOarFileSaved; + + /// + /// Called when the script compile queue becomes empty + /// Returns the number of scripts which failed to start + /// + public delegate void EmptyScriptCompileQueue(int numScriptsFailed, string message); + public event EmptyScriptCompileQueue OnEmptyScriptCompileQueue; + + public class MoneyTransferArgs : EventArgs + { + public UUID sender; + public UUID receiver; + + // Always false. The SL protocol sucks. + public bool authenticated = false; + + public int amount; + public int transactiontype; + public string description; + + public MoneyTransferArgs(UUID asender, UUID areceiver, int aamount, int atransactiontype, string adescription) + { + sender = asender; + receiver = areceiver; + amount = aamount; + transactiontype = atransactiontype; + description = adescription; + } + } + + public class LandBuyArgs : EventArgs + { + public UUID agentId = UUID.Zero; + + public UUID groupId = UUID.Zero; + + public UUID parcelOwnerID = UUID.Zero; + + public bool final = false; + public bool groupOwned = false; + public bool removeContribution = false; + public int parcelLocalID = 0; + public int parcelArea = 0; + public int parcelPrice = 0; + public bool authenticated = false; + public bool landValidated = false; + public bool economyValidated = false; + public int transactionID = 0; + public int amountDebited = 0; + + public LandBuyArgs(UUID pagentId, UUID pgroupId, bool pfinal, bool pgroupOwned, + bool premoveContribution, int pparcelLocalID, int pparcelArea, int pparcelPrice, + bool pauthenticated) + { + agentId = pagentId; + groupId = pgroupId; + final = pfinal; + groupOwned = pgroupOwned; + removeContribution = premoveContribution; + parcelLocalID = pparcelLocalID; + parcelArea = pparcelArea; + parcelPrice = pparcelPrice; + authenticated = pauthenticated; + } + } + + public delegate void MoneyTransferEvent(Object sender, MoneyTransferArgs e); + + public delegate void LandBuy(Object sender, LandBuyArgs e); + + public event MoneyTransferEvent OnMoneyTransfer; + public event LandBuy OnLandBuy; + public event LandBuy OnValidateLandBuy; + + /* Designated Event Deletage Instances */ + + private ScriptChangedEvent handlerScriptChangedEvent = null; //OnScriptChangedEvent; + private ScriptAtTargetEvent handlerScriptAtTargetEvent = null; + private ScriptNotAtTargetEvent handlerScriptNotAtTargetEvent = null; + private ClientMovement handlerClientMovement = null; //OnClientMovement; + private OnPermissionErrorDelegate handlerPermissionError = null; //OnPermissionError; + private OnPluginConsoleDelegate handlerPluginConsole = null; //OnPluginConsole; + private OnFrameDelegate handlerFrame = null; //OnFrame; + private OnNewClientDelegate handlerNewClient = null; //OnNewClient; + private OnClientConnectCoreDelegate handlerClientConnect = null; //OnClientConnect + private OnNewPresenceDelegate handlerNewPresence = null; //OnNewPresence; + private OnRemovePresenceDelegate handlerRemovePresence = null; //OnRemovePresence; + private OnBackupDelegate handlerBackup = null; //OnBackup; + private OnParcelPrimCountUpdateDelegate handlerParcelPrimCountUpdate = null; //OnParcelPrimCountUpdate; + private MoneyTransferEvent handlerMoneyTransfer = null; //OnMoneyTransfer; + private OnParcelPrimCountAddDelegate handlerParcelPrimCountAdd = null; //OnParcelPrimCountAdd; + private OnShutdownDelegate handlerShutdown = null; //OnShutdown; + private ObjectGrabDelegate handlerObjectGrab = null; //OnObjectGrab; + private ObjectDeGrabDelegate handlerObjectDeGrab = null; //OnObjectDeGrab; + private ScriptResetDelegate handlerScriptReset = null; // OnScriptReset + private NewRezScript handlerRezScript = null; //OnRezScript; + private RemoveScript handlerRemoveScript = null; //OnRemoveScript; + private StartScript handlerStartScript = null; //OnStartScript; + private StopScript handlerStopScript = null; //OnStopScript; + private SceneGroupMoved handlerSceneGroupMove = null; //OnSceneGroupMove; + private SceneGroupGrabed handlerSceneGroupGrab = null; //OnSceneGroupGrab; + private LandObjectAdded handlerLandObjectAdded = null; //OnLandObjectAdded; + private LandObjectRemoved handlerLandObjectRemoved = null; //OnLandObjectRemoved; + private AvatarEnteringNewParcel handlerAvatarEnteringNewParcel = null; //OnAvatarEnteringNewParcel; + private IncomingInstantMessage handlerIncomingInstantMessage = null; //OnIncomingInstantMessage; + private IncomingInstantMessage handlerUnhandledInstantMessage = null; //OnUnhandledInstantMessage; + private ClientClosed handlerClientClosed = null; //OnClientClosed; + private OnMakeChildAgentDelegate handlerMakeChildAgent = null; //OnMakeChildAgent; + private OnMakeRootAgentDelegate handlerMakeRootAgent = null; //OnMakeRootAgent; + private OnTerrainTickDelegate handlerTerrainTick = null; // OnTerainTick; + private RegisterCapsEvent handlerRegisterCaps = null; // OnRegisterCaps; + private DeregisterCapsEvent handlerDeregisterCaps = null; // OnDeregisterCaps; + private ChatFromWorldEvent handlerChatFromWorld = null; // OnChatFromWorld; + private ChatFromClientEvent handlerChatFromClient = null; // OnChatFromClient; + private ChatBroadcastEvent handlerChatBroadcast = null; // OnChatBroadcast; + private NewInventoryItemUploadComplete handlerNewInventoryItemUpdateComplete = null; + private RequestChangeWaterHeight handlerRequestChangeWaterHeight = null; //OnRequestChangeWaterHeight + private ScriptControlEvent handlerScriptControlEvent = null; + private SignificantClientMovement handlerSignificantClientMovement = null; + + private LandBuy handlerLandBuy = null; + private LandBuy handlerValidateLandBuy = null; + private AvatarKillData handlerAvatarKill = null; + + private NoticeNoLandDataFromStorage handlerNoticeNoLandDataFromStorage = null; + private IncomingLandDataFromStorage handlerIncomingLandDataFromStorage = null; + private SetAllowForcefulBan handlerSetAllowForcefulBan = null; + private RequestParcelPrimCountUpdate handlerRequestParcelPrimCountUpdate = null; + private ParcelPrimCountTainted handlerParcelPrimCountTainted = null; + private ObjectBeingRemovedFromScene handlerObjectBeingRemovedFromScene = null; + private ScriptTimerEvent handlerScriptTimerEvent = null; + private EstateToolsTimeUpdate handlerEstateToolsTimeUpdate = null; + + private ScriptColliding handlerCollidingStart = null; + private ScriptColliding handlerColliding = null; + private ScriptColliding handlerCollidingEnd = null; + private GetScriptRunning handlerGetScriptRunning = null; + + private SunLindenHour handlerSunGetLindenHour = null; + private OnSetRootAgentSceneDelegate handlerSetRootAgentScene = null; + + private OarFileLoaded handlerOarFileLoaded = null; + private OarFileSaved handlerOarFileSaved = null; + + private EmptyScriptCompileQueue handlerEmptyScriptCompileQueue = null; + + public void TriggerGetScriptRunning(IClientAPI controllingClient, UUID objectID, UUID itemID) + { + handlerGetScriptRunning = OnGetScriptRunning; + if (handlerGetScriptRunning != null) + handlerGetScriptRunning(controllingClient, objectID, itemID); + } + + public void TriggerOnScriptChangedEvent(uint localID, uint change) + { + handlerScriptChangedEvent = OnScriptChangedEvent; + if (handlerScriptChangedEvent != null) + handlerScriptChangedEvent(localID, change); + } + + public void TriggerOnClientMovement(ScenePresence avatar) + { + handlerClientMovement = OnClientMovement; + if (handlerClientMovement != null) + handlerClientMovement(avatar); + } + + public void TriggerPermissionError(UUID user, string reason) + { + handlerPermissionError = OnPermissionError; + if (handlerPermissionError != null) + handlerPermissionError(user, reason); + } + + public void TriggerOnPluginConsole(string[] args) + { + handlerPluginConsole = OnPluginConsole; + if (handlerPluginConsole != null) + handlerPluginConsole(args); + } + + public void TriggerOnFrame() + { + handlerFrame = OnFrame; + if (handlerFrame != null) + { + handlerFrame(); + } + } + + public void TriggerOnNewClient(IClientAPI client) + { + handlerNewClient = OnNewClient; + if (handlerNewClient != null) + handlerNewClient(client); + + if (client is IClientCore) + { + handlerClientConnect = OnClientConnect; + handlerClientConnect((IClientCore) client); + } + } + + public void TriggerOnNewPresence(ScenePresence presence) + { + handlerNewPresence = OnNewPresence; + if (handlerNewPresence != null) + handlerNewPresence(presence); + } + + public void TriggerOnRemovePresence(UUID agentId) + { + handlerRemovePresence = OnRemovePresence; + if (handlerRemovePresence != null) + { + handlerRemovePresence(agentId); + } + } + + public void TriggerOnBackup(IRegionDataStore dstore) + { + handlerBackup = OnBackup; + if (handlerBackup != null) + { + handlerBackup(dstore, false); + } + } + + public void TriggerParcelPrimCountUpdate() + { + handlerParcelPrimCountUpdate = OnParcelPrimCountUpdate; + if (handlerParcelPrimCountUpdate != null) + { + handlerParcelPrimCountUpdate(); + } + } + + public void TriggerMoneyTransfer(Object sender, MoneyTransferArgs e) + { + handlerMoneyTransfer = OnMoneyTransfer; + if (handlerMoneyTransfer != null) + { + handlerMoneyTransfer(sender, e); + } + } + + public void TriggerTerrainTick() + { + handlerTerrainTick = OnTerrainTick; + if (handlerTerrainTick != null) + { + handlerTerrainTick(); + } + } + + public void TriggerParcelPrimCountAdd(SceneObjectGroup obj) + { + handlerParcelPrimCountAdd = OnParcelPrimCountAdd; + if (handlerParcelPrimCountAdd != null) + { + handlerParcelPrimCountAdd(obj); + } + } + + public void TriggerObjectBeingRemovedFromScene(SceneObjectGroup obj) + { + handlerObjectBeingRemovedFromScene = OnObjectBeingRemovedFromScene; + if (handlerObjectBeingRemovedFromScene != null) + { + handlerObjectBeingRemovedFromScene(obj); + } + } + + public void TriggerShutdown() + { + handlerShutdown = OnShutdown; + if (handlerShutdown != null) + handlerShutdown(); + } + + public void TriggerObjectGrab(uint localID, uint originalID, Vector3 offsetPos, IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs) + { + handlerObjectGrab = OnObjectGrab; + if (handlerObjectGrab != null) + { + handlerObjectGrab(localID, originalID, offsetPos, remoteClient, surfaceArgs); + } + } + + public void TriggerObjectDeGrab(uint localID, uint originalID, IClientAPI remoteClient) + { + handlerObjectDeGrab = OnObjectDeGrab; + if (handlerObjectDeGrab != null) + { + handlerObjectDeGrab(localID, originalID, remoteClient); + } + } + + public void TriggerScriptReset(uint localID, UUID itemID) + { + handlerScriptReset = OnScriptReset; + if (handlerScriptReset != null) + { + handlerScriptReset(localID, itemID); + } + } + + public void TriggerRezScript(uint localID, UUID itemID, string script, int startParam, bool postOnRez, string engine, int stateSource) + { + handlerRezScript = OnRezScript; + if (handlerRezScript != null) + { + handlerRezScript(localID, itemID, script, startParam, + postOnRez, engine, stateSource); + } + } + + public void TriggerStartScript(uint localID, UUID itemID) + { + handlerStartScript = OnStartScript; + if (handlerStartScript != null) + { + handlerStartScript(localID, itemID); + } + } + + public void TriggerStopScript(uint localID, UUID itemID) + { + handlerStopScript = OnStopScript; + if (handlerStopScript != null) + { + handlerStopScript(localID, itemID); + } + } + + public void TriggerRemoveScript(uint localID, UUID itemID) + { + handlerRemoveScript = OnRemoveScript; + if (handlerRemoveScript != null) + { + handlerRemoveScript(localID, itemID); + } + } + + public bool TriggerGroupMove(UUID groupID, Vector3 delta) + { + handlerSceneGroupMove = OnSceneGroupMove; + + if (handlerSceneGroupMove != null) + { + return handlerSceneGroupMove(groupID, delta); + } + return true; + } + + public void TriggerGroupGrab(UUID groupID, Vector3 offset, UUID userID) + { + handlerSceneGroupGrab = OnSceneGroupGrab; + if (handlerSceneGroupGrab != null) + { + handlerSceneGroupGrab(groupID, offset, userID); + } + } + + public void TriggerLandObjectAdded(ILandObject newParcel) + { + handlerLandObjectAdded = OnLandObjectAdded; + + if (handlerLandObjectAdded != null) + { + handlerLandObjectAdded(newParcel); + } + } + + public void TriggerLandObjectRemoved(UUID globalID) + { + handlerLandObjectRemoved = OnLandObjectRemoved; + if (handlerLandObjectRemoved != null) + { + handlerLandObjectRemoved(globalID); + } + } + + public void TriggerLandObjectUpdated(uint localParcelID, ILandObject newParcel) + { + //triggerLandObjectRemoved(localParcelID); + + TriggerLandObjectAdded(newParcel); + } + + public void TriggerAvatarEnteringNewParcel(ScenePresence avatar, int localLandID, UUID regionID) + { + handlerAvatarEnteringNewParcel = OnAvatarEnteringNewParcel; + + if (handlerAvatarEnteringNewParcel != null) + { + handlerAvatarEnteringNewParcel(avatar, localLandID, regionID); + } + } + + public void TriggerIncomingInstantMessage(GridInstantMessage message) + { + handlerIncomingInstantMessage = OnIncomingInstantMessage; + if (handlerIncomingInstantMessage != null) + { + handlerIncomingInstantMessage(message); + } + } + + public void TriggerUnhandledInstantMessage(GridInstantMessage message) + { + handlerUnhandledInstantMessage = OnUnhandledInstantMessage; + if (handlerUnhandledInstantMessage != null) + { + handlerUnhandledInstantMessage(message); + } + } + + public void TriggerClientClosed(UUID ClientID) + { + handlerClientClosed = OnClientClosed; + if (handlerClientClosed != null) + { + handlerClientClosed(ClientID); + } + } + + public void TriggerOnMakeChildAgent(ScenePresence presence) + { + handlerMakeChildAgent = OnMakeChildAgent; + if (handlerMakeChildAgent != null) + { + handlerMakeChildAgent(presence); + } + } + + public void TriggerOnMakeRootAgent(ScenePresence presence) + { + handlerMakeRootAgent = OnMakeRootAgent; + if (handlerMakeRootAgent != null) + { + handlerMakeRootAgent(presence); + } + } + + public void TriggerOnRegisterCaps(UUID agentID, Caps caps) + { + handlerRegisterCaps = OnRegisterCaps; + if (handlerRegisterCaps != null) + { + handlerRegisterCaps(agentID, caps); + } + } + + public void TriggerOnDeregisterCaps(UUID agentID, Caps caps) + { + handlerDeregisterCaps = OnDeregisterCaps; + if (handlerDeregisterCaps != null) + { + handlerDeregisterCaps(agentID, caps); + } + } + + public void TriggerOnNewInventoryItemUploadComplete(UUID agentID, UUID AssetID, String AssetName, int userlevel) + { + handlerNewInventoryItemUpdateComplete = OnNewInventoryItemUploadComplete; + if (handlerNewInventoryItemUpdateComplete != null) + { + handlerNewInventoryItemUpdateComplete(agentID, AssetID, AssetName, userlevel); + } + } + + public void TriggerLandBuy(Object sender, LandBuyArgs e) + { + handlerLandBuy = OnLandBuy; + if (handlerLandBuy != null) + { + handlerLandBuy(sender, e); + } + } + + public void TriggerValidateLandBuy(Object sender, LandBuyArgs e) + { + handlerValidateLandBuy = OnValidateLandBuy; + if (handlerValidateLandBuy != null) + { + handlerValidateLandBuy(sender, e); + } + } + + public void TriggerAtTargetEvent(uint localID, uint handle, Vector3 targetpos, Vector3 currentpos) + { + handlerScriptAtTargetEvent = OnScriptAtTargetEvent; + if (handlerScriptAtTargetEvent != null) + { + handlerScriptAtTargetEvent(localID, handle, targetpos, currentpos); + } + } + + public void TriggerNotAtTargetEvent(uint localID) + { + handlerScriptNotAtTargetEvent = OnScriptNotAtTargetEvent; + if (handlerScriptNotAtTargetEvent != null) + { + handlerScriptNotAtTargetEvent(localID); + } + } + + public void TriggerRequestChangeWaterHeight(float height) + { + handlerRequestChangeWaterHeight = OnRequestChangeWaterHeight; + if (handlerRequestChangeWaterHeight != null) + { + handlerRequestChangeWaterHeight(height); + } + } + + public void TriggerAvatarKill(uint KillerObjectLocalID, ScenePresence DeadAvatar) + { + handlerAvatarKill = OnAvatarKilled; + if (handlerAvatarKill != null) + { + handlerAvatarKill(KillerObjectLocalID, DeadAvatar); + } + } + + public void TriggerSignificantClientMovement(IClientAPI client) + { + handlerSignificantClientMovement = OnSignificantClientMovement; + if (handlerSignificantClientMovement != null) + { + handlerSignificantClientMovement(client); + } + } + + public void TriggerOnChatFromWorld(Object sender, OSChatMessage chat) + { + handlerChatFromWorld = OnChatFromWorld; + if (handlerChatFromWorld != null) + { + handlerChatFromWorld(sender, chat); + } + } + + public void TriggerOnChatFromClient(Object sender, OSChatMessage chat) + { + handlerChatFromClient = OnChatFromClient; + if (handlerChatFromClient != null) + { + handlerChatFromClient(sender, chat); + } + } + + public void TriggerOnChatBroadcast(Object sender, OSChatMessage chat) + { + handlerChatBroadcast = OnChatBroadcast; + if (handlerChatBroadcast != null) + { + handlerChatBroadcast(sender, chat); + } + } + + internal void TriggerControlEvent(uint p, UUID scriptUUID, UUID avatarID, uint held, uint _changed) + { + handlerScriptControlEvent = OnScriptControlEvent; + if (handlerScriptControlEvent != null) + { + handlerScriptControlEvent(p, scriptUUID, avatarID, held, _changed); + } + } + + public void TriggerNoticeNoLandDataFromStorage() + { + handlerNoticeNoLandDataFromStorage = OnNoticeNoLandDataFromStorage; + if (handlerNoticeNoLandDataFromStorage != null) + { + handlerNoticeNoLandDataFromStorage(); + + } + } + + public void TriggerIncomingLandDataFromStorage(List landData) + { + handlerIncomingLandDataFromStorage = OnIncomingLandDataFromStorage; + if (handlerIncomingLandDataFromStorage != null) + { + handlerIncomingLandDataFromStorage(landData); + + } + } + + public void TriggerSetAllowForcefulBan(bool allow) + { + handlerSetAllowForcefulBan = OnSetAllowForcefulBan; + if (handlerSetAllowForcefulBan != null) + { + handlerSetAllowForcefulBan(allow); + + } + } + + public void TriggerRequestParcelPrimCountUpdate() + { + handlerRequestParcelPrimCountUpdate = OnRequestParcelPrimCountUpdate; + if (handlerRequestParcelPrimCountUpdate != null) + { + handlerRequestParcelPrimCountUpdate(); + } + } + + public void TriggerParcelPrimCountTainted() + { + handlerParcelPrimCountTainted = OnParcelPrimCountTainted; + if (handlerParcelPrimCountTainted != null) + { + handlerParcelPrimCountTainted(); + } + } + + // this lets us keep track of nasty script events like timer, etc. + public void TriggerTimerEvent(uint objLocalID, double Interval) + { + handlerScriptTimerEvent = OnScriptTimerEvent; + if (handlerScriptTimerEvent != null) + { + handlerScriptTimerEvent(objLocalID, Interval); + } + } + + public void TriggerEstateToolsTimeUpdate(ulong regionHandle, bool FixedTime, bool useEstateTime, float LindenHour) + { + handlerEstateToolsTimeUpdate = OnEstateToolsTimeUpdate; + if (handlerEstateToolsTimeUpdate != null) + { + handlerEstateToolsTimeUpdate(regionHandle, FixedTime, useEstateTime, LindenHour); + } + } + + public float GetSunLindenHour() + { + handlerSunGetLindenHour = OnGetSunLindenHour; + if (handlerSunGetLindenHour != null) + { + return handlerSunGetLindenHour(); + } + return 6; + } + + public void TriggerOarFileLoaded(string message) + { + handlerOarFileLoaded = OnOarFileLoaded; + if (handlerOarFileLoaded != null) + handlerOarFileLoaded(message); + } + + public void TriggerOarFileSaved(string message) + { + handlerOarFileSaved = OnOarFileSaved; + if (handlerOarFileSaved != null) + handlerOarFileSaved(message); + } + + public void TriggerEmptyScriptCompileQueue(int numScriptsFailed, string message) + { + handlerEmptyScriptCompileQueue = OnEmptyScriptCompileQueue; + if (handlerEmptyScriptCompileQueue != null) + handlerEmptyScriptCompileQueue(numScriptsFailed, message); + } + + public void TriggerScriptCollidingStart(uint localId, ColliderArgs colliders) + { + handlerCollidingStart = OnScriptColliderStart; + if (handlerCollidingStart != null) + handlerCollidingStart(localId, colliders); + } + + public void TriggerScriptColliding(uint localId, ColliderArgs colliders) + { + handlerColliding = OnScriptColliding; + if (handlerColliding != null) + handlerColliding(localId, colliders); + } + + public void TriggerScriptCollidingEnd(uint localId, ColliderArgs colliders) + { + handlerCollidingEnd = OnScriptCollidingEnd; + if (handlerCollidingEnd != null) + handlerCollidingEnd(localId, colliders); + } + + public void TriggerSetRootAgentScene(UUID agentID, Scene scene) + { + handlerSetRootAgentScene = OnSetRootAgentScene; + if (handlerSetRootAgentScene != null) + handlerSetRootAgentScene(agentID, scene); + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/Hypergrid/HGAssetMapper.cs b/OpenSim/Region/Framework/Scenes/Hypergrid/HGAssetMapper.cs new file mode 100644 index 0000000..bd9c260 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Hypergrid/HGAssetMapper.cs @@ -0,0 +1,376 @@ +/** + * Copyright (c) 2008, Contributors. All rights reserved. + * 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 Organizations nor the names of Individual + * Contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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.Reflection; +using System.Threading; + +using log4net; +using Nini.Config; +using OpenMetaverse; + +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Framework.Servers; +// using OpenSim.Region.Environment; +using OpenSim.Region.Framework.Scenes; + +//using HyperGrid.Framework; +//using OpenSim.Region.Communications.Hypergrid; + +namespace OpenSim.Region.Framework.Scenes.Hypergrid +{ + public class HGAssetMapper + { + #region Fields + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // This maps between asset server URLs and asset server clients + private Dictionary m_assetServers = new Dictionary(); + + // This maps between asset UUIDs and asset servers + private Dictionary m_assetMap = new Dictionary(); + + private Scene m_scene; + #endregion + + #region Constructor + + public HGAssetMapper(Scene scene) + { + m_scene = scene; + } + + #endregion + + #region Internal functions + + private string UserAssetURL(UUID userID) + { + CachedUserInfo uinfo = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(userID); + if (uinfo != null) + return (uinfo.UserProfile.UserAssetURI == "") ? null : uinfo.UserProfile.UserAssetURI; + return null; + } + + private bool IsLocalUser(UUID userID) + { + CachedUserInfo uinfo = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(userID); + + if (uinfo != null) + { + if (HGNetworkServersInfo.Singleton.IsLocalUser(uinfo.UserProfile)) + { + m_log.Debug("[HGScene]: Home user " + uinfo.UserProfile.FirstName + " " + uinfo.UserProfile.SurName); + return true; + } + } + + m_log.Debug("[HGScene]: Foreign user " + uinfo.UserProfile.FirstName + " " + uinfo.UserProfile.SurName); + return false; + } + + private bool IsInAssetMap(UUID uuid) + { + return m_assetMap.ContainsKey(uuid); + } + + private bool FetchAsset(GridAssetClient asscli, UUID assetID, bool isTexture) + { + // I'm not going over 3 seconds since this will be blocking processing of all the other inbound + // packets from the client. + int pollPeriod = 200; + int maxPolls = 15; + + AssetBase asset; + + // Maybe it came late, and it's already here. Check first. + if (m_scene.CommsManager.AssetCache.TryGetCachedAsset(assetID, out asset)) + { + m_log.Debug("[HGScene]: Asset already in asset cache. " + assetID); + return true; + } + + + asscli.RequestAsset(assetID, isTexture); + + do + { + Thread.Sleep(pollPeriod); + + if (m_scene.CommsManager.AssetCache.TryGetCachedAsset(assetID, out asset) && (asset != null)) + { + m_log.Debug("[HGScene]: Asset made it to asset cache. " + asset.Metadata.Name + " " + assetID); + // I think I need to store it in the asset DB too. + // For now, let me just do it for textures and scripts + if (((AssetType)asset.Metadata.Type == AssetType.Texture) || + ((AssetType)asset.Metadata.Type == AssetType.LSLBytecode) || + ((AssetType)asset.Metadata.Type == AssetType.LSLText)) + { + AssetBase asset1 = new AssetBase(); + Copy(asset, asset1); + m_scene.AssetCache.AssetServer.StoreAsset(asset1); + } + return true; + } + } while (--maxPolls > 0); + + m_log.WarnFormat("[HGScene]: {0} {1} was not received before the retrieval timeout was reached", + isTexture ? "texture" : "asset", assetID.ToString()); + + return false; + } + + private bool PostAsset(GridAssetClient asscli, UUID assetID) + { + AssetBase asset1; + m_scene.CommsManager.AssetCache.TryGetCachedAsset(assetID, out asset1); + + if (asset1 != null) + { + // See long comment in AssetCache.AddAsset + if (!asset1.Metadata.Temporary || asset1.Metadata.Local) + { + // The asset cache returns instances of subclasses of AssetBase: + // TextureImage or AssetInfo. So in passing them to the remote + // server we first need to convert this to instances of AssetBase, + // which is the serializable class for assets. + AssetBase asset = new AssetBase(); + Copy(asset1, asset); + + asscli.StoreAsset(asset); + } + return true; + } + else + m_log.Warn("[HGScene]: Tried to post asset to remote server, but asset not in local cache."); + + return false; + } + + private void Copy(AssetBase from, AssetBase to) + { + to.Data = from.Data; + to.Metadata.Description = from.Metadata.Description; + to.Metadata.FullID = from.Metadata.FullID; + to.Metadata.ID = from.Metadata.ID; + to.Metadata.Local = from.Metadata.Local; + to.Metadata.Name = from.Metadata.Name; + to.Metadata.Temporary = from.Metadata.Temporary; + to.Metadata.Type = from.Metadata.Type; + + } + + private void _guardedAdd(Dictionary lst, UUID obj, bool val) + { + if (!lst.ContainsKey(obj)) + lst.Add(obj, val); + } + + private void SniffTextureUUIDs(Dictionary uuids, SceneObjectGroup sog) + { + try + { + _guardedAdd(uuids, sog.RootPart.Shape.Textures.DefaultTexture.TextureID, true); + } + catch (Exception) { } + + foreach (Primitive.TextureEntryFace tface in sog.RootPart.Shape.Textures.FaceTextures) + { + try + { + _guardedAdd(uuids, tface.TextureID, true); + } + catch (Exception) { } + } + + foreach (SceneObjectPart sop in sog.Children.Values) + { + try + { + _guardedAdd(uuids, sop.Shape.Textures.DefaultTexture.TextureID, true); + } + catch (Exception) { } + foreach (Primitive.TextureEntryFace tface in sop.Shape.Textures.FaceTextures) + { + try + { + _guardedAdd(uuids, tface.TextureID, true); + } + catch (Exception) { } + } + } + } + + private void SniffTaskInventoryUUIDs(Dictionary uuids, SceneObjectGroup sog) + { + TaskInventoryDictionary tinv = sog.RootPart.TaskInventory; + + foreach (TaskInventoryItem titem in tinv.Values) + { + uuids.Add(titem.AssetID, (InventoryType)titem.Type == InventoryType.Texture); + } + } + + private Dictionary SniffUUIDs(AssetBase asset) + { + Dictionary uuids = new Dictionary(); + if ((asset != null) && ((AssetType)asset.Metadata.Type == AssetType.Object)) + { + string ass_str = Utils.BytesToString(asset.Data); + SceneObjectGroup sog = new SceneObjectGroup(ass_str, true); + + SniffTextureUUIDs(uuids, sog); + + // We need to sniff further... + SniffTaskInventoryUUIDs(uuids, sog); + + } + + return uuids; + } + + private Dictionary SniffUUIDs(UUID assetID) + { + //Dictionary uuids = new Dictionary(); + + AssetBase asset; + m_scene.CommsManager.AssetCache.TryGetCachedAsset(assetID, out asset); + + return SniffUUIDs(asset); + } + + private void Dump(Dictionary lst) + { + m_log.Debug("XXX -------- UUID DUMP ------- XXX"); + foreach (KeyValuePair kvp in lst) + m_log.Debug(" >> " + kvp.Key + " (texture? " + kvp.Value + ")"); + m_log.Debug("XXX -------- UUID DUMP ------- XXX"); + } + + #endregion + + + #region Public interface + + public void Get(UUID itemID, UUID ownerID) + { + if (!IsInAssetMap(itemID) && !IsLocalUser(ownerID)) + { + // Get the item from the remote asset server onto the local AssetCache + // and place an entry in m_assetMap + + GridAssetClient asscli = null; + string userAssetURL = UserAssetURL(ownerID); + if (userAssetURL != null) + { + m_assetServers.TryGetValue(userAssetURL, out asscli); + if (asscli == null) + { + m_log.Debug("[HGScene]: Starting new GridAssetClient for " + userAssetURL); + asscli = new GridAssetClient(userAssetURL); + asscli.SetReceiver(m_scene.CommsManager.AssetCache); // Straight to the asset cache! + m_assetServers.Add(userAssetURL, asscli); + } + + m_log.Debug("[HGScene]: Fetching object " + itemID + " to asset server " + userAssetURL); + bool success = FetchAsset(asscli, itemID, false); // asscli.RequestAsset(item.ItemID, false); + + // OK, now fetch the inside. + Dictionary ids = SniffUUIDs(itemID); + Dump(ids); + foreach (KeyValuePair kvp in ids) + FetchAsset(asscli, kvp.Key, kvp.Value); + + + if (success) + { + m_log.Debug("[HGScene]: Successfully fetched item from remote asset server " + userAssetURL); + m_assetMap.Add(itemID, asscli); + } + else + m_log.Warn("[HGScene]: Could not fetch asset from remote asset server " + userAssetURL); + } + else + m_log.Warn("[HGScene]: Unable to locate foreign user's asset server"); + } + } + + public void Post(UUID itemID, UUID ownerID) + { + if (!IsLocalUser(ownerID)) + { + // Post the item from the local AssetCache ontp the remote asset server + // and place an entry in m_assetMap + + GridAssetClient asscli = null; + string userAssetURL = UserAssetURL(ownerID); + if (userAssetURL != null) + { + m_assetServers.TryGetValue(userAssetURL, out asscli); + if (asscli == null) + { + m_log.Debug("[HGScene]: Starting new GridAssetClient for " + userAssetURL); + asscli = new GridAssetClient(userAssetURL); + asscli.SetReceiver(m_scene.CommsManager.AssetCache); // Straight to the asset cache! + m_assetServers.Add(userAssetURL, asscli); + } + m_log.Debug("[HGScene]: Posting object " + itemID + " to asset server " + userAssetURL); + bool success = PostAsset(asscli, itemID); + + // Now the inside + Dictionary ids = SniffUUIDs(itemID); + Dump(ids); + foreach (KeyValuePair kvp in ids) + PostAsset(asscli, kvp.Key); + + if (success) + { + m_log.Debug("[HGScene]: Successfully posted item to remote asset server " + userAssetURL); + if (!m_assetMap.ContainsKey(itemID)) + m_assetMap.Add(itemID, asscli); + } + else + m_log.Warn("[HGScene]: Could not post asset to remote asset server " + userAssetURL); + + //if (!m_assetMap.ContainsKey(itemID)) + // m_assetMap.Add(itemID, asscli); + } + else + m_log.Warn("[HGScene]: Unable to locate foreign user's asset server"); + + } + } + + #endregion + + } +} diff --git a/OpenSim/Region/Framework/Scenes/Hypergrid/HGScene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Hypergrid/HGScene.Inventory.cs new file mode 100644 index 0000000..f36075e --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Hypergrid/HGScene.Inventory.cs @@ -0,0 +1,152 @@ +/** + * Copyright (c) 2008, Contributors. All rights reserved. + * 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 Organizations nor the names of Individual + * Contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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.Reflection; +using System.Threading; + +using log4net; +using Nini.Config; +using OpenMetaverse; + +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Framework.Servers; +// using OpenSim.Region.Environment; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Scenes.Hypergrid +{ + public partial class HGScene : Scene + { + #region Fields + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private HGAssetMapper m_assMapper; + + #endregion + + #region Constructors + + public HGScene(RegionInfo regInfo, AgentCircuitManager authen, + CommunicationsManager commsMan, SceneCommunicationService sceneGridService, + AssetCache assetCach, StorageManager storeManager, + ModuleLoader moduleLoader, bool dumpAssetsToFile, bool physicalPrim, + bool SeeIntoRegionFromNeighbor, IConfigSource config, string simulatorVersion) + : base(regInfo, authen, commsMan, sceneGridService, assetCach, storeManager, moduleLoader, + dumpAssetsToFile, physicalPrim, SeeIntoRegionFromNeighbor, config, simulatorVersion) + { + m_log.Info("[HGScene]: Starting HGScene."); + m_assMapper = new HGAssetMapper(this); + + EventManager.OnNewInventoryItemUploadComplete += UploadInventoryItem; + } + + #endregion + + #region Event handlers + + public void UploadInventoryItem(UUID avatarID, UUID assetID, string name, int userlevel) + { + CachedUserInfo userInfo = CommsManager.UserProfileCacheService.GetUserDetails(avatarID); + if (userInfo != null) + { + m_assMapper.Post(assetID, avatarID); + } + } + + #endregion + + #region Overrides of Scene.Inventory methods + + /// + /// CapsUpdateInventoryItemAsset + /// + public override UUID CapsUpdateInventoryItemAsset(IClientAPI remoteClient, UUID itemID, byte[] data) + { + UUID newAssetID = base.CapsUpdateInventoryItemAsset(remoteClient, itemID, data); + + UploadInventoryItem(remoteClient.AgentId, newAssetID, "", 0); + + return newAssetID; + } + + /// + /// DeleteToInventory + /// + public override UUID DeleteToInventory(DeRezAction action, UUID folderID, SceneObjectGroup objectGroup, IClientAPI remoteClient) + { + UUID assetID = base.DeleteToInventory(action, folderID, objectGroup, remoteClient); + + if (!assetID.Equals(UUID.Zero)) + { + UploadInventoryItem(remoteClient.AgentId, assetID, "", 0); + } + else + m_log.Debug("[HGScene]: Scene.Inventory did not create asset"); + + return assetID; + } + + /// + /// RezObject + /// + public override SceneObjectGroup RezObject(IClientAPI remoteClient, UUID itemID, Vector3 RayEnd, Vector3 RayStart, + UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, + bool RezSelected, bool RemoveItem, UUID fromTaskID, bool attachment) + { + CachedUserInfo userInfo = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + if (userInfo != null) + { + if (userInfo.RootFolder != null) + { + InventoryItemBase item = userInfo.RootFolder.FindItem(itemID); + + if (item != null) + { + m_assMapper.Get(item.AssetID, remoteClient.AgentId); + + } + } + } + + // 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); + + } + + + #endregion + + } + +} diff --git a/OpenSim/Region/Framework/Scenes/Hypergrid/HGScene.cs b/OpenSim/Region/Framework/Scenes/Hypergrid/HGScene.cs new file mode 100644 index 0000000..a52fdc6 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Hypergrid/HGScene.cs @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2008, Contributors. All rights reserved. + * 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 Organizations nor the names of Individual + * Contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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; + +using OpenSim.Framework; + +using OpenSim.Framework.Communications.Cache; +// using OpenSim.Region.Environment; +using OpenSim.Region.Framework.Scenes; +using TPFlags = OpenSim.Framework.Constants.TeleportFlags; + +namespace OpenSim.Region.Framework.Scenes.Hypergrid +{ + public partial class HGScene : Scene + { + /// + /// Teleport an avatar to their home region + /// + /// + /// + public override void TeleportClientHome(UUID agentId, IClientAPI client) + { + m_log.Debug("[HGScene]: TeleportClientHome " + client.FirstName + " " + client.LastName); + + CachedUserInfo uinfo = CommsManager.UserProfileCacheService.GetUserDetails(agentId); + if (uinfo != null) + { + UserProfileData UserProfile = uinfo.UserProfile; + + if (UserProfile != null) + { + RegionInfo regionInfo = CommsManager.GridService.RequestNeighbourInfo(UserProfile.HomeRegion); + //if (regionInfo != null) + //{ + // UserProfile.HomeRegionID = regionInfo.RegionID; + // //CommsManager.UserService.UpdateUserProfile(UserProfile); + //} + if (regionInfo == null) + { + // can't find the Home region: Tell viewer and abort + client.SendTeleportFailed("Your home-region could not be found."); + return; + } + RequestTeleportLocation( + client, regionInfo.RegionHandle, UserProfile.HomeLocation, UserProfile.HomeLookAt, + (uint)(TPFlags.SetLastToTarget | TPFlags.ViaHome)); + } + } + else + client.SendTeleportFailed("Sorry! I lost your home-region information."); + + } + + } +} diff --git a/OpenSim/Region/Framework/Scenes/Hypergrid/HGSceneCommunicationService.cs b/OpenSim/Region/Framework/Scenes/Hypergrid/HGSceneCommunicationService.cs new file mode 100644 index 0000000..d0dc4f7 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Hypergrid/HGSceneCommunicationService.cs @@ -0,0 +1,360 @@ +/** + * Copyright (c) 2008, Contributors. All rights reserved. + * 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 Organizations nor the names of Individual + * Contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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.Net; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OSD = OpenMetaverse.StructuredData.OSD; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Framework.Communications.Capabilities; +using OpenSim.Region.Framework.Scenes; +// using OpenSim.Region.Environment; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.Framework.Scenes.Hypergrid +{ + public class HGSceneCommunicationService : SceneCommunicationService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public readonly IHyperlink m_hg; + + public HGSceneCommunicationService(CommunicationsManager commsMan, IHyperlink hg) : base(commsMan) + { + m_hg = hg; + } + + + /// + /// Try to teleport an agent to a new region. + /// + /// + /// + /// + /// + /// + public override void RequestTeleportToLocation(ScenePresence avatar, ulong regionHandle, Vector3 position, + Vector3 lookAt, uint teleportFlags) + { + if (!avatar.Scene.Permissions.CanTeleport(avatar.UUID)) + return; + + bool destRegionUp = true; + + IEventQueue eq = avatar.Scene.RequestModuleInterface(); + + if (regionHandle == m_regionInfo.RegionHandle) + { + // Teleport within the same region + if (position.X < 0 || position.X > Constants.RegionSize || position.Y < 0 || position.Y > Constants.RegionSize || position.Z < 0) + { + Vector3 emergencyPos = new Vector3(128, 128, 128); + + m_log.WarnFormat( + "[HGSceneCommService]: RequestTeleportToLocation() was given an illegal position of {0} for avatar {1}, {2}. Substituting {3}", + position, avatar.Name, avatar.UUID, emergencyPos); + position = emergencyPos; + } + // TODO: Get proper AVG Height + float localAVHeight = 1.56f; + float posZLimit = (float)avatar.Scene.GetLandHeight((int)position.X, (int)position.Y); + float newPosZ = posZLimit + localAVHeight; + if (posZLimit >= (position.Z - (localAVHeight / 2)) && !(Single.IsInfinity(newPosZ) || Single.IsNaN(newPosZ))) + { + position.Z = newPosZ; + } + + // Only send this if the event queue is null + if (eq == null) + avatar.ControllingClient.SendTeleportLocationStart(); + + + avatar.ControllingClient.SendLocalTeleport(position, lookAt, teleportFlags); + avatar.Teleport(position); + } + else + { + RegionInfo reg = RequestNeighbouringRegionInfo(regionHandle); + if (reg != null) + { + + uint newRegionX = (uint)(reg.RegionHandle >> 40); + uint newRegionY = (((uint)(reg.RegionHandle)) >> 8); + uint oldRegionX = (uint)(m_regionInfo.RegionHandle >> 40); + uint oldRegionY = (((uint)(m_regionInfo.RegionHandle)) >> 8); + + /// + /// Hypergrid mod start + /// + /// + bool isHyperLink = m_hg.IsHyperlinkRegion(reg.RegionHandle); + bool isHomeUser = true; + ulong realHandle = regionHandle; + CachedUserInfo uinfo = m_commsProvider.UserProfileCacheService.GetUserDetails(avatar.UUID); + if (uinfo != null) + { + isHomeUser = HGNetworkServersInfo.Singleton.IsLocalUser(uinfo.UserProfile); + realHandle = m_hg.FindRegionHandle(regionHandle); + Console.WriteLine("XXX ---- home user? " + isHomeUser + " --- hyperlink? " + isHyperLink + " --- real handle: " + realHandle.ToString()); + } + /// + /// Hypergrid mod stop + /// + /// + + if (eq == null) + avatar.ControllingClient.SendTeleportLocationStart(); + + + // Let's do DNS resolution only once in this process, please! + // This may be a costly operation. The reg.ExternalEndPoint field is not a passive field, + // it's actually doing a lot of work. + IPEndPoint endPoint = reg.ExternalEndPoint; + if (endPoint.Address == null) + { + // Couldn't resolve the name. Can't TP, because the viewer wants IP addresses. + destRegionUp = false; + } + + if (destRegionUp) + { + // Fixing a bug where teleporting while sitting results in the avatar ending up removed from + // both regions + if (avatar.ParentID != (uint)0) + avatar.StandUp(); + + if (!avatar.ValidateAttachments()) + { + avatar.ControllingClient.SendTeleportFailed("Inconsistent attachment state"); + return; + } + + // the avatar.Close below will clear the child region list. We need this below for (possibly) + // closing the child agents, so save it here (we need a copy as it is Clear()-ed). + //List childRegions = new List(avatar.GetKnownRegionList()); + // Compared to ScenePresence.CrossToNewRegion(), there's no obvious code to handle a teleport + // failure at this point (unlike a border crossing failure). So perhaps this can never fail + // once we reach here... + //avatar.Scene.RemoveCapsHandler(avatar.UUID); + + string capsPath = String.Empty; + AgentCircuitData agentCircuit = avatar.ControllingClient.RequestClientInfo(); + agentCircuit.BaseFolder = UUID.Zero; + agentCircuit.InventoryFolder = UUID.Zero; + agentCircuit.startpos = position; + agentCircuit.child = true; + if (Util.IsOutsideView(oldRegionX, newRegionX, oldRegionY, newRegionY)) + { + // brand new agent, let's create a new caps seed + agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath(); + } + + //if (!m_commsProvider.InterRegion.InformRegionOfChildAgent(reg.RegionHandle, agentCircuit)) + if (!m_interregionCommsOut.SendCreateChildAgent(reg.RegionHandle, agentCircuit)) + { + avatar.ControllingClient.SendTeleportFailed("Destination is not accepting teleports."); + return; + } + + // Let's close some agents + if (isHyperLink) // close them all except this one + { + List regions = new List(avatar.KnownChildRegionHandles); + regions.Remove(avatar.Scene.RegionInfo.RegionHandle); + SendCloseChildAgentConnections(avatar.UUID, regions); + } + else // close just a few + avatar.CloseChildAgents(newRegionX, newRegionY); + + if (Util.IsOutsideView(oldRegionX, newRegionX, oldRegionY, newRegionY) || isHyperLink) + { + capsPath + = "http://" + + reg.ExternalHostName + + ":" + + reg.HttpPort + + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath); + + if (eq != null) + { + eq.EnableSimulator(realHandle, endPoint, avatar.UUID); + + // ES makes the client send a UseCircuitCode message to the destination, + // which triggers a bunch of things there. + // So let's wait + Thread.Sleep(2000); + + eq.EstablishAgentCommunication(avatar.UUID, endPoint, capsPath); + } + else + { + avatar.ControllingClient.InformClientOfNeighbour(realHandle, endPoint); + // TODO: make Event Queue disablable! + } + } + else + { + // child agent already there + agentCircuit.CapsPath = avatar.Scene.CapsModule.GetChildSeed(avatar.UUID, reg.RegionHandle); + capsPath = "http://" + reg.ExternalHostName + ":" + reg.HttpPort + + "/CAPS/" + agentCircuit.CapsPath + "0000/"; + } + + //m_commsProvider.InterRegion.ExpectAvatarCrossing(reg.RegionHandle, avatar.ControllingClient.AgentId, + // position, false); + + //if (!m_commsProvider.InterRegion.ExpectAvatarCrossing(reg.RegionHandle, avatar.ControllingClient.AgentId, + // position, false)) + //{ + // avatar.ControllingClient.SendTeleportFailed("Problem with destination."); + // // We should close that agent we just created over at destination... + // List lst = new List(); + // lst.Add(realHandle); + // SendCloseChildAgentAsync(avatar.UUID, lst); + // return; + //} + + SetInTransit(avatar.UUID); + // Let's send a full update of the agent. This is a synchronous call. + AgentData agent = new AgentData(); + avatar.CopyTo(agent); + agent.Position = position; + agent.CallbackURI = "http://" + m_regionInfo.ExternalHostName + ":" + m_regionInfo.HttpPort + + "/agent/" + avatar.UUID.ToString() + "/" + avatar.Scene.RegionInfo.RegionHandle.ToString() + "/release/"; + + m_interregionCommsOut.SendChildAgentUpdate(reg.RegionHandle, agent); + + m_log.DebugFormat( + "[CAPS]: Sending new CAPS seed url {0} to client {1}", agentCircuit.CapsPath, avatar.UUID); + + + /// + /// Hypergrid mod: realHandle instead of reg.RegionHandle + /// + /// + if (eq != null) + { + eq.TeleportFinishEvent(realHandle, 13, endPoint, + 4, teleportFlags, capsPath, avatar.UUID); + } + else + { + avatar.ControllingClient.SendRegionTeleport(realHandle, 13, endPoint, 4, + teleportFlags, capsPath); + } + /// + /// Hypergrid mod stop + /// + + + // TeleportFinish makes the client send CompleteMovementIntoRegion (at the destination), which + // trigers a whole shebang of things there, including MakeRoot. So let's wait for confirmation + // that the client contacted the destination before we send the attachments and close things here. + if (!WaitForCallback(avatar.UUID)) + { + // Client never contacted destination. Let's restore everything back + avatar.ControllingClient.SendTeleportFailed("Problems connecting to destination."); + + ResetFromTransit(avatar.UUID); + // Yikes! We should just have a ref to scene here. + avatar.Scene.InformClientOfNeighbours(avatar); + + // Finally, kill the agent we just created at the destination. + m_interregionCommsOut.SendCloseAgent(reg.RegionHandle, avatar.UUID); + + return; + } + + // Can't go back from here + if (KiPrimitive != null) + { + KiPrimitive(avatar.LocalId); + } + + avatar.MakeChildAgent(); + + // CrossAttachmentsIntoNewRegion is a synchronous call. We shouldn't need to wait after it + avatar.CrossAttachmentsIntoNewRegion(reg.RegionHandle, true); + + + // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone + /// + /// Hypergrid mod: extra check for isHyperLink + /// + if (Util.IsOutsideView(oldRegionX, newRegionX, oldRegionY, newRegionY) || isHyperLink) + { + Thread.Sleep(5000); + avatar.Close(); + CloseConnection(avatar.UUID); + } + // if (teleport success) // seems to be always success here + // the user may change their profile information in other region, + // so the userinfo in UserProfileCache is not reliable any more, delete it + if (avatar.Scene.NeedSceneCacheClear(avatar.UUID) || isHyperLink) + { + m_commsProvider.UserProfileCacheService.RemoveUser(avatar.UUID); + m_log.DebugFormat( + "[HGSceneCommService]: User {0} is going to another region, profile cache removed", + avatar.UUID); + } + } + else + { + avatar.ControllingClient.SendTeleportFailed("Remote Region appears to be down"); + } + } + else + { + // TP to a place that doesn't exist (anymore) + // Inform the viewer about that + avatar.ControllingClient.SendTeleportFailed("The region you tried to teleport to doesn't exist anymore"); + + // and set the map-tile to '(Offline)' + uint regX, regY; + Utils.LongToUInts(regionHandle, out regX, out regY); + + MapBlockData block = new MapBlockData(); + block.X = (ushort)(regX / Constants.RegionSize); + block.Y = (ushort)(regY / Constants.RegionSize); + block.Access = 254; // == not there + + List blocks = new List(); + blocks.Add(block); + avatar.ControllingClient.SendMapBlock(blocks, 0); + } + } + } + + } +} diff --git a/OpenSim/Region/Framework/Scenes/IScenePresenceBody.cs b/OpenSim/Region/Framework/Scenes/IScenePresenceBody.cs new file mode 100644 index 0000000..bd5ad33 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/IScenePresenceBody.cs @@ -0,0 +1,37 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Region.Framework.Scenes +{ + public interface IScenePresenceBody + { + void processMovement(IClientAPI remoteClient, uint flags, Quaternion bodyRotation); + } +} diff --git a/OpenSim/Region/Framework/Scenes/ReturnInfo.cs b/OpenSim/Region/Framework/Scenes/ReturnInfo.cs new file mode 100644 index 0000000..43d07f5 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/ReturnInfo.cs @@ -0,0 +1,39 @@ +/* + * 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 OpenSim 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 OpenMetaverse; + +namespace OpenSim.Region.Framework.Scenes +{ + public struct ReturnInfo + { + public int count; + public Vector3 location; + public string objectName; + public string reason; + } +} diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs new file mode 100644 index 0000000..e366c79 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -0,0 +1,2665 @@ +/* + * 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 OpenSim 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 System.Text; +using System.Timers; +using OpenMetaverse; +using log4net; +using OpenSim.Framework; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Region.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.Framework.Scenes +{ + public partial class Scene + { + private static readonly ILog m_log + = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Allows asynchronous derezzing of objects from the scene into a client's inventory. + /// + protected AsyncSceneObjectGroupDeleter m_asyncSceneObjectDeleter; + + /// + /// Start all the scripts in the scene which should be started. + /// + public void CreateScriptInstances() + { + m_log.Info("[PRIM INVENTORY]: Starting scripts in scene"); + + foreach (EntityBase group in Entities) + { + if (group is SceneObjectGroup) + { + ((SceneObjectGroup) group).CreateScriptInstances(0, false, DefaultScriptEngine, 0); + } + } + } + + public void AddUploadedInventoryItem(UUID agentID, InventoryItemBase item) + { + IMoneyModule money=RequestModuleInterface(); + if (money != null) + { + money.ApplyUploadCharge(agentID); + } + + AddInventoryItem(agentID, item); + } + + public bool AddInventoryItemReturned(UUID AgentId, InventoryItemBase item) + { + CachedUserInfo userInfo + = CommsManager.UserProfileCacheService.GetUserDetails(AgentId); + if (userInfo != null) + { + userInfo.AddItem(item); + return true; + } + else + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Agent was not found for add of item {1} {2}", item.Name, item.ID); + + return false; + } + } + + public void AddInventoryItem(UUID AgentID, InventoryItemBase item) + { + CachedUserInfo userInfo + = CommsManager.UserProfileCacheService.GetUserDetails(AgentID); + + if (userInfo != null) + { + userInfo.AddItem(item); + + int userlevel = 0; + if (Permissions.IsGod(AgentID)) + { + userlevel = 1; + } + // TODO: remove this cruft once MasterAvatar is fully deprecated + // + if (m_regInfo.MasterAvatarAssignedUUID == AgentID) + { + userlevel = 2; + } + EventManager.TriggerOnNewInventoryItemUploadComplete(AgentID, item.AssetID, item.Name, userlevel); + } + else + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Agent {1} was not found for add of item {2} {3}", + AgentID, item.Name, item.ID); + + return; + } + } + + /// + /// Add an inventory item to an avatar's inventory. + /// + /// The remote client controlling the avatar + /// The item. This structure contains all the item metadata, including the folder + /// in which the item is to be placed. + public void AddInventoryItem(IClientAPI remoteClient, InventoryItemBase item) + { + CachedUserInfo userInfo + = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (userInfo != null) + { + AddInventoryItem(remoteClient.AgentId, item); + remoteClient.SendInventoryItemCreateUpdate(item); + } + else + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Could not resolve user {0} for adding an inventory item", + remoteClient.AgentId); + } + } + + /// + /// Capability originating call to update the asset of an item in an agent's inventory + /// + /// + /// + /// + /// + public virtual UUID CapsUpdateInventoryItemAsset(IClientAPI remoteClient, UUID itemID, byte[] data) + { + CachedUserInfo userInfo = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + if (userInfo != null) + { + if (userInfo.RootFolder != null) + { + InventoryItemBase item = userInfo.RootFolder.FindItem(itemID); + + if (item != null) + { + if ((InventoryType)item.InvType == InventoryType.Notecard) + { + if (!Permissions.CanEditNotecard(itemID, UUID.Zero, remoteClient.AgentId)) + { + remoteClient.SendAgentAlertMessage("Insufficient permissions to edit notecard", false); + return UUID.Zero; + } + + remoteClient.SendAgentAlertMessage("Notecard saved", false); + } + else if ((InventoryType)item.InvType == InventoryType.LSL) + { + if (!Permissions.CanEditScript(itemID, UUID.Zero, remoteClient.AgentId)) + { + remoteClient.SendAgentAlertMessage("Insufficient permissions to edit script", false); + return UUID.Zero; + } + + remoteClient.SendAgentAlertMessage("Script saved", false); + } + + AssetBase asset = + CreateAsset(item.Name, item.Description, (sbyte)item.AssetType, data); + AssetCache.AddAsset(asset); + + item.AssetID = asset.Metadata.FullID; + userInfo.UpdateItem(item); + + // remoteClient.SendInventoryItemCreateUpdate(item); + return (asset.Metadata.FullID); + } + } + } + else + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Could not resolve user {0} for caps inventory update", + remoteClient.AgentId); + } + + return UUID.Zero; + } + + /// + /// CapsUpdatedInventoryItemAsset(IClientAPI, UUID, byte[]) + /// + public UUID CapsUpdateInventoryItemAsset(UUID avatarId, UUID itemID, byte[] data) + { + ScenePresence avatar; + + if (TryGetAvatar(avatarId, out avatar)) + { + return CapsUpdateInventoryItemAsset(avatar.ControllingClient, itemID, data); + } + else + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: " + + "Avatar {0} cannot be found to update its inventory item asset", + avatarId); + } + + return UUID.Zero; + } + + /// + /// Capability originating call to update the asset of a script in a prim's (task's) inventory + /// + /// + /// + /// The prim which contains the item to update + /// Indicates whether the script to update is currently running + /// + public void CapsUpdateTaskInventoryScriptAsset(IClientAPI remoteClient, UUID itemId, + UUID primId, bool isScriptRunning, byte[] data) + { + if (!Permissions.CanEditScript(itemId, primId, remoteClient.AgentId)) + { + remoteClient.SendAgentAlertMessage("Insufficient permissions to edit script", false); + return; + } + + // Retrieve group + SceneObjectPart part = GetSceneObjectPart(primId); + SceneObjectGroup group = part.ParentGroup; + if (null == group) + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Prim inventory update requested for item ID {0} in prim ID {1} but this prim does not exist", + itemId, primId); + + return; + } + + // Retrieve item + TaskInventoryItem item = group.GetInventoryItem(part.LocalId, itemId); + + if (null == item) + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: Tried to retrieve item ID {0} from prim {1}, {2} for caps script update " + + " but the item does not exist in this inventory", + itemId, part.Name, part.UUID); + + return; + } + + AssetBase asset = CreateAsset(item.Name, item.Description, (sbyte)AssetType.LSLText, data); + AssetCache.AddAsset(asset); + + if (isScriptRunning) + { + part.Inventory.RemoveScriptInstance(item.ItemID); + } + + // Update item with new asset + item.AssetID = asset.Metadata.FullID; + group.UpdateInventoryItem(item); + part.GetProperties(remoteClient); + + // Trigger rerunning of script (use TriggerRezScript event, see RezScript) + if (isScriptRunning) + { + // Needs to determine which engine was running it and use that + // + part.Inventory.CreateScriptInstance(item.ItemID, 0, false, DefaultScriptEngine, 0); + } + else + { + remoteClient.SendAgentAlertMessage("Script saved", false); + } + } + + /// + /// CapsUpdateTaskInventoryScriptAsset(IClientAPI, UUID, UUID, bool, byte[]) + /// + public void CapsUpdateTaskInventoryScriptAsset(UUID avatarId, UUID itemId, + UUID primId, bool isScriptRunning, byte[] data) + { + ScenePresence avatar; + + if (TryGetAvatar(avatarId, out avatar)) + { + CapsUpdateTaskInventoryScriptAsset( + avatar.ControllingClient, itemId, primId, isScriptRunning, data); + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Avatar {0} cannot be found to update its prim item asset", + avatarId); + } + } + + /// + /// Update an item which is either already in the client's inventory or is within + /// a transaction + /// + /// + /// The transaction ID. If this is UUID.Zero we will + /// assume that we are not in a transaction + /// The ID of the updated item + /// The name of the updated item + /// The description of the updated item + /// The permissions of the updated item +/* public void UpdateInventoryItemAsset(IClientAPI remoteClient, UUID transactionID, + UUID itemID, string name, string description, + uint nextOwnerMask)*/ + public void UpdateInventoryItemAsset(IClientAPI remoteClient, UUID transactionID, + UUID itemID, InventoryItemBase itemUpd) + { + CachedUserInfo userInfo + = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (userInfo != null && userInfo.RootFolder != null) + { + InventoryItemBase item = userInfo.RootFolder.FindItem(itemID); + + if (item != null) + { + if (UUID.Zero == transactionID) + { + item.Name = itemUpd.Name; + item.Description = itemUpd.Description; + item.NextPermissions = itemUpd.NextPermissions; + item.CurrentPermissions |= 8; // Slam! + item.EveryOnePermissions = itemUpd.EveryOnePermissions; + item.GroupPermissions = itemUpd.GroupPermissions; + + item.GroupID = itemUpd.GroupID; + item.GroupOwned = itemUpd.GroupOwned; + item.CreationDate = itemUpd.CreationDate; + // The client sends zero if its newly created? + + if (itemUpd.CreationDate == 0) + item.CreationDate = Util.UnixTimeSinceEpoch(); + else + item.CreationDate = itemUpd.CreationDate; + + // TODO: Check if folder changed and move item + //item.NextPermissions = itemUpd.Folder; + item.InvType = itemUpd.InvType; + item.SalePrice = itemUpd.SalePrice; + item.SaleType = itemUpd.SaleType; + item.Flags = itemUpd.Flags; + + userInfo.UpdateItem(item); + } + else + { + IAgentAssetTransactions agentTransactions = this.RequestModuleInterface(); + if (agentTransactions != null) + { + agentTransactions.HandleItemUpdateFromTransaction( + remoteClient, transactionID, item); + } + } + } + else + { + m_log.Error( + "[AGENTINVENTORY]: Item ID " + itemID + " not found for an inventory item update."); + } + } + else + { + m_log.Error( + "[AGENT INVENTORY]: Agent ID " + remoteClient.AgentId + " not found for an inventory item update."); + } + } + + /// + /// Give an inventory item from one user to another + /// + /// + /// ID of the sender of the item + /// + public virtual void GiveInventoryItem(IClientAPI recipientClient, UUID senderId, UUID itemId) + { + InventoryItemBase itemCopy = GiveInventoryItem(recipientClient.AgentId, senderId, itemId); + + if (itemCopy != null) + recipientClient.SendBulkUpdateInventory(itemCopy); + } + + /// + /// Give an inventory item from one user to another + /// + /// + /// ID of the sender of the item + /// + /// The inventory item copy given, null if the give was unsuccessful + public virtual InventoryItemBase GiveInventoryItem(UUID recipient, UUID senderId, UUID itemId) + { + return GiveInventoryItem(recipient, senderId, itemId, UUID.Zero); + } + + /// + /// Give an inventory item from one user to another + /// + /// + /// ID of the sender of the item + /// + /// + /// The id of the folder in which the copy item should go. If UUID.Zero then the item is placed in the most + /// appropriate default folder. + /// + /// + /// The inventory item copy given, null if the give was unsuccessful + /// + public virtual InventoryItemBase GiveInventoryItem( + UUID recipient, UUID senderId, UUID itemId, UUID recipientFolderId) + { + // Retrieve the item from the sender + CachedUserInfo senderUserInfo = CommsManager.UserProfileCacheService.GetUserDetails(senderId); + + if (senderUserInfo == null) + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Failed to find sending user {0} for item {1}", senderId, itemId); + + return null; + } + + if (senderUserInfo.RootFolder != null) + { + InventoryItemBase item = senderUserInfo.RootFolder.FindItem(itemId); + + if (item != null) + { + if (!Permissions.BypassPermissions()) + { + if ((item.CurrentPermissions & (uint)PermissionMask.Transfer) == 0) + return null; + } + + CachedUserInfo recipientUserInfo + = CommsManager.UserProfileCacheService.GetUserDetails(recipient); + + if (recipientUserInfo != null) + { + if (!recipientUserInfo.HasReceivedInventory) + CommsManager.UserProfileCacheService.RequestInventoryForUser(recipient); + + // Insert a copy of the item into the recipient + InventoryItemBase itemCopy = new InventoryItemBase(); + itemCopy.Owner = recipient; + itemCopy.Creator = item.Creator; + itemCopy.ID = UUID.Random(); + itemCopy.AssetID = item.AssetID; + itemCopy.Description = item.Description; + itemCopy.Name = item.Name; + itemCopy.AssetType = item.AssetType; + itemCopy.InvType = item.InvType; + itemCopy.Folder = recipientFolderId; + + if (Permissions.PropagatePermissions()) + { + if (item.InvType == 6) + { + itemCopy.BasePermissions &= ~(uint)(PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer); + itemCopy.BasePermissions |= (item.CurrentPermissions & 7) << 13; + } + else + { + itemCopy.BasePermissions = item.BasePermissions & item.NextPermissions; + } + + itemCopy.CurrentPermissions = itemCopy.BasePermissions; + if ((item.CurrentPermissions & 8) != 0) // Propagate slam bit + { + itemCopy.BasePermissions &= item.NextPermissions; + itemCopy.CurrentPermissions = itemCopy.BasePermissions; + itemCopy.CurrentPermissions |= 8; + } + + itemCopy.NextPermissions = item.NextPermissions; + itemCopy.EveryOnePermissions = item.EveryOnePermissions & item.NextPermissions; + itemCopy.GroupPermissions = item.GroupPermissions & item.NextPermissions; + } + else + { + itemCopy.CurrentPermissions = item.CurrentPermissions; + itemCopy.NextPermissions = item.NextPermissions; + itemCopy.EveryOnePermissions = item.EveryOnePermissions & item.NextPermissions; + itemCopy.GroupPermissions = item.GroupPermissions & item.NextPermissions; + itemCopy.BasePermissions = item.BasePermissions; + } + itemCopy.GroupID = UUID.Zero; + itemCopy.GroupOwned = false; + itemCopy.Flags = item.Flags; + itemCopy.SalePrice = item.SalePrice; + itemCopy.SaleType = item.SaleType; + + itemCopy.CreationDate = item.CreationDate; + + recipientUserInfo.AddItem(itemCopy); + + if (!Permissions.BypassPermissions()) + { + if ((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0) + senderUserInfo.DeleteItem(itemId); + } + + return itemCopy; + } + else + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Could not find userinfo for recipient user {0} of item {1}, {2} from {3}", + recipient, item.Name, + item.ID, senderId); + } + } + else + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Failed to find item {0} to give to {1}", itemId, senderId); + + return null; + } + } + else + { + m_log.Error("[AGENT INVENTORY]: Failed to find item " + itemId.ToString() + ", no root folder"); + return null; + } + + return null; + } + + /// + /// Give an entire inventory folder from one user to another. The entire contents (including all descendent + /// folders) is given. + /// + /// + /// ID of the sender of the item + /// + /// + /// The id of the receipient folder in which the send folder should be placed. If UUID.Zero then the + /// recipient folder is the root folder + /// + /// + /// The inventory folder copy given, null if the copy was unsuccessful + /// + public virtual InventoryFolderImpl GiveInventoryFolder( + UUID recipientId, UUID senderId, UUID folderId, UUID recipientParentFolderId) + { + // Retrieve the folder from the sender + CachedUserInfo senderUserInfo = CommsManager.UserProfileCacheService.GetUserDetails(senderId); + + if (null == senderUserInfo) + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Failed to find sending user {0} for folder {1}", senderId, folderId); + + return null; + } + + if (!senderUserInfo.HasReceivedInventory) + { + m_log.DebugFormat( + "[AGENT INVENTORY]: Could not give inventory folder - have not yet received inventory for {0}", + senderId); + + return null; + } + + InventoryFolderImpl folder = senderUserInfo.RootFolder.FindFolder(folderId); + + if (null == folder) + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Could not find inventory folder {0} to give", folderId); + + return null; + } + + CachedUserInfo recipientUserInfo + = CommsManager.UserProfileCacheService.GetUserDetails(recipientId); + + if (null == recipientUserInfo) + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Failed to find receiving user {0} for folder {1}", recipientId, folderId); + + return null; + } + + if (recipientParentFolderId == UUID.Zero) + recipientParentFolderId = recipientUserInfo.RootFolder.ID; + + UUID newFolderId = UUID.Random(); + recipientUserInfo.CreateFolder(folder.Name, newFolderId, (ushort)folder.Type, recipientParentFolderId); + + // XXX: Messy - we should really get this back in the CreateFolder call + InventoryFolderImpl copiedFolder = recipientUserInfo.RootFolder.FindFolder(newFolderId); + + // Give all the subfolders + List subFolders = folder.RequestListOfFolderImpls(); + foreach (InventoryFolderImpl childFolder in subFolders) + { + GiveInventoryFolder(recipientId, senderId, childFolder.ID, copiedFolder.ID); + } + + // Give all the items + List items = folder.RequestListOfItems(); + foreach (InventoryItemBase item in items) + { + GiveInventoryItem(recipientId, senderId, item.ID, copiedFolder.ID); + } + + return copiedFolder; + } + + public void CopyInventoryItem(IClientAPI remoteClient, uint callbackID, UUID oldAgentID, UUID oldItemID, + UUID newFolderID, string newName) + { + m_log.DebugFormat( + "[AGENT INVENTORY]: CopyInventoryItem received by {0} with oldAgentID {1}, oldItemID {2}, new FolderID {3}, newName {4}", + remoteClient.AgentId, oldAgentID, oldItemID, newFolderID, newName); + + InventoryItemBase item = CommsManager.UserProfileCacheService.LibraryRoot.FindItem(oldItemID); + + if (item == null) + { + CachedUserInfo userInfo = CommsManager.UserProfileCacheService.GetUserDetails(oldAgentID); + if (userInfo == null) + { + m_log.Error("[AGENT INVENTORY]: Failed to find user " + oldAgentID.ToString()); + return; + } + + if (userInfo.RootFolder != null) + { + item = userInfo.RootFolder.FindItem(oldItemID); + + if (item == null) + { + m_log.Error("[AGENT INVENTORY]: Failed to find item " + oldItemID.ToString()); + return; + } + } + else + { + m_log.Error("[AGENT INVENTORY]: Failed to find item " + oldItemID.ToString()); + return; + } + } + + AssetBase asset + = AssetCache.GetAsset( + item.AssetID, (item.AssetType == (int)AssetType.Texture ? true : false)); + + if (asset != null) + { + if (newName != String.Empty) + { + asset.Metadata.Name = newName; + } + else + { + newName = item.Name; + } + + if (remoteClient.AgentId == oldAgentID) + { + CreateNewInventoryItem( + remoteClient, newFolderID, newName, item.Flags, callbackID, asset, (sbyte)item.InvType, + item.BasePermissions, item.CurrentPermissions, item.EveryOnePermissions, item.NextPermissions, item.GroupPermissions, Util.UnixTimeSinceEpoch()); + } + else + { + CreateNewInventoryItem( + remoteClient, newFolderID, newName, item.Flags, callbackID, asset, (sbyte)item.InvType, + item.NextPermissions, item.NextPermissions, item.EveryOnePermissions & item.NextPermissions, item.NextPermissions, item.GroupPermissions, Util.UnixTimeSinceEpoch()); + } + } + else + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Could not copy item {0} since asset {1} could not be found", + item.Name, item.AssetID); + } + } + + /// + /// Create a new asset data structure. + /// + /// + /// + /// + /// + /// + /// + private AssetBase CreateAsset(string name, string description, sbyte assetType, byte[] data) + { + AssetBase asset = new AssetBase(); + asset.Metadata.Name = name; + asset.Metadata.Description = description; + asset.Metadata.Type = assetType; + asset.Metadata.FullID = UUID.Random(); + asset.Data = (data == null) ? new byte[1] : data; + + return asset; + } + + /// + /// Move an item within the agent's inventory. + /// + /// + /// + /// + /// + /// + public void MoveInventoryItem(IClientAPI remoteClient, UUID folderID, UUID itemID, int length, + string newName) + { + m_log.DebugFormat( + "[AGENT INVENTORY]: Moving item {0} to {1} for {2}", itemID, folderID, remoteClient.AgentId); + + CachedUserInfo userInfo = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (userInfo == null) + { + m_log.Error("[AGENT INVENTORY]: Failed to find user " + remoteClient.AgentId.ToString()); + + return; + } + + if (userInfo.RootFolder != null) + { + InventoryItemBase item = userInfo.RootFolder.FindItem(itemID); + + if (item != null) + { + if (newName != String.Empty) + { + item.Name = newName; + } + item.Folder = folderID; + + userInfo.DeleteItem(item.ID); + + AddInventoryItem(remoteClient, item); + } + else + { + m_log.Error("[AGENT INVENTORY]: Failed to find item " + itemID.ToString()); + + return; + } + } + else + { + m_log.Error("[AGENT INVENTORY]: Failed to find item " + itemID.ToString() + ", no root folder"); + + return; + } + } + + /// + /// Create a new inventory item. + /// + /// + /// + /// + /// + /// + /// + private void CreateNewInventoryItem(IClientAPI remoteClient, UUID folderID, string name, uint flags, uint callbackID, + AssetBase asset, sbyte invType, uint nextOwnerMask, int creationDate) + { + CreateNewInventoryItem( + remoteClient, folderID, name, flags, callbackID, asset, invType, + (uint)PermissionMask.All, (uint)PermissionMask.All, 0, nextOwnerMask, 0, creationDate); + } + + /// + /// Create a new Inventory Item + /// + /// + /// + /// + /// + /// + /// + /// + private void CreateNewInventoryItem( + IClientAPI remoteClient, UUID folderID, string name, uint flags, uint callbackID, AssetBase asset, sbyte invType, + uint baseMask, uint currentMask, uint everyoneMask, uint nextOwnerMask, uint groupMask, int creationDate) + { + CachedUserInfo userInfo + = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (userInfo != null) + { + InventoryItemBase item = new InventoryItemBase(); + item.Owner = remoteClient.AgentId; + item.Creator = remoteClient.AgentId; + item.ID = UUID.Random(); + item.AssetID = asset.Metadata.FullID; + item.Description = asset.Metadata.Description; + item.Name = name; + item.Flags = flags; + item.AssetType = asset.Metadata.Type; + item.InvType = invType; + item.Folder = folderID; + item.CurrentPermissions = currentMask; + item.NextPermissions = nextOwnerMask; + item.EveryOnePermissions = everyoneMask; + item.GroupPermissions = groupMask; + item.BasePermissions = baseMask; + item.CreationDate = creationDate; + + userInfo.AddItem(item); + remoteClient.SendInventoryItemCreateUpdate(item); + } + else + { + m_log.WarnFormat( + "No user details associated with client {0} uuid {1} in CreateNewInventoryItem!", + remoteClient.Name, remoteClient.AgentId); + } + } + + /// + /// Create a new inventory item. Called when the client creates a new item directly within their + /// inventory (e.g. by selecting a context inventory menu option). + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void CreateNewInventoryItem(IClientAPI remoteClient, UUID transactionID, UUID folderID, + uint callbackID, string description, string name, sbyte invType, + sbyte assetType, + byte wearableType, uint nextOwnerMask, int creationDate) + { + m_log.DebugFormat("[AGENT INVENTORY]: Received request to create inventory item {0} in folder {1}", name, folderID); + + if (!Permissions.CanCreateUserInventory(invType, remoteClient.AgentId)) + return; + + if (transactionID == UUID.Zero) + { + CachedUserInfo userInfo + = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (userInfo != null) + { + ScenePresence presence; + TryGetAvatar(remoteClient.AgentId, out presence); + byte[] data = null; + + if (invType == 3 && presence != null) // OpenMetaverse.asset.assettype.landmark = 3 - needs to be turned into an enum + { + Vector3 pos = presence.AbsolutePosition; + string strdata = String.Format( + "Landmark version 2\nregion_id {0}\nlocal_pos {1} {2} {3}\nregion_handle {4}\n", + presence.Scene.RegionInfo.RegionID, + pos.X, pos.Y, pos.Z, + presence.RegionHandle); + data = Encoding.ASCII.GetBytes(strdata); + } + + AssetBase asset = CreateAsset(name, description, assetType, data); + AssetCache.AddAsset(asset); + + CreateNewInventoryItem(remoteClient, folderID, asset.Metadata.Name, 0, callbackID, asset, invType, nextOwnerMask, creationDate); + } + else + { + m_log.ErrorFormat( + "userInfo for agent uuid {0} unexpectedly null in CreateNewInventoryItem", + remoteClient.AgentId); + } + } + else + { + IAgentAssetTransactions agentTransactions = this.RequestModuleInterface(); + if (agentTransactions != null) + { + agentTransactions.HandleItemCreationFromTransaction( + remoteClient, transactionID, folderID, callbackID, description, + name, invType, assetType, wearableType, nextOwnerMask); + } + } + } + + /// + /// Remove an inventory item for the client's inventory + /// + /// + /// + private void RemoveInventoryItem(IClientAPI remoteClient, UUID itemID) + { + CachedUserInfo userInfo + = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (userInfo == null) + { + m_log.WarnFormat( + "[AGENT INVENTORY]: Failed to find user {0} {1} to delete inventory item {2}", + remoteClient.Name, remoteClient.AgentId, itemID); + + return; + } + + userInfo.DeleteItem(itemID); + } + + /// + /// Removes an inventory folder. Although there is a packet in the Linden protocol for this, it may be + /// legacy and not currently used (purge folder is used to remove folders from trash instead). + /// + /// + /// + private void RemoveInventoryFolder(IClientAPI remoteClient, UUID folderID) + { + CachedUserInfo userInfo + = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (userInfo == null) + { + m_log.Warn("[AGENT INVENTORY]: Failed to find user " + remoteClient.AgentId.ToString()); + return; + } + + if (userInfo.RootFolder != null) + { + InventoryItemBase folder = userInfo.RootFolder.FindItem(folderID); + + if (folder != null) + { + m_log.WarnFormat( + "[AGENT INVENTORY]: Remove folder not implemented in request by {0} {1} for {2}", + remoteClient.Name, remoteClient.AgentId, folderID); + + // doesn't work just yet, commented out. will fix in next patch. + // userInfo.DeleteItem(folder); + } + } + } + + private SceneObjectGroup GetGroupByPrim(uint localID) + { + List EntityList = GetEntities(); + + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + if (((SceneObjectGroup) ent).HasChildPrim(localID)) + return (SceneObjectGroup) ent; + } + } + return null; + } + + /// + /// Send the details of a prim's inventory to the client. + /// + /// + /// + public void RequestTaskInventory(IClientAPI remoteClient, uint primLocalID) + { + SceneObjectGroup group = GetGroupByPrim(primLocalID); + if (group != null) + { + bool fileChange = group.GetPartInventoryFileName(remoteClient, primLocalID); + if (fileChange) + { + if (XferManager != null) + { + group.RequestInventoryFile(remoteClient, primLocalID, XferManager); + } + } + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: Inventory requested of prim {0} which doesn't exist", primLocalID); + } + } + + /// + /// Remove an item from a prim (task) inventory + /// + /// Unused at the moment but retained since the avatar ID might + /// be necessary for a permissions check at some stage. + /// + /// + public void RemoveTaskInventory(IClientAPI remoteClient, UUID itemID, uint localID) + { + SceneObjectPart part = GetSceneObjectPart(localID); + SceneObjectGroup group = part.ParentGroup; + if (group != null) + { + TaskInventoryItem item = group.GetInventoryItem(localID, itemID); + if (item == null) + return; + + if (item.Type == 10) + { + EventManager.TriggerRemoveScript(localID, itemID); + } + group.RemoveInventoryItem(localID, itemID); + part.GetProperties(remoteClient); + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Removal of item {0} requested of prim {1} but this prim does not exist", + itemID, + localID); + } + } + + private InventoryItemBase CreateAgentInventoryItemFromTask(UUID destAgent, SceneObjectPart part, UUID itemId) + { + TaskInventoryItem taskItem = part.Inventory.GetInventoryItem(itemId); + + if (null == taskItem) + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: Tried to retrieve item ID {0} from prim {1}, {2} for creating an avatar" + + " inventory item from a prim's inventory item " + + " but the required item does not exist in the prim's inventory", + itemId, part.Name, part.UUID); + + return null; + } + + if ((destAgent != taskItem.OwnerID) && ((taskItem.CurrentPermissions & (uint)PermissionMask.Transfer) == 0)) + { + return null; + } + + InventoryItemBase agentItem = new InventoryItemBase(); + + agentItem.ID = UUID.Random(); + agentItem.Creator = taskItem.CreatorID; + agentItem.Owner = destAgent; + agentItem.AssetID = taskItem.AssetID; + agentItem.Description = taskItem.Description; + agentItem.Name = taskItem.Name; + agentItem.AssetType = taskItem.Type; + agentItem.InvType = taskItem.InvType; + agentItem.Flags = taskItem.Flags; + + if ((part.OwnerID != destAgent) && Permissions.PropagatePermissions()) + { + if (taskItem.InvType == 6) + agentItem.BasePermissions = taskItem.BasePermissions & ((taskItem.CurrentPermissions & 7) << 13); + else + agentItem.BasePermissions = taskItem.BasePermissions; + agentItem.BasePermissions &= taskItem.NextPermissions; + agentItem.CurrentPermissions = agentItem.BasePermissions | 8; + agentItem.NextPermissions = taskItem.NextPermissions; + agentItem.EveryOnePermissions = taskItem.EveryonePermissions & taskItem.NextPermissions; + agentItem.GroupPermissions = taskItem.GroupPermissions & taskItem.NextPermissions; + } + else + { + agentItem.BasePermissions = taskItem.BasePermissions; + agentItem.CurrentPermissions = taskItem.CurrentPermissions; + agentItem.NextPermissions = taskItem.NextPermissions; + agentItem.EveryOnePermissions = taskItem.EveryonePermissions; + agentItem.GroupPermissions = taskItem.GroupPermissions; + } + + if (!Permissions.BypassPermissions()) + { + if ((taskItem.CurrentPermissions & (uint)PermissionMask.Copy) == 0) + part.Inventory.RemoveInventoryItem(itemId); + } + + return agentItem; + } + + /// + /// Move the given item in the given prim to a folder in the client's inventory + /// + /// + /// + /// + /// + public InventoryItemBase MoveTaskInventoryItem(IClientAPI remoteClient, UUID folderId, SceneObjectPart part, UUID itemId) + { + InventoryItemBase agentItem = CreateAgentInventoryItemFromTask(remoteClient.AgentId, part, itemId); + + if (agentItem == null) + return null; + + agentItem.Folder = folderId; + AddInventoryItem(remoteClient, agentItem); + return agentItem; + } + + /// + /// ClientMoveTaskInventoryItem + /// + /// + /// + /// + /// + public void ClientMoveTaskInventoryItem(IClientAPI remoteClient, UUID folderId, uint primLocalId, UUID itemId) + { + SceneObjectPart part = GetSceneObjectPart(primLocalId); + + if (null == part) + { + m_log.WarnFormat( + "[PRIM INVENTORY]: " + + "Move of inventory item {0} from prim with local id {1} failed because the prim could not be found", + itemId, primLocalId); + + return; + } + + TaskInventoryItem taskItem = part.Inventory.GetInventoryItem(itemId); + + if (null == taskItem) + { + m_log.WarnFormat("[PRIM INVENTORY]: Move of inventory item {0} from prim with local id {1} failed" + + " because the inventory item could not be found", + itemId, primLocalId); + + return; + } + + // Only owner can copy + if (remoteClient.AgentId != taskItem.OwnerID) + return; + + MoveTaskInventoryItem(remoteClient, folderId, part, itemId); + } + + /// + /// MoveTaskInventoryItem + /// + /// + /// + /// + /// + public InventoryItemBase MoveTaskInventoryItem(UUID avatarId, UUID folderId, SceneObjectPart part, UUID itemId) + { + ScenePresence avatar; + + if (TryGetAvatar(avatarId, out avatar)) + { + return MoveTaskInventoryItem(avatar.ControllingClient, folderId, part, itemId); + } + else + { + CachedUserInfo profile = CommsManager.UserProfileCacheService.GetUserDetails(avatarId); + if (profile == null || profile.RootFolder == null) + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Avatar {0} cannot be found to add item", + avatarId); + } + if (!profile.HasReceivedInventory) + CommsManager.UserProfileCacheService.RequestInventoryForUser(avatarId); + InventoryItemBase agentItem = CreateAgentInventoryItemFromTask(avatarId, part, itemId); + + if (agentItem == null) + return null; + + agentItem.Folder = folderId; + + AddInventoryItem(avatarId, agentItem); + + return agentItem; + } + } + + /// + /// Copy a task (prim) inventory item to another task (prim) + /// + /// + /// + /// + public void MoveTaskInventoryItem(UUID destId, SceneObjectPart part, UUID itemId) + { + TaskInventoryItem srcTaskItem = part.Inventory.GetInventoryItem(itemId); + + if (srcTaskItem == null) + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: Tried to retrieve item ID {0} from prim {1}, {2} for moving" + + " but the item does not exist in this inventory", + itemId, part.Name, part.UUID); + + return; + } + + SceneObjectPart destPart = GetSceneObjectPart(destId); + + if (destPart == null) + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Could not find prim for ID {0}", + destId); + return; + } + + // Can't transfer this + // + if ((part.OwnerID != destPart.OwnerID) && ((srcTaskItem.CurrentPermissions & (uint)PermissionMask.Transfer) == 0)) + return; + + if (part.OwnerID != destPart.OwnerID && (part.GetEffectiveObjectFlags() & (uint)PrimFlags.AllowInventoryDrop) == 0) + { + // object cannot copy items to an object owned by a different owner + // unless llAllowInventoryDrop has been called + + return; + } + + // must have both move and modify permission to put an item in an object + if ((part.OwnerMask & ((uint)PermissionMask.Move | (uint)PermissionMask.Modify)) == 0) + { + return; + } + + TaskInventoryItem destTaskItem = new TaskInventoryItem(); + + destTaskItem.ItemID = UUID.Random(); + destTaskItem.CreatorID = srcTaskItem.CreatorID; + destTaskItem.AssetID = srcTaskItem.AssetID; + destTaskItem.GroupID = destPart.GroupID; + destTaskItem.OwnerID = destPart.OwnerID; + destTaskItem.ParentID = destPart.UUID; + destTaskItem.ParentPartID = destPart.UUID; + + destTaskItem.BasePermissions = srcTaskItem.BasePermissions; + destTaskItem.EveryonePermissions = srcTaskItem.EveryonePermissions; + destTaskItem.GroupPermissions = srcTaskItem.GroupPermissions; + destTaskItem.CurrentPermissions = srcTaskItem.CurrentPermissions; + destTaskItem.NextPermissions = srcTaskItem.NextPermissions; + destTaskItem.Flags = srcTaskItem.Flags; + + if (destPart.OwnerID != part.OwnerID) + { + if (Permissions.PropagatePermissions()) + { + destTaskItem.CurrentPermissions = srcTaskItem.CurrentPermissions & + srcTaskItem.NextPermissions; + destTaskItem.GroupPermissions = srcTaskItem.GroupPermissions & + srcTaskItem.NextPermissions; + destTaskItem.EveryonePermissions = srcTaskItem.EveryonePermissions & + srcTaskItem.NextPermissions; + destTaskItem.BasePermissions = srcTaskItem.BasePermissions & + srcTaskItem.NextPermissions; + destTaskItem.CurrentPermissions |= 8; // Slam! + } + } + + destTaskItem.Description = srcTaskItem.Description; + destTaskItem.Name = srcTaskItem.Name; + destTaskItem.InvType = srcTaskItem.InvType; + destTaskItem.Type = srcTaskItem.Type; + + destPart.Inventory.AddInventoryItem(destTaskItem, part.OwnerID != destPart.OwnerID); + + if ((srcTaskItem.CurrentPermissions & (uint)PermissionMask.Copy) == 0) + part.Inventory.RemoveInventoryItem(itemId); + + ScenePresence avatar; + + if (TryGetAvatar(srcTaskItem.OwnerID, out avatar)) + { + destPart.GetProperties(avatar.ControllingClient); + } + } + + public UUID MoveTaskInventoryItems(UUID destID, string category, SceneObjectPart host, List items) + { + CachedUserInfo profile = CommsManager.UserProfileCacheService.GetUserDetails(destID); + if (profile == null || profile.RootFolder == null) + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Avatar {0} cannot be found to add items", + destID); + return UUID.Zero; + } + + UUID newFolderID = UUID.Random(); + + profile.CreateFolder(category, newFolderID, 0xffff, profile.RootFolder.ID); + + foreach (UUID itemID in items) + { + InventoryItemBase agentItem = CreateAgentInventoryItemFromTask(destID, host, itemID); + + if (agentItem != null) + { + agentItem.Folder = newFolderID; + + AddInventoryItem(destID, agentItem); + } + } + + ScenePresence avatar; + + if (TryGetAvatar(destID, out avatar)) + { + profile.SendInventoryDecendents(avatar.ControllingClient, + profile.RootFolder.ID, true, false); + profile.SendInventoryDecendents(avatar.ControllingClient, + newFolderID, false, true); + } + + return newFolderID; + } + + /// + /// Update an item in a prim (task) inventory. + /// This method does not handle scripts, RezScript(IClientAPI, UUID, unit) + /// + /// + /// + /// + /// + public void UpdateTaskInventory(IClientAPI remoteClient, UUID transactionID, TaskInventoryItem itemInfo, + uint primLocalID) + { + UUID itemID = itemInfo.ItemID; + + // Find the prim we're dealing with + SceneObjectPart part = GetSceneObjectPart(primLocalID); + + if (part != null) + { + TaskInventoryItem currentItem = part.Inventory.GetInventoryItem(itemID); + bool allowInventoryDrop = (part.GetEffectiveObjectFlags() + & (uint)PrimFlags.AllowInventoryDrop) != 0; + + // Explicity allow anyone to add to the inventory if the + // AllowInventoryDrop flag has been set. Don't however let + // them update an item unless they pass the external checks + // + if (!Permissions.CanEditObjectInventory(part.UUID, remoteClient.AgentId) + && (currentItem != null || !allowInventoryDrop)) + return; + + if (currentItem == null) + { + UUID copyID = UUID.Random(); + if (itemID != UUID.Zero) + { + CachedUserInfo userInfo = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (userInfo != null && userInfo.RootFolder != null) + { + InventoryItemBase item = userInfo.RootFolder.FindItem(itemID); + + // Try library + // XXX clumsy, possibly should be one call + if (null == item) + { + item = CommsManager.UserProfileCacheService.LibraryRoot.FindItem(itemID); + } + + if (item != null) + { + part.ParentGroup.AddInventoryItem(remoteClient, primLocalID, item, copyID); + m_log.InfoFormat( + "[PRIM INVENTORY]: Update with item {0} requested of prim {1} for {2}", + item.Name, primLocalID, remoteClient.Name); + part.GetProperties(remoteClient); + if (!Permissions.BypassPermissions()) + { + if ((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0) + RemoveInventoryItem(remoteClient, itemID); + } + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: Could not find inventory item {0} to update for {1}!", + itemID, remoteClient.Name); + } + } + } + } + else // Updating existing item with new perms etc + { + IAgentAssetTransactions agentTransactions = this.RequestModuleInterface(); + if (agentTransactions != null) + { + agentTransactions.HandleTaskItemUpdateFromTransaction( + remoteClient, part, transactionID, currentItem); + } + if (part.Inventory.UpdateInventoryItem(itemInfo)) + part.GetProperties(remoteClient); + } + } + else + { + m_log.WarnFormat( + "[PRIM INVENTORY]: " + + "Update with item {0} requested of prim {1} for {2} but this prim does not exist", + itemID, primLocalID, remoteClient.Name); + } + } + + /// + /// Rez a script into a prim's inventory, either ex nihilo or from an existing avatar inventory + /// + /// + /// + /// + public void RezScript(IClientAPI remoteClient, InventoryItemBase itemBase, UUID transactionID, uint localID) + { + UUID itemID = itemBase.ID; + UUID copyID = UUID.Random(); + + if (itemID != UUID.Zero) // transferred from an avatar inventory to the prim's inventory + { + CachedUserInfo userInfo = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (userInfo != null && userInfo.RootFolder != null) + { + InventoryItemBase item = userInfo.RootFolder.FindItem(itemID); + + // Try library + // XXX clumsy, possibly should be one call + if (null == item) + { + item = CommsManager.UserProfileCacheService.LibraryRoot.FindItem(itemID); + } + + if (item != null) + { + SceneObjectPart part = GetSceneObjectPart(localID); + if (part != null) + { + if (!Permissions.CanEditObjectInventory(part.UUID, remoteClient.AgentId)) + return; + + part.ParentGroup.AddInventoryItem(remoteClient, localID, item, copyID); + // TODO: switch to posting on_rez here when scripts + // have state in inventory + part.Inventory.CreateScriptInstance(copyID, 0, false, DefaultScriptEngine, 0); + + // m_log.InfoFormat("[PRIMINVENTORY]: " + + // "Rezzed script {0} into prim local ID {1} for user {2}", + // item.inventoryName, localID, remoteClient.Name); + part.GetProperties(remoteClient); + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Could not rez script {0} into prim local ID {1} for user {2}" + + " because the prim could not be found in the region!", + item.Name, localID, remoteClient.Name); + } + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: Could not find script inventory item {0} to rez for {1}!", + itemID, remoteClient.Name); + } + } + } + else // script has been rezzed directly into a prim's inventory + { + SceneObjectPart part = GetSceneObjectPart(itemBase.Folder); + if (part == null) + return; + + if (part.OwnerID != remoteClient.AgentId) + return; + + if ((part.OwnerMask & (uint)PermissionMask.Modify) == 0) + return; + + if (!Permissions.CanCreateObjectInventory( + itemBase.InvType, part.UUID, remoteClient.AgentId)) + return; + + AssetBase asset = CreateAsset(itemBase.Name, itemBase.Description, (sbyte)itemBase.AssetType, Encoding.ASCII.GetBytes("default\n{\n state_entry()\n {\n llSay(0, \"Script running\");\n }\n}")); + AssetCache.AddAsset(asset); + + TaskInventoryItem taskItem = new TaskInventoryItem(); + + taskItem.ResetIDs(itemBase.Folder); + taskItem.ParentID = itemBase.Folder; + taskItem.CreationDate = (uint)itemBase.CreationDate; + taskItem.Name = itemBase.Name; + taskItem.Description = itemBase.Description; + taskItem.Type = itemBase.AssetType; + taskItem.InvType = itemBase.InvType; + taskItem.OwnerID = itemBase.Owner; + taskItem.CreatorID = itemBase.Creator; + taskItem.BasePermissions = itemBase.BasePermissions; + taskItem.CurrentPermissions = itemBase.CurrentPermissions; + taskItem.EveryonePermissions = itemBase.EveryOnePermissions; + taskItem.GroupPermissions = itemBase.GroupPermissions; + taskItem.NextPermissions = itemBase.NextPermissions; + taskItem.GroupID = itemBase.GroupID; + taskItem.GroupPermissions = 0; + taskItem.Flags = itemBase.Flags; + taskItem.PermsGranter = UUID.Zero; + taskItem.PermsMask = 0; + taskItem.AssetID = asset.Metadata.FullID; + + part.Inventory.AddInventoryItem(taskItem, false); + part.GetProperties(remoteClient); + + part.Inventory.CreateScriptInstance(taskItem, 0, false, DefaultScriptEngine, 0); + } + } + + /// + /// Rez a script into a prim's inventory from another prim + /// + /// + /// + /// + public void RezScript(UUID srcId, SceneObjectPart srcPart, UUID destId, int pin, int running, int start_param) + { + TaskInventoryItem srcTaskItem = srcPart.Inventory.GetInventoryItem(srcId); + + if (srcTaskItem == null) + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: Tried to retrieve item ID {0} from prim {1}, {2} for rezzing a script but the " + + " item does not exist in this inventory", + srcId, srcPart.Name, srcPart.UUID); + + return; + } + + SceneObjectPart destPart = GetSceneObjectPart(destId); + + if (destPart == null) + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Could not find script for ID {0}", + destId); + return; + } + + // Must own the object, and have modify rights + if (srcPart.OwnerID != destPart.OwnerID) + return; + + if ((destPart.OwnerMask & (uint)PermissionMask.Modify) == 0) + return; + + if (destPart.ScriptAccessPin != pin) + { + m_log.WarnFormat( + "[PRIM INVENTORY]: " + + "Script in object {0} : {1}, attempted to load script {2} : {3} into object {4} : {5} with invalid pin {6}", + srcPart.Name, srcId, srcTaskItem.Name, srcTaskItem.ItemID, destPart.Name, destId, pin); + // the LSL Wiki says we are supposed to shout on the DEBUG_CHANNEL - + // "Object: Task Object trying to illegally load script onto task Other_Object!" + // How do we shout from in here? + return; + } + + TaskInventoryItem destTaskItem = new TaskInventoryItem(); + + destTaskItem.ItemID = UUID.Random(); + destTaskItem.CreatorID = srcTaskItem.CreatorID; + destTaskItem.AssetID = srcTaskItem.AssetID; + destTaskItem.GroupID = destPart.GroupID; + destTaskItem.OwnerID = destPart.OwnerID; + destTaskItem.ParentID = destPart.UUID; + destTaskItem.ParentPartID = destPart.UUID; + + destTaskItem.BasePermissions = srcTaskItem.BasePermissions; + destTaskItem.EveryonePermissions = srcTaskItem.EveryonePermissions; + destTaskItem.GroupPermissions = srcTaskItem.GroupPermissions; + destTaskItem.CurrentPermissions = srcTaskItem.CurrentPermissions; + destTaskItem.NextPermissions = srcTaskItem.NextPermissions; + destTaskItem.Flags = srcTaskItem.Flags; + + if (destPart.OwnerID != srcPart.OwnerID) + { + if (Permissions.PropagatePermissions()) + { + destTaskItem.CurrentPermissions = srcTaskItem.CurrentPermissions & + srcTaskItem.NextPermissions; + destTaskItem.GroupPermissions = srcTaskItem.GroupPermissions & + srcTaskItem.NextPermissions; + destTaskItem.EveryonePermissions = srcTaskItem.EveryonePermissions & + srcTaskItem.NextPermissions; + destTaskItem.BasePermissions = srcTaskItem.BasePermissions & + srcTaskItem.NextPermissions; + destTaskItem.CurrentPermissions |= 8; // Slam! + } + } + + destTaskItem.Description = srcTaskItem.Description; + destTaskItem.Name = srcTaskItem.Name; + destTaskItem.InvType = srcTaskItem.InvType; + destTaskItem.Type = srcTaskItem.Type; + + destPart.Inventory.AddInventoryItemExclusive(destTaskItem, false); + + if (running > 0) + { + destPart.Inventory.CreateScriptInstance(destTaskItem, 0, false, DefaultScriptEngine, 0); + } + + ScenePresence avatar; + + if (TryGetAvatar(srcTaskItem.OwnerID, out avatar)) + { + destPart.GetProperties(avatar.ControllingClient); + } + } + + /// + /// Called when an object is removed from the environment into inventory. + /// + /// + /// + /// + /// + /// + public virtual void DeRezObject(IClientAPI remoteClient, uint localID, + UUID groupID, DeRezAction action, UUID destinationID) + { + SceneObjectPart part = GetSceneObjectPart(localID); + if (part == null) + return; + + if (part.ParentGroup == null || part.ParentGroup.IsDeleted) + return; + + // Can't delete child prims + if (part != part.ParentGroup.RootPart) + return; + + SceneObjectGroup grp = part.ParentGroup; + + //force a database backup/update on this SceneObjectGroup + //So that we know the database is upto date, for when deleting the object from it + ForceSceneObjectBackup(grp); + + bool permissionToTake = false; + bool permissionToDelete = false; + + if (action == DeRezAction.SaveToExistingUserInventoryItem) + { + if (grp.OwnerID == remoteClient.AgentId && grp.RootPart.FromUserInventoryItemID != UUID.Zero) + { + permissionToTake = true; + permissionToDelete = false; + } + } + else if (action == DeRezAction.TakeCopy) + { + permissionToTake = + Permissions.CanTakeCopyObject( + grp.UUID, + remoteClient.AgentId); + } + else if (action == DeRezAction.GodTakeCopy) + { + permissionToTake = + Permissions.IsGod( + remoteClient.AgentId); + } + else if (action == DeRezAction.Take) + { + permissionToTake = + Permissions.CanTakeObject( + grp.UUID, + remoteClient.AgentId); + + //If they can take, they can delete! + permissionToDelete = permissionToTake; + } + else if (action == DeRezAction.Delete) + { + permissionToTake = + Permissions.CanDeleteObject( + grp.UUID, + remoteClient.AgentId); + permissionToDelete = permissionToTake; + } + else if (action == DeRezAction.Return) + { + if (remoteClient != null) + { + permissionToTake = + Permissions.CanReturnObject( + grp.UUID, + remoteClient.AgentId); + permissionToDelete = permissionToTake; + + if (permissionToDelete) + { + AddReturn(grp.OwnerID, grp.Name, grp.AbsolutePosition, "parcel owner return"); + } + } + else // Auto return passes through here with null agent + { + permissionToTake = true; + permissionToDelete = true; + } + } + else + { + m_log.DebugFormat( + "[AGENT INVENTORY]: Ignoring unexpected derez action {0} for {1}", action, remoteClient.Name); + return; + } + + if (permissionToTake) + { + m_asyncSceneObjectDeleter.DeleteToInventory( + action, destinationID, grp, remoteClient, + permissionToDelete); + } + else if (permissionToDelete) + { + DeleteSceneObject(grp, false); + } + } + + private bool WaitForInventory(CachedUserInfo info) + { + // 200 Seconds wait. This is called in the context of the + // background delete thread, so we can afford to waste time + // here. + // + int count = 200; + + while (count > 0) + { + System.Threading.Thread.Sleep(100); + count--; + if (info.HasReceivedInventory) + return true; + } + m_log.DebugFormat("Timed out waiting for inventory of user {0}", + info.UserProfile.ID.ToString()); + return false; + } + + /// + /// Delete a scene object from a scene and place in the given avatar's inventory. + /// Returns the UUID of the newly created asset. + /// + /// + /// + /// + /// + public virtual UUID DeleteToInventory(DeRezAction action, UUID folderID, + SceneObjectGroup objectGroup, IClientAPI remoteClient) + { + UUID assetID = UUID.Zero; + + string sceneObjectXml = objectGroup.ToXmlString(); + + // Get the user info of the item destination + // + CachedUserInfo userInfo; + + if (action == DeRezAction.Take || action == DeRezAction.TakeCopy || + action == DeRezAction.SaveToExistingUserInventoryItem) + { + // Take or take copy require a taker + // Saving changes requires a local user + // + if (remoteClient == null) + return UUID.Zero; + + userInfo = CommsManager.UserProfileCacheService.GetUserDetails( + remoteClient.AgentId); + } + else + { + // All returns / deletes go to the object owner + // + userInfo = CommsManager.UserProfileCacheService.GetUserDetails( + objectGroup.RootPart.OwnerID); + } + + if (userInfo == null) // Can't proceed + { + return UUID.Zero; + } + + if (!userInfo.HasReceivedInventory) + { + // Async inventory requests will queue, but they will never + // execute unless inventory is actually fetched + // + CommsManager.UserProfileCacheService.RequestInventoryForUser( + userInfo.UserProfile.ID); + } + + if (userInfo != null) + { + // If we're returning someone's item, it goes back to the + // owner's Lost And Found folder. + // Delete is treated like return in this case + // Deleting your own items makes them go to trash + // + + InventoryFolderBase folder = null; + InventoryItemBase item = null; + + if (DeRezAction.SaveToExistingUserInventoryItem == action) + { + item = userInfo.RootFolder.FindItem( + objectGroup.RootPart.FromUserInventoryItemID); + + if (null == item) + { + m_log.DebugFormat( + "[AGENT INVENTORY]: Object {0} {1} scheduled for save to inventory has already been deleted.", + objectGroup.Name, objectGroup.UUID); + return UUID.Zero; + } + } + else + { + // Folder magic + // + if (action == DeRezAction.Delete) + { + // Deleting someone else's item + // + if (remoteClient == null || + objectGroup.OwnerID != remoteClient.AgentId) + { + // Folder skeleton may not be loaded and we + // have to wait for the inventory to find + // the destination folder + // + if (!WaitForInventory(userInfo)) + return UUID.Zero; + folder = userInfo.FindFolderForType( + (int)AssetType.LostAndFoundFolder); + } + else + { + // Assume inventory skeleton was loaded during login + // and all folders can be found + // + folder = userInfo.FindFolderForType( + (int)AssetType.TrashFolder); + } + } + else if (action == DeRezAction.Return) + { + // Wait if needed + // + if (!userInfo.HasReceivedInventory) + { + if (!WaitForInventory(userInfo)) + return UUID.Zero; + } + + // Dump to lost + found unconditionally + // + folder = userInfo.FindFolderForType( + (int)AssetType.LostAndFoundFolder); + } + + if (folderID == UUID.Zero && folder == null) + { + // Catch all. Use lost & found + // + if (!userInfo.HasReceivedInventory) + { + if (!WaitForInventory(userInfo)) + return UUID.Zero; + } + + folder = userInfo.FindFolderForType( + (int)AssetType.LostAndFoundFolder); + } + + if (folder == null) // None of the above + { + folder = userInfo.RootFolder.FindFolder(folderID); + + if (folder == null) // Nowhere to put it + { + return UUID.Zero; + } + } + + item = new InventoryItemBase(); + item.Creator = objectGroup.RootPart.CreatorID; + item.ID = UUID.Random(); + item.InvType = (int)InventoryType.Object; + item.Folder = folder.ID; + item.Owner = userInfo.UserProfile.ID; + + } + + AssetBase asset = CreateAsset( + objectGroup.GetPartName(objectGroup.RootPart.LocalId), + objectGroup.GetPartDescription(objectGroup.RootPart.LocalId), + (sbyte)AssetType.Object, + Utils.StringToBytes(sceneObjectXml)); + AssetCache.AddAsset(asset); + assetID = asset.Metadata.FullID; + + if (DeRezAction.SaveToExistingUserInventoryItem == action) + { + item.AssetID = asset.Metadata.FullID; + userInfo.UpdateItem(item); + } + else + { + item.AssetID = asset.Metadata.FullID; + + if (remoteClient != null && (remoteClient.AgentId != objectGroup.RootPart.OwnerID) && Permissions.PropagatePermissions()) + { + uint perms=objectGroup.GetEffectivePermissions(); + uint nextPerms=(perms & 7) << 13; + if ((nextPerms & (uint)PermissionMask.Copy) == 0) + perms &= ~(uint)PermissionMask.Copy; + if ((nextPerms & (uint)PermissionMask.Transfer) == 0) + perms &= ~(uint)PermissionMask.Transfer; + if ((nextPerms & (uint)PermissionMask.Modify) == 0) + perms &= ~(uint)PermissionMask.Modify; + + item.BasePermissions = perms & objectGroup.RootPart.NextOwnerMask; + item.CurrentPermissions = item.BasePermissions; + item.NextPermissions = objectGroup.RootPart.NextOwnerMask; + item.EveryOnePermissions = objectGroup.RootPart.EveryoneMask & objectGroup.RootPart.NextOwnerMask; + item.GroupPermissions = objectGroup.RootPart.GroupMask & objectGroup.RootPart.NextOwnerMask; + item.CurrentPermissions |= 8; // Slam! + } + else + { + item.BasePermissions = objectGroup.GetEffectivePermissions(); + item.CurrentPermissions = objectGroup.GetEffectivePermissions(); + item.NextPermissions = objectGroup.RootPart.NextOwnerMask; + item.EveryOnePermissions = objectGroup.RootPart.EveryoneMask; + item.GroupPermissions = objectGroup.RootPart.GroupMask; + + item.CurrentPermissions |= 8; // Slam! + } + + // TODO: add the new fields (Flags, Sale info, etc) + item.CreationDate = Util.UnixTimeSinceEpoch(); + item.Description = asset.Metadata.Description; + item.Name = asset.Metadata.Name; + item.AssetType = asset.Metadata.Type; + + userInfo.AddItem(item); + + if (remoteClient != null && item.Owner == remoteClient.AgentId) + { + remoteClient.SendInventoryItemCreateUpdate(item); + } + else + { + ScenePresence notifyUser = GetScenePresence(item.Owner); + if (notifyUser != null) + { + notifyUser.ControllingClient.SendInventoryItemCreateUpdate(item); + } + } + } + } + + return assetID; + } + + public void updateKnownAsset(IClientAPI remoteClient, SceneObjectGroup grp, UUID assetID, UUID agentID) + { + SceneObjectGroup objectGroup = grp; + if (objectGroup != null) + { + if (!grp.HasGroupChanged) + { + m_log.InfoFormat("[ATTACHMENT]: Save request for {0} which is unchanged", grp.UUID); + return; + } + + m_log.InfoFormat( + "[ATTACHMENT]: Updating asset for attachment {0}, attachpoint {1}", + grp.UUID, grp.GetAttachmentPoint()); + + string sceneObjectXml = objectGroup.ToXmlString(); + + CachedUserInfo userInfo = + CommsManager.UserProfileCacheService.GetUserDetails(agentID); + if (userInfo != null && userInfo.RootFolder != null) + { + Queue searchfolders = new Queue(); + searchfolders.Enqueue(userInfo.RootFolder); + + UUID foundFolder = UUID.Zero; + InventoryItemBase item = null; + + // search through folders to find the asset. + while (searchfolders.Count > 0) + { + InventoryFolderImpl fld = searchfolders.Dequeue(); + lock (fld) + { + if (fld != null) + { + if (fld.Items.ContainsKey(assetID)) + { + item = fld.Items[assetID]; + foundFolder = fld.ID; + searchfolders.Clear(); + break; + } + else + { + foreach (InventoryFolderImpl subfld in fld.RequestListOfFolderImpls()) + { + searchfolders.Enqueue(subfld); + } + } + } + } + } + + if (foundFolder != UUID.Zero && item != null) + { + AssetBase asset = CreateAsset( + objectGroup.GetPartName(objectGroup.LocalId), + objectGroup.GetPartDescription(objectGroup.LocalId), + (sbyte)AssetType.Object, + Utils.StringToBytes(sceneObjectXml)); + AssetCache.AddAsset(asset); + + item.AssetID = asset.Metadata.FullID; + item.Description = asset.Metadata.Description; + item.Name = asset.Metadata.Name; + item.AssetType = asset.Metadata.Type; + item.InvType = (int)InventoryType.Object; + item.Folder = foundFolder; + + userInfo.UpdateItem(item); + + // this gets called when the agent loggs off! + if (remoteClient != null) + { + remoteClient.SendInventoryItemCreateUpdate(item); + } + } + } + } + } + + public UUID attachObjectAssetStore(IClientAPI remoteClient, SceneObjectGroup grp, UUID AgentId, out UUID itemID) + { + itemID = UUID.Zero; + if (grp != null) + { + string sceneObjectXml = grp.ToXmlString(); + + CachedUserInfo userInfo = + CommsManager.UserProfileCacheService.GetUserDetails(AgentId); + if (userInfo != null) + { + AssetBase asset = CreateAsset( + grp.GetPartName(grp.LocalId), + grp.GetPartDescription(grp.LocalId), + (sbyte)AssetType.Object, + Utils.StringToBytes(sceneObjectXml)); + AssetCache.AddAsset(asset); + + InventoryItemBase item = new InventoryItemBase(); + item.Creator = grp.RootPart.CreatorID; + item.Owner = remoteClient.AgentId; + item.ID = UUID.Random(); + item.AssetID = asset.Metadata.FullID; + item.Description = asset.Metadata.Description; + item.Name = asset.Metadata.Name; + item.AssetType = asset.Metadata.Type; + item.InvType = (int)InventoryType.Object; + + item.Folder = UUID.Zero; // Objects folder! + + if ((remoteClient.AgentId != grp.RootPart.OwnerID) && Permissions.PropagatePermissions()) + { + item.BasePermissions = grp.RootPart.NextOwnerMask; + item.CurrentPermissions = grp.RootPart.NextOwnerMask; + item.NextPermissions = grp.RootPart.NextOwnerMask; + item.EveryOnePermissions = grp.RootPart.EveryoneMask & grp.RootPart.NextOwnerMask; + item.GroupPermissions = grp.RootPart.GroupMask & grp.RootPart.NextOwnerMask; + } + else + { + item.BasePermissions = grp.RootPart.BaseMask; + item.CurrentPermissions = grp.RootPart.OwnerMask; + item.NextPermissions = grp.RootPart.NextOwnerMask; + item.EveryOnePermissions = grp.RootPart.EveryoneMask; + item.GroupPermissions = grp.RootPart.GroupMask; + } + item.CreationDate = Util.UnixTimeSinceEpoch(); + + // sets assetID so client can show asset as 'attached' in inventory + grp.SetFromAssetID(item.ID); + + userInfo.AddItem(item); + remoteClient.SendInventoryItemCreateUpdate(item); + + itemID = item.ID; + return item.AssetID; + } + return UUID.Zero; + } + return UUID.Zero; + } + + /// + /// Event Handler Rez an object into a scene + /// Calls the non-void event handler + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public virtual void RezObject(IClientAPI remoteClient, UUID itemID, Vector3 RayEnd, Vector3 RayStart, + UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, + bool RezSelected, bool RemoveItem, UUID fromTaskID) + { + RezObject( + remoteClient, itemID, RayEnd, RayStart, RayTargetID, BypassRayCast, RayEndIsIntersection, + RezSelected, RemoveItem, fromTaskID, false); + } + + /// + /// Rez an object into the scene from the user's inventory + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// The SceneObjectGroup rezzed or null if rez was unsuccessful. + public virtual SceneObjectGroup RezObject(IClientAPI remoteClient, UUID itemID, Vector3 RayEnd, Vector3 RayStart, + UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, + bool RezSelected, bool RemoveItem, UUID fromTaskID, bool attachment) + { + // Work out position details + byte bRayEndIsIntersection = (byte)0; + + if (RayEndIsIntersection) + { + bRayEndIsIntersection = (byte)1; + } + else + { + bRayEndIsIntersection = (byte)0; + } + + Vector3 scale = new Vector3(0.5f, 0.5f, 0.5f); + + + Vector3 pos = GetNewRezLocation( + RayStart, RayEnd, RayTargetID, Quaternion.Identity, + BypassRayCast, bRayEndIsIntersection,true,scale, false); + + // Rez object + CachedUserInfo userInfo = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + if (userInfo != null) + { + // Do NOT use HasReceivedInventory here, this is called + // from within ItemReceive during login for attachments. + // Using HasReceivedInventory here will break attachment + // persistence! + // + if (userInfo.RootFolder != null) + { + InventoryItemBase item = userInfo.RootFolder.FindItem(itemID); + + if (item != null) + { + AssetBase rezAsset = AssetCache.GetAsset(item.AssetID, false); + + if (rezAsset != null) + { + UUID itemId = UUID.Zero; + + // If we have permission to copy then link the rezzed object back to the user inventory + // item that it came from. This allows us to enable 'save object to inventory' + if (!Permissions.BypassPermissions()) + { + if ((item.CurrentPermissions & (uint)PermissionMask.Copy) == (uint)PermissionMask.Copy) + { + itemId = item.ID; + } + } + + string xmlData = Utils.BytesToString(rezAsset.Data); + SceneObjectGroup group = new SceneObjectGroup(itemId, xmlData, true); + + if (!Permissions.CanRezObject( + group.Children.Count, remoteClient.AgentId, pos) + && !attachment) + { + return null; + } + + group.ResetIDs(); + + AddNewSceneObject(group, true); + + // if attachment we set it's asset id so object updates can reflect that + // if not, we set it's position in world. + if (!attachment) + { + pos = GetNewRezLocation( + RayStart, RayEnd, RayTargetID, Quaternion.Identity, + BypassRayCast, bRayEndIsIntersection, true, group.GroupScale(), false); + group.AbsolutePosition = pos; + } + else + { + group.SetFromAssetID(itemID); + } + + SceneObjectPart rootPart = null; + try + { + rootPart = group.GetChildPart(group.UUID); + } + catch (NullReferenceException) + { + string isAttachment = ""; + + if (attachment) + isAttachment = " Object was an attachment"; + + m_log.Error("[AGENT INVENTORY]: Error rezzing ItemID: " + itemID + " object has no rootpart." + isAttachment); + } + + // Since renaming the item in the inventory does not affect the name stored + // in the serialization, transfer the correct name from the inventory to the + // object itself before we rez. + rootPart.Name = item.Name; + rootPart.Description = item.Description; + + List partList = new List(group.Children.Values); + + group.SetGroup(remoteClient.ActiveGroupId, remoteClient); + if (rootPart.OwnerID != item.Owner) + { + //Need to kill the for sale here + rootPart.ObjectSaleType = 0; + rootPart.SalePrice = 10; + + if (Permissions.PropagatePermissions()) + { + if ((item.CurrentPermissions & 8) != 0) + { + foreach (SceneObjectPart part in partList) + { + part.EveryoneMask = item.EveryOnePermissions; + part.NextOwnerMask = item.NextPermissions; + part.GroupMask = 0; // DO NOT propagate here + } + } + group.ApplyNextOwnerPermissions(); + } + } + + foreach (SceneObjectPart part in partList) + { + if (part.OwnerID != item.Owner) + { + part.LastOwnerID = part.OwnerID; + part.OwnerID = item.Owner; + part.Inventory.ChangeInventoryOwner(item.Owner); + } + else if (((item.CurrentPermissions & 8) != 0) && (!attachment)) // Slam! + { + part.EveryoneMask = item.EveryOnePermissions; + part.NextOwnerMask = item.NextPermissions; + + part.GroupMask = 0; // DO NOT propagate here + } + } + + rootPart.TrimPermissions(); + + if (!attachment) + { + if (group.RootPart.Shape.PCode == (byte)PCode.Prim) + { + group.ClearPartAttachmentData(); + } + } + + if (!attachment) + { + // Fire on_rez + group.CreateScriptInstances(0, true, DefaultScriptEngine, 0); + + rootPart.ScheduleFullUpdate(); + } + + if (!Permissions.BypassPermissions()) + { + if ((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0) + { + // If this is done on attachments, no + // copy ones will be lost, so avoid it + // + if (!attachment) + userInfo.DeleteItem(item.ID); + } + } + + return rootPart.ParentGroup; + } + } + } + else + m_log.WarnFormat("[AGENT INVENTORY]: Root folder not found in {0}", RegionInfo.RegionName); + } + else + m_log.WarnFormat("[AGENT INVENTORY]: User profile not found in {0}", RegionInfo.RegionName); + + return null; + } + + /// + /// Rez an object into the scene from a prim's inventory. + /// + /// + /// + /// + /// + /// + /// + /// The SceneObjectGroup rezzed or null if rez was unsuccessful + public virtual SceneObjectGroup RezObject( + SceneObjectPart sourcePart, TaskInventoryItem item, + Vector3 pos, Quaternion rot, Vector3 vel, int param) + { + // Rez object + if (item != null) + { + UUID ownerID = item.OwnerID; + + AssetBase rezAsset = AssetCache.GetAsset(item.AssetID, false); + + if (rezAsset != null) + { + string xmlData = Utils.BytesToString(rezAsset.Data); + SceneObjectGroup group = new SceneObjectGroup(xmlData, true); + + if (!Permissions.CanRezObject(group.Children.Count, ownerID, pos)) + { + return null; + } + group.ResetIDs(); + + AddNewSceneObject(group, true); + + // we set it's position in world. + group.AbsolutePosition = pos; + + SceneObjectPart rootPart = group.GetChildPart(group.UUID); + + // Since renaming the item in the inventory does not affect the name stored + // in the serialization, transfer the correct name from the inventory to the + // object itself before we rez. + rootPart.Name = item.Name; + rootPart.Description = item.Description; + + List partList = new List(group.Children.Values); + + group.SetGroup(sourcePart.GroupID, null); + + if (rootPart.OwnerID != item.OwnerID) + { + if (Permissions.PropagatePermissions()) + { + if ((item.CurrentPermissions & 8) != 0) + { + foreach (SceneObjectPart part in partList) + { + part.EveryoneMask = item.EveryonePermissions; + part.NextOwnerMask = item.NextPermissions; + } + } + group.ApplyNextOwnerPermissions(); + } + } + + foreach (SceneObjectPart part in partList) + { + if (part.OwnerID != item.OwnerID) + { + part.LastOwnerID = part.OwnerID; + part.OwnerID = item.OwnerID; + part.Inventory.ChangeInventoryOwner(item.OwnerID); + } + else if ((item.CurrentPermissions & 8) != 0) // Slam! + { + part.EveryoneMask = item.EveryonePermissions; + part.NextOwnerMask = item.NextPermissions; + } + } + rootPart.TrimPermissions(); + if (group.RootPart.Shape.PCode == (byte)PCode.Prim) + { + group.ClearPartAttachmentData(); + } + group.UpdateGroupRotation(rot); + //group.ApplyPhysics(m_physicalPrim); + if (group.RootPart.PhysActor != null && group.RootPart.PhysActor.IsPhysical && vel != Vector3.Zero) + { + group.RootPart.ApplyImpulse((vel * group.GetMass()), false); + group.Velocity = vel; + rootPart.ScheduleFullUpdate(); + } + group.CreateScriptInstances(param, true, DefaultScriptEngine, 2); + rootPart.ScheduleFullUpdate(); + + if (!Permissions.BypassPermissions()) + { + if ((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0) + sourcePart.Inventory.RemoveInventoryItem(item.ItemID); + } + return rootPart.ParentGroup; + } + } + + return null; + } + + public virtual bool returnObjects(SceneObjectGroup[] returnobjects, UUID AgentId) + { + foreach (SceneObjectGroup grp in returnobjects) + { + AddReturn(grp.OwnerID, grp.Name, grp.AbsolutePosition, "parcel owner return"); + DeRezObject(null, grp.RootPart.LocalId, + grp.RootPart.GroupID, DeRezAction.Return, UUID.Zero); + } + + return true; + } + + public void SetScriptRunning(IClientAPI controllingClient, UUID objectID, UUID itemID, bool running) + { + SceneObjectPart part = GetSceneObjectPart(objectID); + if (part == null) + return; + + if (running) + EventManager.TriggerStartScript(part.LocalId, itemID); + else + EventManager.TriggerStopScript(part.LocalId, itemID); + } + + public UUID RezSingleAttachment(IClientAPI remoteClient, UUID itemID, + uint AttachmentPt) + { + SceneObjectGroup att = m_sceneGraph.RezSingleAttachment(remoteClient, itemID, AttachmentPt); + + if (att == null) + { + DetachSingleAttachmentToInv(itemID, remoteClient); + return UUID.Zero; + } + + return RezSingleAttachment(att, remoteClient, itemID, AttachmentPt); + } + + public UUID RezSingleAttachment(SceneObjectGroup att, + IClientAPI remoteClient, UUID itemID, uint AttachmentPt) + { + if (!att.IsDeleted) + AttachmentPt = att.RootPart.AttachmentPoint; + + ScenePresence presence; + if (TryGetAvatar(remoteClient.AgentId, out presence)) + { + presence.Appearance.SetAttachment((int)AttachmentPt, itemID, att.UUID); + IAvatarFactory ava = RequestModuleInterface(); + if (ava != null) + { + ava.UpdateDatabase(remoteClient.AgentId, presence.Appearance); + } + + } + return att.UUID; + } + + public void AttachObject(IClientAPI controllingClient, uint localID, uint attachPoint, Quaternion rot, Vector3 pos, bool silent) + { + m_sceneGraph.AttachObject(controllingClient, localID, attachPoint, rot, pos, silent); + } + + public void AttachObject(IClientAPI remoteClient, uint AttachmentPt, UUID itemID, SceneObjectGroup att) + { + if (UUID.Zero == itemID) + { + m_log.Error("[SCENE INVENTORY]: Unable to save attachment. Error inventory item ID."); + return; + } + + if (0 == AttachmentPt) + { + m_log.Error("[SCENE INVENTORY]: Unable to save attachment. Error attachment point."); + return; + } + + if (null == att.RootPart) + { + m_log.Error("[SCENE INVENTORY]: Unable to save attachment for a prim without the rootpart!"); + return; + } + + ScenePresence presence; + if (TryGetAvatar(remoteClient.AgentId, out presence)) + { + presence.Appearance.SetAttachment((int)AttachmentPt, itemID, att.UUID); + IAvatarFactory ava = RequestModuleInterface(); + if (ava != null) + { + m_log.InfoFormat("[SCENE INVENTORY]: Saving avatar attachment. AgentID:{0} ItemID:{1} AttachmentPoint:{2}", remoteClient.AgentId, itemID, AttachmentPt); + ava.UpdateDatabase(remoteClient.AgentId, presence.Appearance); + } + } + } + + public void DetachSingleAttachmentToGround(UUID itemID, IClientAPI remoteClient) + { + SceneObjectPart part = GetSceneObjectPart(itemID); + if (part == null || part.ParentGroup == null) + return; + + UUID inventoryID = part.ParentGroup.GetFromAssetID(); + + ScenePresence presence; + if (TryGetAvatar(remoteClient.AgentId, out presence)) + { + if (!Permissions.CanRezObject(part.ParentGroup.Children.Count, remoteClient.AgentId, presence.AbsolutePosition)) + return; + + presence.Appearance.DetachAttachment(itemID); + IAvatarFactory ava = RequestModuleInterface(); + if (ava != null) + { + ava.UpdateDatabase(remoteClient.AgentId, presence.Appearance); + } + part.ParentGroup.DetachToGround(); + CachedUserInfo userInfo = + CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + if (userInfo != null) + { + userInfo.DeleteItem(inventoryID); + remoteClient.SendRemoveInventoryItem(inventoryID); + } + } + } + + public void DetachSingleAttachmentToInv(UUID itemID, IClientAPI remoteClient) + { + ScenePresence presence; + if (TryGetAvatar(remoteClient.AgentId, out presence)) + { + presence.Appearance.DetachAttachment(itemID); + IAvatarFactory ava = RequestModuleInterface(); + if (ava != null) + { + ava.UpdateDatabase(remoteClient.AgentId, presence.Appearance); + } + + } + + m_sceneGraph.DetachSingleAttachmentToInv(itemID, remoteClient); + } + + public void GetScriptRunning(IClientAPI controllingClient, UUID objectID, UUID itemID) + { + EventManager.TriggerGetScriptRunning(controllingClient, objectID, itemID); + } + + void ObjectOwner(IClientAPI remoteClient, UUID ownerID, UUID groupID, List localIDs) + { + if (!Permissions.IsGod(remoteClient.AgentId)) + return; + + foreach (uint localID in localIDs) + { + SceneObjectPart part = GetSceneObjectPart(localID); + if (part != null && part.ParentGroup != null) + { + part.ParentGroup.SetOwnerId(ownerID); + part.Inventory.ChangeInventoryOwner(ownerID); + part.ParentGroup.SetGroup(groupID, remoteClient); + } + } + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs new file mode 100644 index 0000000..039b81b --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs @@ -0,0 +1,632 @@ +/* + * 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 OpenSim 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.Collections.Generic; +using System.Threading; +using OpenMetaverse; +using OpenMetaverse.Packets; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.Communications.Cache; + +namespace OpenSim.Region.Framework.Scenes +{ + public partial class Scene + { + protected void SimChat(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName, + UUID fromID, bool fromAgent, bool broadcast) + { + OSChatMessage args = new OSChatMessage(); + + args.Message = Utils.BytesToString(message); + args.Channel = channel; + args.Type = type; + args.Position = fromPos; + args.SenderUUID = fromID; + args.Scene = this; + + if (fromAgent) + { + ScenePresence user = GetScenePresence(fromID); + if (user != null) + args.Sender = user.ControllingClient; + } + else + { + SceneObjectPart obj = GetSceneObjectPart(fromID); + args.SenderObject = obj; + } + + args.From = fromName; + //args. + + if (broadcast) + EventManager.TriggerOnChatBroadcast(this, args); + else + EventManager.TriggerOnChatFromWorld(this, args); + + } + /// + /// + /// + /// + /// + /// + /// + /// + public void SimChat(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName, + UUID fromID, bool fromAgent) + { + SimChat(message, type, channel, fromPos, fromName, fromID, fromAgent, false); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public void SimChatBroadcast(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName, + UUID fromID, bool fromAgent) + { + SimChat(message, type, channel, fromPos, fromName, fromID, fromAgent, true); + } + + /// + /// Invoked when the client selects a prim. + /// + /// + /// + public void SelectPrim(uint primLocalID, IClientAPI remoteClient) + { + List EntityList = GetEntities(); + + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + if (((SceneObjectGroup) ent).LocalId == primLocalID) + { + ((SceneObjectGroup) ent).GetProperties(remoteClient); + ((SceneObjectGroup) ent).IsSelected = true; + // A prim is only tainted if it's allowed to be edited by the person clicking it. + if (Permissions.CanEditObject(((SceneObjectGroup)ent).UUID, remoteClient.AgentId) + || Permissions.CanMoveObject(((SceneObjectGroup)ent).UUID, remoteClient.AgentId)) + { + EventManager.TriggerParcelPrimCountTainted(); + } + break; + } + else + { + // We also need to check the children of this prim as they + // can be selected as well and send property information + bool foundPrim = false; + foreach (KeyValuePair child in ((SceneObjectGroup) ent).Children) + { + if (child.Value.LocalId == primLocalID) + { + child.Value.GetProperties(remoteClient); + foundPrim = true; + break; + } + } + if (foundPrim) break; + } + } + } + } + + /// + /// Handle the deselection of a prim from the client. + /// + /// + /// + public void DeselectPrim(uint primLocalID, IClientAPI remoteClient) + { + SceneObjectPart part = GetSceneObjectPart(primLocalID); + if (part == null) + return; + + // The prim is in the process of being deleted. + if (null == part.ParentGroup.RootPart) + return; + + // A deselect packet contains all the local prims being deselected. However, since selection is still + // group based we only want the root prim to trigger a full update - otherwise on objects with many prims + // we end up sending many duplicate ObjectUpdates + if (part.ParentGroup.RootPart.LocalId != part.LocalId) + return; + + bool isAttachment = false; + + // This is wrong, wrong, wrong. Selection should not be + // handled by group, but by prim. Legacy cruft. + // TODO: Make selection flagging per prim! + // + part.ParentGroup.IsSelected = false; + + if (part.ParentGroup.IsAttachment) + isAttachment = true; + else + part.ParentGroup.ScheduleGroupForFullUpdate(); + + // If it's not an attachment, and we are allowed to move it, + // then we might have done so. If we moved across a parcel + // boundary, we will need to recount prims on the parcels. + // For attachments, that makes no sense. + // + if (!isAttachment) + { + if (Permissions.CanEditObject( + part.UUID, remoteClient.AgentId) + || Permissions.CanMoveObject( + part.UUID, remoteClient.AgentId)) + EventManager.TriggerParcelPrimCountTainted(); + } + } + + public virtual void ProcessMoneyTransferRequest(UUID source, UUID destination, int amount, + int transactiontype, string description) + { + EventManager.MoneyTransferArgs args = new EventManager.MoneyTransferArgs(source, destination, amount, + transactiontype, description); + + EventManager.TriggerMoneyTransfer(this, args); + } + + public virtual void ProcessParcelBuy(UUID agentId, UUID groupId, bool final, bool groupOwned, + bool removeContribution, int parcelLocalID, int parcelArea, int parcelPrice, bool authenticated) + { + EventManager.LandBuyArgs args = new EventManager.LandBuyArgs(agentId, groupId, final, groupOwned, + removeContribution, parcelLocalID, parcelArea, + parcelPrice, authenticated); + + // First, allow all validators a stab at it + m_eventManager.TriggerValidateLandBuy(this, args); + + // Then, check validation and transfer + m_eventManager.TriggerLandBuy(this, args); + } + + public virtual void ProcessObjectGrab(uint localID, Vector3 offsetPos, IClientAPI remoteClient, List surfaceArgs) + { + List EntityList = GetEntities(); + + SurfaceTouchEventArgs surfaceArg = null; + if (surfaceArgs != null && surfaceArgs.Count > 0) + surfaceArg = surfaceArgs[0]; + + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + SceneObjectGroup obj = ent as SceneObjectGroup; + if (obj != null) + { + // Is this prim part of the group + if (obj.HasChildPrim(localID)) + { + // Currently only grab/touch for the single prim + // the client handles rez correctly + obj.ObjectGrabHandler(localID, offsetPos, remoteClient); + + SceneObjectPart part = obj.GetChildPart(localID); + + // If the touched prim handles touches, deliver it + // If not, deliver to root prim + if ((part.ScriptEvents & scriptEvents.touch_start) != 0) + EventManager.TriggerObjectGrab(part.LocalId, 0, part.OffsetPosition, remoteClient, surfaceArg); + else + EventManager.TriggerObjectGrab(obj.RootPart.LocalId, part.LocalId, part.OffsetPosition, remoteClient, surfaceArg); + + return; + } + } + } + } + } + + public virtual void ProcessObjectDeGrab(uint localID, IClientAPI remoteClient) + { + List EntityList = GetEntities(); + + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + SceneObjectGroup obj = ent as SceneObjectGroup; + + // Is this prim part of the group + if (obj.HasChildPrim(localID)) + { + SceneObjectPart part=obj.GetChildPart(localID); + if (part != null) + { + // If the touched prim handles touches, deliver it + // If not, deliver to root prim + if ((part.ScriptEvents & scriptEvents.touch_end) != 0) + EventManager.TriggerObjectDeGrab(part.LocalId, 0, remoteClient); + else + EventManager.TriggerObjectDeGrab(obj.RootPart.LocalId, part.LocalId, remoteClient); + + return; + } + return; + } + } + } + } + + public void ProcessAvatarPickerRequest(IClientAPI client, UUID avatarID, UUID RequestID, string query) + { + //EventManager.TriggerAvatarPickerRequest(); + + List AvatarResponses = new List(); + AvatarResponses = m_sceneGridService.GenerateAgentPickerRequestResponse(RequestID, query); + + AvatarPickerReplyPacket replyPacket = (AvatarPickerReplyPacket) PacketPool.Instance.GetPacket(PacketType.AvatarPickerReply); + // TODO: don't create new blocks if recycling an old packet + + AvatarPickerReplyPacket.DataBlock[] searchData = + new AvatarPickerReplyPacket.DataBlock[AvatarResponses.Count]; + AvatarPickerReplyPacket.AgentDataBlock agentData = new AvatarPickerReplyPacket.AgentDataBlock(); + + agentData.AgentID = avatarID; + agentData.QueryID = RequestID; + replyPacket.AgentData = agentData; + //byte[] bytes = new byte[AvatarResponses.Count*32]; + + int i = 0; + foreach (AvatarPickerAvatar item in AvatarResponses) + { + UUID translatedIDtem = item.AvatarID; + searchData[i] = new AvatarPickerReplyPacket.DataBlock(); + searchData[i].AvatarID = translatedIDtem; + searchData[i].FirstName = Utils.StringToBytes((string) item.firstName); + searchData[i].LastName = Utils.StringToBytes((string) item.lastName); + i++; + } + if (AvatarResponses.Count == 0) + { + searchData = new AvatarPickerReplyPacket.DataBlock[0]; + } + replyPacket.Data = searchData; + + AvatarPickerReplyAgentDataArgs agent_data = new AvatarPickerReplyAgentDataArgs(); + agent_data.AgentID = replyPacket.AgentData.AgentID; + agent_data.QueryID = replyPacket.AgentData.QueryID; + + List data_args = new List(); + for (i = 0; i < replyPacket.Data.Length; i++) + { + AvatarPickerReplyDataArgs data_arg = new AvatarPickerReplyDataArgs(); + data_arg.AvatarID = replyPacket.Data[i].AvatarID; + data_arg.FirstName = replyPacket.Data[i].FirstName; + data_arg.LastName = replyPacket.Data[i].LastName; + data_args.Add(data_arg); + } + client.SendAvatarPickerReply(agent_data, data_args); + } + + public void ProcessScriptReset(IClientAPI remoteClient, UUID objectID, + UUID itemID) + { + SceneObjectPart part=GetSceneObjectPart(objectID); + if (part == null) + return; + + if (Permissions.CanResetScript(objectID, itemID, remoteClient.AgentId)) + { + EventManager.TriggerScriptReset(part.LocalId, itemID); + } + } + + /// + /// Handle a fetch inventory request from the client + /// + /// + /// + /// + public void HandleFetchInventory(IClientAPI remoteClient, UUID itemID, UUID ownerID) + { + if (ownerID == CommsManager.UserProfileCacheService.LibraryRoot.Owner) + { + //Console.WriteLine("request info for library item"); + return; + } + + CachedUserInfo userProfile = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (null == userProfile) + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Could not find user profile for {0} {1}", + remoteClient.Name, remoteClient.AgentId); + return; + } + + if (userProfile.HasReceivedInventory) + { + InventoryItemBase item = null; + if (userProfile.RootFolder == null) + m_log.ErrorFormat( + "[AGENT INVENTORY]: User {0} {1} does not have a root folder.", + remoteClient.Name, remoteClient.AgentId); + else + item = userProfile.RootFolder.FindItem(itemID); + + if (item != null) + { + remoteClient.SendInventoryItemDetails(ownerID, item); + } + } + } + + /// + /// Tell the client about the various child items and folders contained in the requested folder. + /// + /// + /// + /// + /// + /// + /// + public void HandleFetchInventoryDescendents(IClientAPI remoteClient, UUID folderID, UUID ownerID, + bool fetchFolders, bool fetchItems, int sortOrder) + { + // FIXME MAYBE: We're not handling sortOrder! + + // TODO: This code for looking in the folder for the library should be folded back into the + // CachedUserInfo so that this class doesn't have to know the details (and so that multiple libraries, etc. + // can be handled transparently). + InventoryFolderImpl fold = null; + if ((fold = CommsManager.UserProfileCacheService.LibraryRoot.FindFolder(folderID)) != null) + { + remoteClient.SendInventoryFolderDetails( + fold.Owner, folderID, fold.RequestListOfItems(), + fold.RequestListOfFolders(), fetchFolders, fetchItems); + return; + } + + CachedUserInfo userProfile = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (null == userProfile) + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Could not find user profile for {0} {1}", + remoteClient.Name, remoteClient.AgentId); + return; + } + + userProfile.SendInventoryDecendents(remoteClient, folderID, fetchFolders, fetchItems); + } + + /// + /// Handle the caps inventory descendents fetch. + /// + /// Since the folder structure is sent to the client on login, I believe we only need to handle items. + /// + /// + /// + /// + /// + /// + /// + /// null if the inventory look up failed + public List HandleFetchInventoryDescendentsCAPS(UUID agentID, UUID folderID, UUID ownerID, + bool fetchFolders, bool fetchItems, int sortOrder) + { +// m_log.DebugFormat( +// "[INVENTORY CACHE]: Fetching folders ({0}), items ({1}) from {2} for agent {3}", +// fetchFolders, fetchItems, folderID, agentID); + + // FIXME MAYBE: We're not handling sortOrder! + + // TODO: This code for looking in the folder for the library should be folded back into the + // CachedUserInfo so that this class doesn't have to know the details (and so that multiple libraries, etc. + // can be handled transparently). + InventoryFolderImpl fold; + if ((fold = CommsManager.UserProfileCacheService.LibraryRoot.FindFolder(folderID)) != null) + { + return fold.RequestListOfItems(); + } + + CachedUserInfo userProfile = CommsManager.UserProfileCacheService.GetUserDetails(agentID); + + if (null == userProfile) + { + m_log.ErrorFormat("[AGENT INVENTORY]: Could not find user profile for {0}", agentID); + return null; + } + + // XXX: When a client crosses into a scene, their entire inventory is fetched + // asynchronously. If the client makes a request before the inventory is received, we need + // to give the inventory a chance to come in. + // + // This is a crude way of dealing with that by retrying the lookup. It's not quite as bad + // in CAPS as doing this with the udp request, since here it won't hold up other packets. + // In fact, here we'll be generous and try for longer. + if (!userProfile.HasReceivedInventory) + { + int attempts = 0; + while (attempts++ < 30) + { + m_log.DebugFormat( + "[INVENTORY CACHE]: Poll number {0} for inventory items in folder {1} for user {2}", + attempts, folderID, agentID); + + Thread.Sleep(2000); + + if (userProfile.HasReceivedInventory) + { + break; + } + } + } + + if (userProfile.HasReceivedInventory) + { + if ((fold = userProfile.RootFolder.FindFolder(folderID)) != null) + { + return fold.RequestListOfItems(); + } + else + { + m_log.WarnFormat( + "[AGENT INVENTORY]: Could not find folder {0} requested by user {1}", + folderID, agentID); + return null; + } + } + else + { + m_log.ErrorFormat("[INVENTORY CACHE]: Could not find root folder for user {0}", agentID); + return null; + } + } + + /// + /// Handle an inventory folder creation request from the client. + /// + /// + /// + /// + /// + /// + public void HandleCreateInventoryFolder(IClientAPI remoteClient, UUID folderID, ushort folderType, + string folderName, UUID parentID) + { + CachedUserInfo userProfile = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (null == userProfile) + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Could not find user profile for {0} {1}", + remoteClient.Name, remoteClient.AgentId); + return; + } + + if (!userProfile.CreateFolder(folderName, folderID, folderType, parentID)) + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Failed to move create folder for user {0} {1}", + remoteClient.Name, remoteClient.AgentId); + } + } + + /// + /// Handle a client request to update the inventory folder + /// + /// + /// FIXME: We call add new inventory folder because in the data layer, we happen to use an SQL REPLACE + /// so this will work to rename an existing folder. Needless to say, to rely on this is very confusing, + /// and needs to be changed. + /// + /// + /// + /// + /// + /// + public void HandleUpdateInventoryFolder(IClientAPI remoteClient, UUID folderID, ushort type, string name, + UUID parentID) + { +// m_log.DebugFormat( +// "[AGENT INVENTORY]: Updating inventory folder {0} {1} for {2} {3}", folderID, name, remoteClient.Name, remoteClient.AgentId); + + CachedUserInfo userProfile = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (null == userProfile) + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Could not find user profile for {0} {1}", + remoteClient.Name, remoteClient.AgentId); + return; + } + + if (!userProfile.UpdateFolder(name, folderID, type, parentID)) + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Failed to update folder for user {0} {1}", + remoteClient.Name, remoteClient.AgentId); + } + } + + /// + /// Handle an inventory folder move request from the client. + /// + /// + /// + /// + public void HandleMoveInventoryFolder(IClientAPI remoteClient, UUID folderID, UUID parentID) + { + CachedUserInfo userProfile = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (null == userProfile) + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Could not find user profile for {0} {1}", + remoteClient.Name, remoteClient.AgentId); + return; + } + + if (!userProfile.MoveFolder(folderID, parentID)) + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Failed to move folder {0} to {1} for user {2}", + folderID, parentID, remoteClient.Name); + } + } + + /// + /// This should delete all the items and folders in the given directory. + /// + /// + /// + public void HandlePurgeInventoryDescendents(IClientAPI remoteClient, UUID folderID) + { + CachedUserInfo userProfile = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (null == userProfile) + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Could not find user profile for {0} {1}", + remoteClient.Name, remoteClient.AgentId); + return; + } + + if (!userProfile.PurgeFolder(folderID)) + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Failed to purge folder for user {0} {1}", + remoteClient.Name, remoteClient.AgentId); + } + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/Scene.Permissions.cs b/OpenSim/Region/Framework/Scenes/Scene.Permissions.cs new file mode 100644 index 0000000..6aa617f --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Scene.Permissions.cs @@ -0,0 +1,1334 @@ +/* + * 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 OpenSim 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; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.Framework.Scenes +{ + public class ScenePermissions + { + private Scene m_scene; + + public ScenePermissions(Scene scene) + { + m_scene = scene; + } + + #region Object Permission Checks + + public delegate uint GenerateClientFlagsHandler(UUID userID, UUID objectIDID); + private List GenerateClientFlagsCheckFunctions = new List(); + + public void AddGenerateClientFlagsHandler(GenerateClientFlagsHandler delegateFunc) + { + if (!GenerateClientFlagsCheckFunctions.Contains(delegateFunc)) + GenerateClientFlagsCheckFunctions.Add(delegateFunc); + } + + public void RemoveGenerateClientFlagsHandler(GenerateClientFlagsHandler delegateFunc) + { + if (GenerateClientFlagsCheckFunctions.Contains(delegateFunc)) + GenerateClientFlagsCheckFunctions.Remove(delegateFunc); + } + + public uint GenerateClientFlags(UUID userID, UUID objectID) + { + SceneObjectPart part=m_scene.GetSceneObjectPart(objectID); + + if (part == null) + return 0; + + // libomv will moan about PrimFlags.ObjectYouOfficer being + // obsolete... + #pragma warning disable 0612 + uint perms=part.GetEffectiveObjectFlags() | + (uint)PrimFlags.ObjectModify | + (uint)PrimFlags.ObjectCopy | + (uint)PrimFlags.ObjectMove | + (uint)PrimFlags.ObjectTransfer | + (uint)PrimFlags.ObjectYouOwner | + (uint)PrimFlags.ObjectAnyOwner | + (uint)PrimFlags.ObjectOwnerModify | + (uint)PrimFlags.ObjectYouOfficer; + #pragma warning restore 0612 + + foreach (GenerateClientFlagsHandler check in GenerateClientFlagsCheckFunctions) + { + perms &= check(userID, objectID); + } + return perms; + } + + public delegate void SetBypassPermissionsHandler(bool value); + private List SetBypassPermissionsCheckFunctions = new List(); + + public void AddSetBypassPermissionsHandler(SetBypassPermissionsHandler delegateFunc) + { + if (!SetBypassPermissionsCheckFunctions.Contains(delegateFunc)) + SetBypassPermissionsCheckFunctions.Add(delegateFunc); + } + + public void RemoveSetBypassPermissionsHandler(SetBypassPermissionsHandler delegateFunc) + { + if (SetBypassPermissionsCheckFunctions.Contains(delegateFunc)) + SetBypassPermissionsCheckFunctions.Remove(delegateFunc); + } + + public void SetBypassPermissions(bool value) + { + foreach (SetBypassPermissionsHandler check in SetBypassPermissionsCheckFunctions) + { + check(value); + } + } + + public delegate bool BypassPermissionsHandler(); + private List BypassPermissionsCheckFunctions = new List(); + + public void AddBypassPermissionsHandler(BypassPermissionsHandler delegateFunc) + { + if (!BypassPermissionsCheckFunctions.Contains(delegateFunc)) + BypassPermissionsCheckFunctions.Add(delegateFunc); + } + + public void RemoveBypassPermissionsHandler(BypassPermissionsHandler delegateFunc) + { + if (BypassPermissionsCheckFunctions.Contains(delegateFunc)) + BypassPermissionsCheckFunctions.Remove(delegateFunc); + } + + public bool BypassPermissions() + { + foreach (BypassPermissionsHandler check in BypassPermissionsCheckFunctions) + { + if (check() == false) + { + return false; + } + } + return true; + } + + public delegate bool PropagatePermissionsHandler(); + private List PropagatePermissionsCheckFunctions = new List(); + + public void AddPropagatePermissionsHandler(PropagatePermissionsHandler delegateFunc) + { + if (!PropagatePermissionsCheckFunctions.Contains(delegateFunc)) + PropagatePermissionsCheckFunctions.Add(delegateFunc); + } + + public void RemovePropagatePermissionsHandler(PropagatePermissionsHandler delegateFunc) + { + if (PropagatePermissionsCheckFunctions.Contains(delegateFunc)) + PropagatePermissionsCheckFunctions.Remove(delegateFunc); + } + + public bool PropagatePermissions() + { + foreach (PropagatePermissionsHandler check in PropagatePermissionsCheckFunctions) + { + if (check() == false) + { + return false; + } + } + return true; + } + + #region REZ OBJECT + public delegate bool CanRezObjectHandler(int objectCount, UUID owner, Vector3 objectPosition, Scene scene); + private List CanRezObjectCheckFunctions = new List(); + + public void AddRezObjectHandler(CanRezObjectHandler delegateFunc) + { + if (!CanRezObjectCheckFunctions.Contains(delegateFunc)) + CanRezObjectCheckFunctions.Add(delegateFunc); + } + + public void RemoveRezObjectHandler(CanRezObjectHandler delegateFunc) + { + if (CanRezObjectCheckFunctions.Contains(delegateFunc)) + CanRezObjectCheckFunctions.Remove(delegateFunc); + } + + public bool CanRezObject(int objectCount, UUID owner, Vector3 objectPosition) + { + foreach (CanRezObjectHandler check in CanRezObjectCheckFunctions) + { + if (check(objectCount, owner,objectPosition, m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region DELETE OBJECT + public delegate bool CanDeleteObjectHandler(UUID objectID, UUID deleter, Scene scene); + private List CanDeleteObjectCheckFunctions = new List(); + + public void AddDeleteObjectHandler(CanDeleteObjectHandler delegateFunc) + { + if (!CanDeleteObjectCheckFunctions.Contains(delegateFunc)) + CanDeleteObjectCheckFunctions.Add(delegateFunc); + } + + public void RemoveDeleteObjectHandler(CanDeleteObjectHandler delegateFunc) + { + if (CanDeleteObjectCheckFunctions.Contains(delegateFunc)) + CanDeleteObjectCheckFunctions.Remove(delegateFunc); + } + + public bool CanDeleteObject(UUID objectID, UUID deleter) + { + foreach (CanDeleteObjectHandler check in CanDeleteObjectCheckFunctions) + { + if (check(objectID,deleter,m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region TAKE OBJECT + public delegate bool CanTakeObjectHandler(UUID objectID, UUID stealer, Scene scene); + private List CanTakeObjectCheckFunctions = new List(); + + public void AddTakeObjectHandler(CanTakeObjectHandler delegateFunc) + { + if (!CanTakeObjectCheckFunctions.Contains(delegateFunc)) + CanTakeObjectCheckFunctions.Add(delegateFunc); + } + + public void RemoveTakeObjectHandler(CanTakeObjectHandler delegateFunc) + { + if (CanTakeObjectCheckFunctions.Contains(delegateFunc)) + CanTakeObjectCheckFunctions.Remove(delegateFunc); + } + + public bool CanTakeObject(UUID objectID, UUID AvatarTakingUUID) + { + foreach (CanTakeObjectHandler check in CanTakeObjectCheckFunctions) + { + if (check(objectID, AvatarTakingUUID, m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region TAKE COPY OBJECT + public delegate bool CanTakeCopyObjectHandler(UUID objectID, UUID userID, Scene inScene); + private List CanTakeCopyObjectCheckFunctions = new List(); + + public void AddTakeCopyObjectHandler(CanTakeCopyObjectHandler delegateFunc) + { + if (!CanTakeCopyObjectCheckFunctions.Contains(delegateFunc)) + CanTakeCopyObjectCheckFunctions.Add(delegateFunc); + } + + public void RemoveTakeCopyObjectHandler(CanTakeCopyObjectHandler delegateFunc) + { + if (CanTakeCopyObjectCheckFunctions.Contains(delegateFunc)) + CanTakeCopyObjectCheckFunctions.Remove(delegateFunc); + } + + public bool CanTakeCopyObject(UUID objectID, UUID userID) + { + foreach (CanTakeCopyObjectHandler check in CanTakeCopyObjectCheckFunctions) + { + if (check(objectID,userID,m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region DUPLICATE OBJECT + public delegate bool CanDuplicateObjectHandler(int objectCount, UUID objectID, UUID owner, Scene scene, Vector3 objectPosition); + private List CanDuplicateObjectCheckFunctions = new List(); + + public void AddDuplicateObjectHandler(CanDuplicateObjectHandler delegateFunc) + { + if (!CanDuplicateObjectCheckFunctions.Contains(delegateFunc)) + CanDuplicateObjectCheckFunctions.Add(delegateFunc); + } + + public void RemoveDuplicateObjectHandler(CanDuplicateObjectHandler delegateFunc) + { + if (CanDuplicateObjectCheckFunctions.Contains(delegateFunc)) + CanDuplicateObjectCheckFunctions.Remove(delegateFunc); + } + + public bool CanDuplicateObject(int objectCount, UUID objectID, UUID owner, Vector3 objectPosition) + { + foreach (CanDuplicateObjectHandler check in CanDuplicateObjectCheckFunctions) + { + if (check(objectCount, objectID, owner, m_scene, objectPosition) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region EDIT OBJECT + public delegate bool CanEditObjectHandler(UUID objectID, UUID editorID, Scene scene); + private List CanEditObjectCheckFunctions = new List(); + + public void AddEditObjectHandler(CanEditObjectHandler delegateFunc) + { + if (!CanEditObjectCheckFunctions.Contains(delegateFunc)) + CanEditObjectCheckFunctions.Add(delegateFunc); + } + + public void RemoveEditObjectHandler(CanEditObjectHandler delegateFunc) + { + if (CanEditObjectCheckFunctions.Contains(delegateFunc)) + CanEditObjectCheckFunctions.Remove(delegateFunc); + } + + public bool CanEditObject(UUID objectID, UUID editorID) + { + foreach (CanEditObjectHandler check in CanEditObjectCheckFunctions) + { + if (check(objectID, editorID, m_scene) == false) + { + return false; + } + } + return true; + } + + public delegate bool CanEditObjectInventoryHandler(UUID objectID, UUID editorID, Scene scene); + private List CanEditObjectInventoryCheckFunctions = new List(); + + public void AddEditObjectInventoryHandler(CanEditObjectInventoryHandler delegateFunc) + { + if (!CanEditObjectInventoryCheckFunctions.Contains(delegateFunc)) + CanEditObjectInventoryCheckFunctions.Add(delegateFunc); + } + + public void RemoveEditObjectInventoryHandler(CanEditObjectInventoryHandler delegateFunc) + { + if (CanEditObjectInventoryCheckFunctions.Contains(delegateFunc)) + CanEditObjectInventoryCheckFunctions.Remove(delegateFunc); + } + + public bool CanEditObjectInventory(UUID objectID, UUID editorID) + { + foreach (CanEditObjectInventoryHandler check in CanEditObjectInventoryCheckFunctions) + { + if (check(objectID, editorID, m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region MOVE OBJECT + public delegate bool CanMoveObjectHandler(UUID objectID, UUID moverID, Scene scene); + private List CanMoveObjectCheckFunctions = new List(); + + public void AddMoveObjectHandler(CanMoveObjectHandler delegateFunc) + { + if (!CanMoveObjectCheckFunctions.Contains(delegateFunc)) + CanMoveObjectCheckFunctions.Add(delegateFunc); + } + + public void RemoveMoveObjectHandler(CanMoveObjectHandler delegateFunc) + { + if (CanMoveObjectCheckFunctions.Contains(delegateFunc)) + CanMoveObjectCheckFunctions.Remove(delegateFunc); + } + + public bool CanMoveObject(UUID objectID, UUID moverID) + { + foreach (CanMoveObjectHandler check in CanMoveObjectCheckFunctions) + { + if (check(objectID,moverID,m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region OBJECT ENTRY + public delegate bool CanObjectEntryHandler(UUID objectID, bool enteringRegion, Vector3 newPoint, Scene scene); + private List CanObjectEntryCheckFunctions = new List(); + + public void AddObjectEntryHandler(CanObjectEntryHandler delegateFunc) + { + if (!CanObjectEntryCheckFunctions.Contains(delegateFunc)) + CanObjectEntryCheckFunctions.Add(delegateFunc); + } + + public void RemoveObjectEntryHandler(CanObjectEntryHandler delegateFunc) + { + if (CanObjectEntryCheckFunctions.Contains(delegateFunc)) + CanObjectEntryCheckFunctions.Remove(delegateFunc); + } + + public bool CanObjectEntry(UUID objectID, bool enteringRegion, Vector3 newPoint) + { + foreach (CanObjectEntryHandler check in CanObjectEntryCheckFunctions) + { + if (check(objectID, enteringRegion, newPoint, m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region RETURN OBJECT + public delegate bool CanReturnObjectHandler(UUID objectID, UUID returnerID, Scene scene); + private List CanReturnObjectCheckFunctions = new List(); + + public void AddReturnObjectHandler(CanReturnObjectHandler delegateFunc) + { + if (!CanReturnObjectCheckFunctions.Contains(delegateFunc)) + CanReturnObjectCheckFunctions.Add(delegateFunc); + } + + public void RemoveReturnObjectHandler(CanReturnObjectHandler delegateFunc) + { + if (CanReturnObjectCheckFunctions.Contains(delegateFunc)) + CanReturnObjectCheckFunctions.Remove(delegateFunc); + } + + public bool CanReturnObject(UUID objectID, UUID returnerID) + { + foreach (CanReturnObjectHandler check in CanReturnObjectCheckFunctions) + { + if (check(objectID,returnerID,m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region INSTANT MESSAGE + public delegate bool CanInstantMessageHandler(UUID user, UUID target, Scene startScene); + private List CanInstantMessageCheckFunctions = new List(); + + public void AddInstantMessageHandler(CanInstantMessageHandler delegateFunc) + { + if (!CanInstantMessageCheckFunctions.Contains(delegateFunc)) + CanInstantMessageCheckFunctions.Add(delegateFunc); + } + + public void RemoveInstantMessageHandler(CanInstantMessageHandler delegateFunc) + { + if (CanInstantMessageCheckFunctions.Contains(delegateFunc)) + CanInstantMessageCheckFunctions.Remove(delegateFunc); + } + + public bool CanInstantMessage(UUID user, UUID target) + { + foreach (CanInstantMessageHandler check in CanInstantMessageCheckFunctions) + { + if (check(user, target, m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region INVENTORY TRANSFER + public delegate bool CanInventoryTransferHandler(UUID user, UUID target, Scene startScene); + private List CanInventoryTransferCheckFunctions = new List(); + + public void AddInventoryTransferHandler(CanInventoryTransferHandler delegateFunc) + { + if (!CanInventoryTransferCheckFunctions.Contains(delegateFunc)) + CanInventoryTransferCheckFunctions.Add(delegateFunc); + } + + public void RemoveInventoryTransferHandler(CanInventoryTransferHandler delegateFunc) + { + if (CanInventoryTransferCheckFunctions.Contains(delegateFunc)) + CanInventoryTransferCheckFunctions.Remove(delegateFunc); + } + + public bool CanInventoryTransfer(UUID user, UUID target) + { + foreach (CanInventoryTransferHandler check in CanInventoryTransferCheckFunctions) + { + if (check(user, target, m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region VIEW SCRIPT + public delegate bool CanViewScriptHandler(UUID script, UUID objectID, UUID user, Scene scene); + private List CanViewScriptCheckFunctions = new List(); + + public void AddViewScriptHandler(CanViewScriptHandler delegateFunc) + { + if (!CanViewScriptCheckFunctions.Contains(delegateFunc)) + CanViewScriptCheckFunctions.Add(delegateFunc); + } + + public void RemoveViewScriptHandler(CanViewScriptHandler delegateFunc) + { + if (CanViewScriptCheckFunctions.Contains(delegateFunc)) + CanViewScriptCheckFunctions.Remove(delegateFunc); + } + + public bool CanViewScript(UUID script, UUID objectID, UUID user) + { + foreach (CanViewScriptHandler check in CanViewScriptCheckFunctions) + { + if (check(script, objectID, user, m_scene) == false) + { + return false; + } + } + return true; + } + + public delegate bool CanViewNotecardHandler(UUID script, UUID objectID, UUID user, Scene scene); + private List CanViewNotecardCheckFunctions = new List(); + + public void AddViewNotecardHandler(CanViewNotecardHandler delegateFunc) + { + if (!CanViewNotecardCheckFunctions.Contains(delegateFunc)) + CanViewNotecardCheckFunctions.Add(delegateFunc); + } + + public void RemoveViewNotecardHandler(CanViewNotecardHandler delegateFunc) + { + if (CanViewNotecardCheckFunctions.Contains(delegateFunc)) + CanViewNotecardCheckFunctions.Remove(delegateFunc); + } + + public bool CanViewNotecard(UUID script, UUID objectID, UUID user) + { + foreach (CanViewNotecardHandler check in CanViewNotecardCheckFunctions) + { + if (check(script, objectID, user, m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region EDIT SCRIPT + public delegate bool CanEditScriptHandler(UUID script, UUID objectID, UUID user, Scene scene); + private List CanEditScriptCheckFunctions = new List(); + + public void AddEditScriptHandler(CanEditScriptHandler delegateFunc) + { + if (!CanEditScriptCheckFunctions.Contains(delegateFunc)) + CanEditScriptCheckFunctions.Add(delegateFunc); + } + + public void RemoveEditScriptHandler(CanEditScriptHandler delegateFunc) + { + if (CanEditScriptCheckFunctions.Contains(delegateFunc)) + CanEditScriptCheckFunctions.Remove(delegateFunc); + } + + public bool CanEditScript(UUID script, UUID objectID, UUID user) + { + foreach (CanEditScriptHandler check in CanEditScriptCheckFunctions) + { + if (check(script, objectID, user, m_scene) == false) + { + return false; + } + } + return true; + } + + public delegate bool CanEditNotecardHandler(UUID notecard, UUID objectID, UUID user, Scene scene); + private List CanEditNotecardCheckFunctions = new List(); + + public void AddEditNotecardHandler(CanEditNotecardHandler delegateFunc) + { + if (!CanEditNotecardCheckFunctions.Contains(delegateFunc)) + CanEditNotecardCheckFunctions.Add(delegateFunc); + } + + public void RemoveEditNotecardHandler(CanEditNotecardHandler delegateFunc) + { + if (CanEditNotecardCheckFunctions.Contains(delegateFunc)) + CanEditNotecardCheckFunctions.Remove(delegateFunc); + } + + public bool CanEditNotecard(UUID script, UUID objectID, UUID user) + { + foreach (CanEditNotecardHandler check in CanEditNotecardCheckFunctions) + { + if (check(script, objectID, user, m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region RUN SCRIPT (When Script Placed in Object) + public delegate bool CanRunScriptHandler(UUID script, UUID objectID, UUID user, Scene scene); + private List CanRunScriptCheckFunctions = new List(); + + public void AddRunScriptHandler(CanRunScriptHandler delegateFunc) + { + if (!CanRunScriptCheckFunctions.Contains(delegateFunc)) + CanRunScriptCheckFunctions.Add(delegateFunc); + } + + public void RemoveRunScriptHandler(CanRunScriptHandler delegateFunc) + { + if (CanRunScriptCheckFunctions.Contains(delegateFunc)) + CanRunScriptCheckFunctions.Remove(delegateFunc); + } + + public bool CanRunScript(UUID script, UUID objectID, UUID user) + { + foreach (CanRunScriptHandler check in CanRunScriptCheckFunctions) + { + if (check(script, objectID, user, m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region START SCRIPT (When Script run box is Checked after placed in object) + public delegate bool CanStartScriptHandler(UUID script, UUID user, Scene scene); + private List CanStartScriptCheckFunctions = new List(); + + public void AddStartScriptHandler(CanStartScriptHandler delegateFunc) + { + if (!CanStartScriptCheckFunctions.Contains(delegateFunc)) + CanStartScriptCheckFunctions.Add(delegateFunc); + } + + public void RemoveStartScriptHandler(CanStartScriptHandler delegateFunc) + { + if (CanStartScriptCheckFunctions.Contains(delegateFunc)) + CanStartScriptCheckFunctions.Remove(delegateFunc); + } + + public bool CanStartScript(UUID script, UUID user) + { + foreach (CanStartScriptHandler check in CanStartScriptCheckFunctions) + { + if (check(script, user, m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region STOP SCRIPT (When Script run box is unchecked after placed in object) + public delegate bool CanStopScriptHandler(UUID script, UUID user, Scene scene); + private List CanStopScriptCheckFunctions = new List(); + + public void AddStopScriptHandler(CanStopScriptHandler delegateFunc) + { + if (!CanStopScriptCheckFunctions.Contains(delegateFunc)) + CanStopScriptCheckFunctions.Add(delegateFunc); + } + + public void RemoveStopScriptHandler(CanStopScriptHandler delegateFunc) + { + if (CanStopScriptCheckFunctions.Contains(delegateFunc)) + CanStopScriptCheckFunctions.Remove(delegateFunc); + } + + public bool CanStopScript(UUID script, UUID user) + { + foreach (CanStopScriptHandler check in CanStopScriptCheckFunctions) + { + if (check(script, user, m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region RESET SCRIPT + public delegate bool CanResetScriptHandler(UUID prim, UUID script, UUID user, Scene scene); + private List CanResetScriptCheckFunctions = new List(); + + public void AddResetScriptHandler(CanResetScriptHandler delegateFunc) + { + if (!CanResetScriptCheckFunctions.Contains(delegateFunc)) + CanResetScriptCheckFunctions.Add(delegateFunc); + } + + public void RemoveResetScriptHandler(CanResetScriptHandler delegateFunc) + { + if (CanResetScriptCheckFunctions.Contains(delegateFunc)) + CanResetScriptCheckFunctions.Remove(delegateFunc); + } + + public bool CanResetScript(UUID prim, UUID script, UUID user) + { + foreach (CanResetScriptHandler check in CanResetScriptCheckFunctions) + { + if (check(prim, script, user, m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region TERRAFORM LAND + public delegate bool CanTerraformLandHandler(UUID user, Vector3 position, Scene requestFromScene); + private List CanTerraformLandCheckFunctions = new List(); + + public void AddTerraformLandHandler(CanTerraformLandHandler delegateFunc) + { + if (!CanTerraformLandCheckFunctions.Contains(delegateFunc)) + CanTerraformLandCheckFunctions.Add(delegateFunc); + } + + public void RemoveTerraformLandHandler(CanTerraformLandHandler delegateFunc) + { + if (CanTerraformLandCheckFunctions.Contains(delegateFunc)) + CanTerraformLandCheckFunctions.Remove(delegateFunc); + } + + public bool CanTerraformLand(UUID user, Vector3 pos) + { + foreach (CanTerraformLandHandler check in CanTerraformLandCheckFunctions) + { + if (check(user, pos, m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region RUN CONSOLE COMMAND + public delegate bool CanRunConsoleCommandHandler(UUID user, Scene requestFromScene); + private List CanRunConsoleCommandCheckFunctions = new List(); + + public void AddRunConsoleCommandHandler(CanRunConsoleCommandHandler delegateFunc) + { + if (!CanRunConsoleCommandCheckFunctions.Contains(delegateFunc)) + CanRunConsoleCommandCheckFunctions.Add(delegateFunc); + } + + public void RemoveRunConsoleCommandHandler(CanRunConsoleCommandHandler delegateFunc) + { + if (CanRunConsoleCommandCheckFunctions.Contains(delegateFunc)) + CanRunConsoleCommandCheckFunctions.Remove(delegateFunc); + } + + public bool CanRunConsoleCommand(UUID user) + { + foreach (CanRunConsoleCommandHandler check in CanRunConsoleCommandCheckFunctions) + { + if (check(user, m_scene) == false) + { + return false; + } + } + return true; + } + + #endregion + + #region CAN ISSUE ESTATE COMMAND + public delegate bool CanIssueEstateCommandHandler(UUID user, Scene requestFromScene, bool ownerCommand); + private List CanIssueEstateCommandCheckFunctions = new List(); + + public void AddIssueEstateCommandHandler(CanIssueEstateCommandHandler delegateFunc) + { + if (!CanIssueEstateCommandCheckFunctions.Contains(delegateFunc)) + CanIssueEstateCommandCheckFunctions.Add(delegateFunc); + } + + public void RemoveIssueEstateCommandHandler(CanIssueEstateCommandHandler delegateFunc) + { + if (CanIssueEstateCommandCheckFunctions.Contains(delegateFunc)) + CanIssueEstateCommandCheckFunctions.Remove(delegateFunc); + } + + public bool CanIssueEstateCommand(UUID user, bool ownerCommand) + { + foreach (CanIssueEstateCommandHandler check in CanIssueEstateCommandCheckFunctions) + { + if (check(user, m_scene, ownerCommand) == false) + { + return false; + } + } + return true; + } + #endregion + + #region CAN BE GODLIKE + public delegate bool IsGodHandler(UUID user, Scene requestFromScene); + private List IsGodCheckFunctions = new List(); + + public void AddIsGodHandler(IsGodHandler delegateFunc) + { + if (!IsGodCheckFunctions.Contains(delegateFunc)) + IsGodCheckFunctions.Add(delegateFunc); + } + + public void RemoveIsGodHandler(IsGodHandler delegateFunc) + { + if (IsGodCheckFunctions.Contains(delegateFunc)) + IsGodCheckFunctions.Remove(delegateFunc); + } + + public bool IsGod(UUID user) + { + foreach (IsGodHandler check in IsGodCheckFunctions) + { + if (check(user, m_scene) == false) + { + return false; + } + } + return true; + } + #endregion + + #region EDIT PARCEL + public delegate bool CanEditParcelHandler(UUID user, ILandObject parcel, Scene scene); + private List CanEditParcelCheckFunctions = new List(); + + public void AddEditParcelHandler(CanEditParcelHandler delegateFunc) + { + if (!CanEditParcelCheckFunctions.Contains(delegateFunc)) + CanEditParcelCheckFunctions.Add(delegateFunc); + } + + public void RemoveEditParcelHandler(CanEditParcelHandler delegateFunc) + { + if (CanEditParcelCheckFunctions.Contains(delegateFunc)) + CanEditParcelCheckFunctions.Remove(delegateFunc); + } + + public bool CanEditParcel(UUID user, ILandObject parcel) + { + foreach (CanEditParcelHandler check in CanEditParcelCheckFunctions) + { + if (check(user, parcel, m_scene) == false) + { + return false; + } + } + return true; + } + #endregion + + #region SELL PARCEL + public delegate bool CanSellParcelHandler(UUID user, ILandObject parcel, Scene scene); + private List CanSellParcelCheckFunctions = new List(); + + public void AddSellParcelHandler(CanSellParcelHandler delegateFunc) + { + if (!CanSellParcelCheckFunctions.Contains(delegateFunc)) + CanSellParcelCheckFunctions.Add(delegateFunc); + } + + public void RemoveSellParcelHandler(CanSellParcelHandler delegateFunc) + { + if (CanSellParcelCheckFunctions.Contains(delegateFunc)) + CanSellParcelCheckFunctions.Remove(delegateFunc); + } + + public bool CanSellParcel(UUID user, ILandObject parcel) + { + foreach (CanSellParcelHandler check in CanSellParcelCheckFunctions) + { + if (check(user, parcel, m_scene) == false) + { + return false; + } + } + return true; + } + #endregion + + #region ABANDON PARCEL + public delegate bool CanAbandonParcelHandler(UUID user, ILandObject parcel, Scene scene); + private List CanAbandonParcelCheckFunctions = new List(); + + public void AddAbandonParcelHandler(CanAbandonParcelHandler delegateFunc) + { + if (!CanAbandonParcelCheckFunctions.Contains(delegateFunc)) + CanAbandonParcelCheckFunctions.Add(delegateFunc); + } + + public void RemoveAbandonParcelHandler(CanAbandonParcelHandler delegateFunc) + { + if (CanAbandonParcelCheckFunctions.Contains(delegateFunc)) + CanAbandonParcelCheckFunctions.Remove(delegateFunc); + } + + public bool CanAbandonParcel(UUID user, ILandObject parcel) + { + foreach (CanAbandonParcelHandler check in CanAbandonParcelCheckFunctions) + { + if (check(user, parcel, m_scene) == false) + { + return false; + } + } + return true; + } + #endregion + + public delegate bool CanReclaimParcelHandler(UUID user, ILandObject parcel, Scene scene); + private List CanReclaimParcelCheckFunctions = new List(); + + public void AddReclaimParcelHandler(CanReclaimParcelHandler delegateFunc) + { + if (!CanReclaimParcelCheckFunctions.Contains(delegateFunc)) + CanReclaimParcelCheckFunctions.Add(delegateFunc); + } + + public void RemoveReclaimParcelHandler(CanReclaimParcelHandler delegateFunc) + { + if (CanReclaimParcelCheckFunctions.Contains(delegateFunc)) + CanReclaimParcelCheckFunctions.Remove(delegateFunc); + } + + public bool CanReclaimParcel(UUID user, ILandObject parcel) + { + foreach (CanReclaimParcelHandler check in CanReclaimParcelCheckFunctions) + { + if (check(user, parcel, m_scene) == false) + { + return false; + } + } + return true; + } + public delegate bool CanBuyLandHandler(UUID user, ILandObject parcel, Scene scene); + private List CanBuyLandCheckFunctions = new List(); + + public void AddCanBuyLandHandler(CanBuyLandHandler delegateFunc) + { + if (!CanBuyLandCheckFunctions.Contains(delegateFunc)) + CanBuyLandCheckFunctions.Add(delegateFunc); + } + + public void RemoveCanBuyLandHandler(CanBuyLandHandler delegateFunc) + { + if (CanBuyLandCheckFunctions.Contains(delegateFunc)) + CanBuyLandCheckFunctions.Remove(delegateFunc); + } + + public bool CanBuyLand(UUID user, ILandObject parcel) + { + foreach (CanBuyLandHandler check in CanBuyLandCheckFunctions) + { + if (check(user, parcel, m_scene) == false) + { + return false; + } + } + return true; + } + + public delegate bool CanLinkObjectHandler(UUID user, UUID objectID); + private List CanLinkObjectCheckFunctions = new List(); + + public void AddCanLinkObjectHandler(CanLinkObjectHandler delegateFunc) + { + if (!CanLinkObjectCheckFunctions.Contains(delegateFunc)) + CanLinkObjectCheckFunctions.Add(delegateFunc); + } + + public void RemoveCanLinkObjectHandler(CanLinkObjectHandler delegateFunc) + { + if (CanLinkObjectCheckFunctions.Contains(delegateFunc)) + CanLinkObjectCheckFunctions.Remove(delegateFunc); + } + + public bool CanLinkObject(UUID user, UUID objectID) + { + foreach (CanLinkObjectHandler check in CanLinkObjectCheckFunctions) + { + if (check(user, objectID) == false) + { + return false; + } + } + return true; + } + + public delegate bool CanDelinkObjectHandler(UUID user, UUID objectID); + private List CanDelinkObjectCheckFunctions = new List(); + + public void AddCanDelinkObjectHandler(CanDelinkObjectHandler delegateFunc) + { + if (!CanDelinkObjectCheckFunctions.Contains(delegateFunc)) + CanDelinkObjectCheckFunctions.Add(delegateFunc); + } + + public void RemoveCanDelinkObjectHandler(CanDelinkObjectHandler delegateFunc) + { + if (CanDelinkObjectCheckFunctions.Contains(delegateFunc)) + CanDelinkObjectCheckFunctions.Remove(delegateFunc); + } + + public bool CanDelinkObject(UUID user, UUID objectID) + { + foreach (CanDelinkObjectHandler check in CanDelinkObjectCheckFunctions) + { + if (check(user, objectID) == false) + { + return false; + } + } + return true; + } + + #endregion + + public delegate bool CanCreateObjectInventoryHandler(int invType, UUID objectID, UUID userID); + private List CanCreateObjectInventoryCheckFunctions + = new List(); + + + public void AddCanCreateObjectInventoryHandler(CanCreateObjectInventoryHandler delegateFunc) + { + if (!CanCreateObjectInventoryCheckFunctions.Contains(delegateFunc)) + CanCreateObjectInventoryCheckFunctions.Add(delegateFunc); + } + + public void RemoveCanCreateObjectInventoryHandler(CanCreateObjectInventoryHandler delegateFunc) + { + if (CanCreateObjectInventoryCheckFunctions.Contains(delegateFunc)) + CanCreateObjectInventoryCheckFunctions.Remove(delegateFunc); + } + + /// + /// Check whether the specified user is allowed to directly create the given inventory type in a prim's + /// inventory (e.g. the New Script button in the 1.21 Linden Lab client). + /// + /// + /// + /// + /// + public bool CanCreateObjectInventory(int invType, UUID objectID, UUID userID) + { + foreach (CanCreateObjectInventoryHandler check in CanCreateObjectInventoryCheckFunctions) + { + if (check(invType, objectID, userID) == false) + { + return false; + } + } + + return true; + } + + public delegate bool CanCopyObjectInventoryHandler(UUID itemID, UUID objectID, UUID userID); + private List CanCopyObjectInventoryCheckFunctions = new List(); + + public void AddCanCopyObjectInventoryHandler(CanCopyObjectInventoryHandler delegateFunc) + { + if (!CanCopyObjectInventoryCheckFunctions.Contains(delegateFunc)) + CanCopyObjectInventoryCheckFunctions.Add(delegateFunc); + } + + public void RemoveCanCopyObjectInventoryHandler(CanCopyObjectInventoryHandler delegateFunc) + { + if (CanCopyObjectInventoryCheckFunctions.Contains(delegateFunc)) + CanCopyObjectInventoryCheckFunctions.Remove(delegateFunc); + } + + public bool CanCopyObjectInventory(UUID itemID, UUID objectID, UUID userID) + { + foreach (CanCopyObjectInventoryHandler check in CanCopyObjectInventoryCheckFunctions) + { + if (check(itemID, objectID, userID) == false) + { + return false; + } + } + return true; + } + + public delegate bool CanDeleteObjectInventoryHandler(UUID itemID, UUID objectID, UUID userID); + private List CanDeleteObjectInventoryCheckFunctions + = new List(); + + public void AddCanDeleteObjectInventoryHandler(CanDeleteObjectInventoryHandler delegateFunc) + { + if (!CanDeleteObjectInventoryCheckFunctions.Contains(delegateFunc)) + CanDeleteObjectInventoryCheckFunctions.Add(delegateFunc); + } + + public void RemoveCanDeleteObjectInventoryHandler(CanDeleteObjectInventoryHandler delegateFunc) + { + if (CanDeleteObjectInventoryCheckFunctions.Contains(delegateFunc)) + CanDeleteObjectInventoryCheckFunctions.Remove(delegateFunc); + } + + public bool CanDeleteObjectInventory(UUID itemID, UUID objectID, UUID userID) + { + foreach (CanDeleteObjectInventoryHandler check in CanDeleteObjectInventoryCheckFunctions) + { + if (check(itemID, objectID, userID) == false) + { + return false; + } + } + + return true; + } + + public delegate bool CanCreateUserInventoryHandler(int invType, UUID userID); + private List CanCreateUserInventoryCheckFunctions + = new List(); + + public void AddCanCreateUserInventoryHandler(CanCreateUserInventoryHandler delegateFunc) + { + if (!CanCreateUserInventoryCheckFunctions.Contains(delegateFunc)) + CanCreateUserInventoryCheckFunctions.Add(delegateFunc); + } + + public void RemoveCanCreateUserInventoryHandler(CanCreateUserInventoryHandler delegateFunc) + { + if (CanCreateUserInventoryCheckFunctions.Contains(delegateFunc)) + CanCreateUserInventoryCheckFunctions.Remove(delegateFunc); + } + + /// + /// Check whether the specified user is allowed to create the given inventory type in their inventory. + /// + /// + /// + /// + public bool CanCreateUserInventory(int invType, UUID userID) + { + foreach (CanCreateUserInventoryHandler check in CanCreateUserInventoryCheckFunctions) + { + if (check(invType, userID) == false) + { + return false; + } + } + + return true; + } + + public delegate bool CanEditUserInventoryHandler(UUID itemID, UUID userID); + private List CanEditUserInventoryCheckFunctions + = new List(); + + public void AddCanEditUserInventoryHandler(CanEditUserInventoryHandler delegateFunc) + { + if (!CanEditUserInventoryCheckFunctions.Contains(delegateFunc)) + CanEditUserInventoryCheckFunctions.Add(delegateFunc); + } + + public void RemoveCanEditUserInventoryHandler(CanEditUserInventoryHandler delegateFunc) + { + if (CanEditUserInventoryCheckFunctions.Contains(delegateFunc)) + CanEditUserInventoryCheckFunctions.Remove(delegateFunc); + } + + /// + /// Check whether the specified user is allowed to edit the given inventory item within their own inventory. + /// + /// + /// + /// + public bool CanEditUserInventory(UUID itemID, UUID userID) + { + foreach (CanEditUserInventoryHandler check in CanEditUserInventoryCheckFunctions) + { + if (check(itemID, userID) == false) + { + return false; + } + } + + return true; + } + + public delegate bool CanCopyUserInventoryHandler(UUID itemID, UUID userID); + private List CanCopyUserInventoryCheckFunctions + = new List(); + + public void AddCanCopyUserInventoryHandler(CanCopyUserInventoryHandler delegateFunc) + { + if (!CanCopyUserInventoryCheckFunctions.Contains(delegateFunc)) + CanCopyUserInventoryCheckFunctions.Add(delegateFunc); + } + + public void RemoveCanCopyUserInventoryHandler(CanCopyUserInventoryHandler delegateFunc) + { + if (CanCopyUserInventoryCheckFunctions.Contains(delegateFunc)) + CanCopyUserInventoryCheckFunctions.Remove(delegateFunc); + } + + /// + /// Check whether the specified user is allowed to copy the given inventory item from their own inventory. + /// + /// + /// + /// + public bool CanCopyUserInventory(UUID itemID, UUID userID) + { + foreach (CanCopyUserInventoryHandler check in CanCopyUserInventoryCheckFunctions) + { + if (check(itemID, userID) == false) + { + return false; + } + } + + return true; + } + + public delegate bool CanDeleteUserInventoryHandler(UUID itemID, UUID userID); + private List CanDeleteUserInventoryCheckFunctions + = new List(); + + public void AddCanDeleteUserInventoryHandler(CanDeleteUserInventoryHandler delegateFunc) + { + if (!CanDeleteUserInventoryCheckFunctions.Contains(delegateFunc)) + CanDeleteUserInventoryCheckFunctions.Add(delegateFunc); + } + + public void RemoveCanDeleteUserInventoryHandler(CanDeleteUserInventoryHandler delegateFunc) + { + if (CanDeleteUserInventoryCheckFunctions.Contains(delegateFunc)) + CanDeleteUserInventoryCheckFunctions.Remove(delegateFunc); + } + + /// + /// Check whether the specified user is allowed to edit the given inventory item within their own inventory. + /// + /// + /// + /// + public bool CanDeleteUserInventory(UUID itemID, UUID userID) + { + foreach (CanDeleteUserInventoryHandler check in CanDeleteUserInventoryCheckFunctions) + { + if (check(itemID, userID) == false) + { + return false; + } + } + + return true; + } + + public delegate bool CanTeleportHandler(UUID userID); + private List CanTeleportCheckFunctions = new List(); + + public void AddCanTeleportHandler(CanTeleportHandler delegateFunc) + { + if (!CanTeleportCheckFunctions.Contains(delegateFunc)) + CanTeleportCheckFunctions.Add(delegateFunc); + } + + public void RemoveCanTeleportHandler(CanTeleportHandler delegateFunc) + { + if (CanTeleportCheckFunctions.Contains(delegateFunc)) + CanTeleportCheckFunctions.Remove(delegateFunc); + } + + public bool CanTeleport(UUID userID) + { + foreach (CanTeleportHandler check in CanTeleportCheckFunctions) + { + if (check(userID) == false) + { + return false; + } + } + return true; + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs new file mode 100644 index 0000000..f07391d --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -0,0 +1,4239 @@ +/* + * 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 OpenSim 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.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Xml; +using System.Threading; +using System.Timers; +using OpenMetaverse; +using OpenMetaverse.Imaging; +using OpenMetaverse.Packets; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Framework.Servers; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes.Scripting; +using OpenSim.Region.Physics.Manager; +using Nini.Config; +using Caps = OpenSim.Framework.Communications.Capabilities.Caps; +using Image = System.Drawing.Image; +using TPFlags = OpenSim.Framework.Constants.TeleportFlags; +using Timer = System.Timers.Timer; +using OSD = OpenMetaverse.StructuredData.OSD; + +namespace OpenSim.Region.Framework.Scenes +{ + public delegate bool FilterAvatarList(ScenePresence avatar); + + public partial class Scene : SceneBase + { + public delegate void SynchronizeSceneHandler(Scene scene); + public SynchronizeSceneHandler SynchronizeScene = null; + public int splitID = 0; + + private const long DEFAULT_MIN_TIME_FOR_PERSISTENCE = 60L; + private const long DEFAULT_MAX_TIME_FOR_PERSISTENCE = 600L; + + #region Fields + + protected Timer m_restartWaitTimer = new Timer(); + + public SimStatsReporter StatsReporter; + + protected List m_regionRestartNotifyList = new List(); + protected List m_neighbours = new List(); + + /// + /// The scene graph for this scene + /// + /// TODO: Possibly stop other classes being able to manipulate this directly. + public SceneGraph m_sceneGraph; + + /// + /// Are we applying physics to any of the prims in this scene? + /// + public bool m_physicalPrim; + public float m_maxNonphys = 65536; + public float m_maxPhys = 10; + public bool m_clampPrimSize = false; + public bool m_trustBinaries = false; + public bool m_allowScriptCrossings = false; + + public bool m_seeIntoRegionFromNeighbor; + public int MaxUndoCount = 5; + private int m_RestartTimerCounter; + private readonly Timer m_restartTimer = new Timer(15000); // Wait before firing + private int m_incrementsof15seconds = 0; + private volatile bool m_backingup = false; + + private Dictionary m_returns = new Dictionary(); + + protected string m_simulatorVersion = "OpenSimulator Server"; + + protected ModuleLoader m_moduleLoader; + protected StorageManager m_storageManager; + protected AgentCircuitManager m_authenticateHandler; + public CommunicationsManager CommsManager; + + protected SceneCommunicationService m_sceneGridService; + + public SceneCommunicationService SceneGridService + { + get { return m_sceneGridService; } + } + + public IXfer XferManager; + + protected IXMLRPC m_xmlrpcModule; + protected IWorldComm m_worldCommModule; + protected IAvatarFactory m_AvatarFactory; + protected IConfigSource m_config; + protected IRegionSerialiserModule m_serialiser; + protected IInterregionCommsOut m_interregionCommsOut; + protected IInterregionCommsIn m_interregionCommsIn; + protected IDialogModule m_dialogModule; + + protected ICapabilitiesModule m_capsModule; + public ICapabilitiesModule CapsModule + { + get { return m_capsModule; } + } + + // Central Update Loop + + protected int m_fps = 10; + protected int m_frame = 0; + protected float m_timespan = 0.089f; + protected DateTime m_lastupdate = DateTime.Now; + + protected float m_timedilation = 1.0f; + + private int m_update_physics = 1; + private int m_update_entitymovement = 1; + private int m_update_entities = 1; // Run through all objects checking for updates + private int m_update_entitiesquick = 200; // Run through objects that have scheduled updates checking for updates + private int m_update_presences = 1; // Update scene presence movements + private int m_update_events = 1; + private int m_update_backup = 200; + private int m_update_terrain = 50; + private int m_update_land = 1; + + private int frameMS = 0; + private int physicsMS2 = 0; + private int physicsMS = 0; + private int otherMS = 0; + + private bool m_physics_enabled = true; + private bool m_scripts_enabled = true; + private string m_defaultScriptEngine; + private int m_LastLogin = 0; + private Thread HeartbeatThread; + private volatile bool shuttingdown = false; + + private object m_deleting_scene_object = new object(); + + // the minimum time that must elapse before a changed object will be considered for persisted + public long m_dontPersistBefore = DEFAULT_MIN_TIME_FOR_PERSISTENCE * 10000000L; + // the maximum time that must elapse before a changed object will be considered for persisted + public long m_persistAfter = DEFAULT_MAX_TIME_FOR_PERSISTENCE * 10000000L; + + #endregion + + #region Properties + + public AgentCircuitManager AuthenticateHandler + { + get { return m_authenticateHandler; } + } + + public SceneGraph SceneContents + { + get { return m_sceneGraph; } + } + + // an instance to the physics plugin's Scene object. + public PhysicsScene PhysicsScene + { + get { return m_sceneGraph.PhysicsScene; } + set + { + // If we're not doing the initial set + // Then we've got to remove the previous + // event handler + if (PhysicsScene != null && PhysicsScene.SupportsNINJAJoints) + { + PhysicsScene.OnJointMoved -= jointMoved; + PhysicsScene.OnJointDeactivated -= jointDeactivated; + PhysicsScene.OnJointErrorMessage -= jointErrorMessage; + } + + m_sceneGraph.PhysicsScene = value; + + if (PhysicsScene != null && m_sceneGraph.PhysicsScene.SupportsNINJAJoints) + { + // register event handlers to respond to joint movement/deactivation + PhysicsScene.OnJointMoved += jointMoved; + PhysicsScene.OnJointDeactivated += jointDeactivated; + PhysicsScene.OnJointErrorMessage += jointErrorMessage; + } + + } + } + + // This gets locked so things stay thread safe. + public object SyncRoot + { + get { return m_sceneGraph.m_syncRoot; } + } + + public float TimeDilation + { + get { return m_timedilation; } + } + + /// + /// This is for llGetRegionFPS + /// + public float SimulatorFPS + { + get { return StatsReporter.getLastReportedSimFPS(); } + } + + public string DefaultScriptEngine + { + get { return m_defaultScriptEngine; } + } + + // Local reference to the objects in the scene (which are held in the scenegraph) + // public Dictionary Objects + // { + // get { return m_sceneGraph.SceneObjects; } + // } + + // Reference to all of the agents in the scene (root and child) + protected Dictionary m_scenePresences + { + get { return m_sceneGraph.ScenePresences; } + set { m_sceneGraph.ScenePresences = value; } + } + + // protected Dictionary m_sceneObjects + // { + // get { return m_sceneGraph.SceneObjects; } + // set { m_sceneGraph.SceneObjects = value; } + // } + + public EntityManager Entities + { + get { return m_sceneGraph.Entities; } + } + + public Dictionary m_restorePresences + { + get { return m_sceneGraph.RestorePresences; } + set { m_sceneGraph.RestorePresences = value; } + } + + public int objectCapacity = 45000; + + #endregion + + #region Constructors + + public Scene(RegionInfo regInfo, AgentCircuitManager authen, + CommunicationsManager commsMan, SceneCommunicationService sceneGridService, + AssetCache assetCach, StorageManager storeManager, + ModuleLoader moduleLoader, bool dumpAssetsToFile, bool physicalPrim, + bool SeeIntoRegionFromNeighbor, IConfigSource config, string simulatorVersion) + { + m_config = config; + + Random random = new Random(); + m_lastAllocatedLocalId = (uint)(random.NextDouble() * (double)(uint.MaxValue/2))+(uint)(uint.MaxValue/4); + m_moduleLoader = moduleLoader; + m_authenticateHandler = authen; + CommsManager = commsMan; + m_sceneGridService = sceneGridService; + m_storageManager = storeManager; + AssetCache = assetCach; + m_regInfo = regInfo; + m_regionHandle = m_regInfo.RegionHandle; + m_regionName = m_regInfo.RegionName; + m_datastore = m_regInfo.DataStore; + + m_physicalPrim = physicalPrim; + m_seeIntoRegionFromNeighbor = SeeIntoRegionFromNeighbor; + + m_eventManager = new EventManager(); + m_permissions = new ScenePermissions(this); + + m_asyncSceneObjectDeleter = new AsyncSceneObjectGroupDeleter(this); + m_asyncSceneObjectDeleter.Enabled = true; + + // Load region settings + m_regInfo.RegionSettings = m_storageManager.DataStore.LoadRegionSettings(m_regInfo.RegionID); + if (m_storageManager.EstateDataStore != null) + m_regInfo.EstateSettings = m_storageManager.EstateDataStore.LoadEstateSettings(m_regInfo.RegionID); + + //Bind Storage Manager functions to some land manager functions for this scene + EventManager.OnLandObjectAdded += + new EventManager.LandObjectAdded(m_storageManager.DataStore.StoreLandObject); + EventManager.OnLandObjectRemoved += + new EventManager.LandObjectRemoved(m_storageManager.DataStore.RemoveLandObject); + + m_sceneGraph = new SceneGraph(this, m_regInfo); + + // If the scene graph has an Unrecoverable error, restart this sim. + // Currently the only thing that causes it to happen is two kinds of specific + // Physics based crashes. + // + // Out of memory + // Operating system has killed the plugin + m_sceneGraph.UnRecoverableError += RestartNow; + + RegisterDefaultSceneEvents(); + + DumpAssetsToFile = dumpAssetsToFile; + + m_scripts_enabled = !RegionInfo.RegionSettings.DisableScripts; + + m_physics_enabled = !RegionInfo.RegionSettings.DisablePhysics; + + StatsReporter = new SimStatsReporter(this); + StatsReporter.OnSendStatsResult += SendSimStatsPackets; + StatsReporter.OnStatsIncorrect += m_sceneGraph.RecalculateStats; + + StatsReporter.SetObjectCapacity(objectCapacity); + + m_simulatorVersion = simulatorVersion + + " (OS " + Util.GetOperatingSystemInformation() + ")" + + " ChilTasks:" + m_seeIntoRegionFromNeighbor.ToString() + + " PhysPrim:" + m_physicalPrim.ToString(); + + try + { + // Region config overrides global config + // + IConfig startupConfig = m_config.Configs["Startup"]; + m_maxNonphys = startupConfig.GetFloat("NonPhysicalPrimMax", 65536.0f); + if (RegionInfo.NonphysPrimMax > 0) + m_maxNonphys = RegionInfo.NonphysPrimMax; + + m_maxPhys = startupConfig.GetFloat("PhysicalPrimMax", 10.0f); + + if (RegionInfo.PhysPrimMax > 0) + m_maxPhys = RegionInfo.PhysPrimMax; + + // Here, if clamping is requested in either global or + // local config, it will be used + // + m_clampPrimSize = startupConfig.GetBoolean("ClampPrimSize", false); + if (RegionInfo.ClampPrimSize) + m_clampPrimSize = true; + + m_trustBinaries = startupConfig.GetBoolean("TrustBinaries", false); + m_allowScriptCrossings = startupConfig.GetBoolean("AllowScriptCrossing", false); + m_dontPersistBefore = + startupConfig.GetLong("MinimumTimeBeforePersistenceConsidered", DEFAULT_MIN_TIME_FOR_PERSISTENCE); + m_dontPersistBefore *= 10000000; + m_persistAfter = + startupConfig.GetLong("MaximumTimeBeforePersistenceConsidered", DEFAULT_MAX_TIME_FOR_PERSISTENCE); + m_persistAfter *= 10000000; + + m_defaultScriptEngine = startupConfig.GetString("DefaultScriptEngine", "DotNetEngine"); + } + catch + { + m_log.Warn("[SCENE]: Failed to load StartupConfig"); + } + } + + /// + /// Mock constructor for scene group persistency unit tests. + /// SceneObjectGroup RegionId property is delegated to Scene. + /// + /// + public Scene(RegionInfo regInfo) + { + m_regInfo = regInfo; + m_eventManager = new EventManager(); + } + + #endregion + + #region Startup / Close Methods + + public bool ShuttingDown + { + get { return shuttingdown; } + } + + protected virtual void RegisterDefaultSceneEvents() + { + IDialogModule dm = RequestModuleInterface(); + + if (dm != null) + m_eventManager.OnPermissionError += dm.SendAlertToUser; + } + + public override string GetSimulatorVersion() + { + return m_simulatorVersion; + } + + /// + /// Another region is up. Gets called from Grid Comms: + /// (OGS1 -> LocalBackEnd -> RegionListened -> SceneCommunicationService) + /// We have to tell all our ScenePresences about it, and add it to the + /// neighbor list. + /// + /// We only add it to the neighbor list if it's within 1 region from here. + /// Agents may have draw distance values that cross two regions though, so + /// we add it to the notify list regardless of distance. We'll check + /// the agent's draw distance before notifying them though. + /// + /// RegionInfo handle for the new region. + /// True after all operations complete, throws exceptions otherwise. + public override bool OtherRegionUp(RegionInfo otherRegion) + { + if (RegionInfo.RegionHandle != otherRegion.RegionHandle) + { + for (int i = 0; i < m_neighbours.Count; i++) + { + // The purpose of this loop is to re-update the known neighbors + // when another region comes up on top of another one. + // The latest region in that location ends up in the + // 'known neighbors list' + // Additionally, the commFailTF property gets reset to false. + if (m_neighbours[i].RegionHandle == otherRegion.RegionHandle) + { + lock (m_neighbours) + { + m_neighbours[i] = otherRegion; + } + } + } + + // If the value isn't in the neighbours, add it. + // If the RegionInfo isn't exact but is for the same XY World location, + // then the above loop will fix that. + + if (!(CheckNeighborRegion(otherRegion))) + { + lock (m_neighbours) + { + m_neighbours.Add(otherRegion); + //m_log.Info("[UP]: " + otherRegion.RegionHandle.ToString()); + } + } + + // If these are cast to INT because long + negative values + abs returns invalid data + int resultX = Math.Abs((int)otherRegion.RegionLocX - (int)RegionInfo.RegionLocX); + int resultY = Math.Abs((int)otherRegion.RegionLocY - (int)RegionInfo.RegionLocY); + if (resultX <= 1 && resultY <= 1) + { + try + { + ForEachScenePresence(delegate(ScenePresence agent) + { + // If agent is a root agent. + if (!agent.IsChildAgent) + { + //agent.ControllingClient.new + //this.CommsManager.InterRegion.InformRegionOfChildAgent(otherRegion.RegionHandle, agent.ControllingClient.RequestClientInfo()); + InformClientOfNeighbor(agent, otherRegion); + } + } + ); + } + catch (NullReferenceException) + { + // This means that we're not booted up completely yet. + // This shouldn't happen too often anymore. + m_log.Error("[SCENE]: Couldn't inform client of regionup because we got a null reference exception"); + } + } + else + { + m_log.Info("[INTERGRID]: Got notice about far away Region: " + otherRegion.RegionName.ToString() + + " at (" + otherRegion.RegionLocX.ToString() + ", " + + otherRegion.RegionLocY.ToString() + ")"); + } + } + return true; + } + + public void AddNeighborRegion(RegionInfo region) + { + lock (m_neighbours) + { + if (!CheckNeighborRegion(region)) + { + m_neighbours.Add(region); + } + } + } + + public bool CheckNeighborRegion(RegionInfo region) + { + bool found = false; + lock (m_neighbours) + { + foreach (RegionInfo reg in m_neighbours) + { + if (reg.RegionHandle == region.RegionHandle) + { + found = true; + break; + } + } + } + return found; + } + + /// + /// Given float seconds, this will restart the region. + /// + /// float indicating duration before restart. + public virtual void Restart(float seconds) + { + // notifications are done in 15 second increments + // so .. if the number of seconds is less then 15 seconds, it's not really a restart request + // It's a 'Cancel restart' request. + + // RestartNow() does immediate restarting. + if (seconds < 15) + { + m_restartTimer.Stop(); + m_dialogModule.SendGeneralAlert("Restart Aborted"); + } + else + { + // Now we figure out what to set the timer to that does the notifications and calls, RestartNow() + m_restartTimer.Interval = 15000; + m_incrementsof15seconds = (int)seconds / 15; + m_RestartTimerCounter = 0; + m_restartTimer.AutoReset = true; + m_restartTimer.Elapsed += new ElapsedEventHandler(RestartTimer_Elapsed); + m_log.Info("[REGION]: Restarting Region in " + (seconds / 60) + " minutes"); + m_restartTimer.Start(); + m_dialogModule.SendNotificationToUsersInRegion( + UUID.Random(), String.Empty, RegionInfo.RegionName + ": Restarting in 2 Minutes"); + } + } + + // The Restart timer has occured. + // We have to figure out if this is a notification or if the number of seconds specified in Restart + // have elapsed. + // If they have elapsed, call RestartNow() + public void RestartTimer_Elapsed(object sender, ElapsedEventArgs e) + { + m_RestartTimerCounter++; + if (m_RestartTimerCounter <= m_incrementsof15seconds) + { + if (m_RestartTimerCounter == 4 || m_RestartTimerCounter == 6 || m_RestartTimerCounter == 7) + m_dialogModule.SendNotificationToUsersInRegion( + UUID.Random(), + String.Empty, + RegionInfo.RegionName + ": Restarting in " + ((8 - m_RestartTimerCounter) * 15) + " seconds"); + } + else + { + m_restartTimer.Stop(); + m_restartTimer.AutoReset = false; + RestartNow(); + } + } + + // This causes the region to restart immediatley. + public void RestartNow() + { + if (PhysicsScene != null) + { + PhysicsScene.Dispose(); + } + + m_log.Error("[REGION]: Closing"); + Close(); + m_log.Error("[REGION]: Firing Region Restart Message"); + base.Restart(0); + } + + // This is a helper function that notifies root agents in this region that a new sim near them has come up + // This is in the form of a timer because when an instance of OpenSim.exe is started, + // Even though the sims initialize, they don't listen until 'all of the sims are initialized' + // If we tell an agent about a sim that's not listening yet, the agent will not be able to connect to it. + // subsequently the agent will never see the region come back online. + public void RestartNotifyWaitElapsed(object sender, ElapsedEventArgs e) + { + m_restartWaitTimer.Stop(); + lock (m_regionRestartNotifyList) + { + foreach (RegionInfo region in m_regionRestartNotifyList) + { + try + { + ForEachScenePresence(delegate(ScenePresence agent) + { + // If agent is a root agent. + if (!agent.IsChildAgent) + { + //agent.ControllingClient.new + //this.CommsManager.InterRegion.InformRegionOfChildAgent(otherRegion.RegionHandle, agent.ControllingClient.RequestClientInfo()); + InformClientOfNeighbor(agent, region); + } + } + ); + } + catch (NullReferenceException) + { + // This means that we're not booted up completely yet. + // This shouldn't happen too often anymore. + } + } + + // Reset list to nothing. + m_regionRestartNotifyList.Clear(); + } + } + + public void SetSceneCoreDebug(bool ScriptEngine, bool CollisionEvents, bool PhysicsEngine) + { + if (m_scripts_enabled != !ScriptEngine) + { + // Tedd! Here's the method to disable the scripting engine! + if (ScriptEngine) + { + m_log.Info("Stopping all Scripts in Scene"); + foreach (EntityBase ent in Entities) + { + if (ent is SceneObjectGroup) + { + ((SceneObjectGroup) ent).RemoveScriptInstances(); + } + } + } + else + { + m_log.Info("Starting all Scripts in Scene"); + lock (Entities) + { + foreach (EntityBase ent in Entities) + { + if (ent is SceneObjectGroup) + { + ((SceneObjectGroup)ent).CreateScriptInstances(0, false, DefaultScriptEngine, 0); + } + } + } + } + m_scripts_enabled = !ScriptEngine; + m_log.Info("[TOTEDD]: Here is the method to trigger disabling of the scripting engine"); + } + + if (m_physics_enabled != !PhysicsEngine) + { + m_physics_enabled = !PhysicsEngine; + } + } + + public int GetInaccurateNeighborCount() + { + lock (m_neighbours) + { + return m_neighbours.Count; + } + } + + // This is the method that shuts down the scene. + public override void Close() + { + m_log.InfoFormat("[SCENE]: Closing down the single simulator: {0}", RegionInfo.RegionName); + + // Kick all ROOT agents with the message, 'The simulator is going down' + ForEachScenePresence(delegate(ScenePresence avatar) + { + if (avatar.KnownChildRegionHandles.Contains(RegionInfo.RegionHandle)) + avatar.KnownChildRegionHandles.Remove(RegionInfo.RegionHandle); + + if (!avatar.IsChildAgent) + avatar.ControllingClient.Kick("The simulator is going down."); + + avatar.ControllingClient.SendShutdownConnectionNotice(); + }); + + // Wait here, or the kick messages won't actually get to the agents before the scene terminates. + Thread.Sleep(500); + + // Stop all client threads. + ForEachScenePresence(delegate(ScenePresence avatar) { avatar.ControllingClient.Close(true); }); + + // Stop updating the scene objects and agents. + //m_heartbeatTimer.Close(); + shuttingdown = true; + + m_log.Debug("[SCENE]: Persisting changed objects"); + List entities = GetEntities(); + foreach (EntityBase entity in entities) + { + if (!entity.IsDeleted && entity is SceneObjectGroup && ((SceneObjectGroup)entity).HasGroupChanged) + { + ((SceneObjectGroup)entity).ProcessBackup(m_storageManager.DataStore, false); + } + } + + m_sceneGraph.Close(); + + // De-register with region communications (events cleanup) + UnRegisterRegionWithComms(); + + // call the base class Close method. + base.Close(); + } + + /// + /// Start the timer which triggers regular scene updates + /// + public void StartTimer() + { + //m_log.Debug("[SCENE]: Starting timer"); + //m_heartbeatTimer.Enabled = true; + //m_heartbeatTimer.Interval = (int)(m_timespan * 1000); + //m_heartbeatTimer.Elapsed += new ElapsedEventHandler(Heartbeat); + HeartbeatThread = new Thread(new ParameterizedThreadStart(Heartbeat)); + HeartbeatThread.SetApartmentState(ApartmentState.MTA); + HeartbeatThread.Name = "Heartbeat"; + HeartbeatThread.Priority = ThreadPriority.AboveNormal; + ThreadTracker.Add(HeartbeatThread); + HeartbeatThread.Start(); + } + + /// + /// Sets up references to modules required by the scene + /// + public void SetModuleInterfaces() + { + m_xmlrpcModule = RequestModuleInterface(); + m_worldCommModule = RequestModuleInterface(); + XferManager = RequestModuleInterface(); + m_AvatarFactory = RequestModuleInterface(); + m_serialiser = RequestModuleInterface(); + m_interregionCommsOut = RequestModuleInterface(); + m_interregionCommsIn = RequestModuleInterface(); + m_dialogModule = RequestModuleInterface(); + m_capsModule = RequestModuleInterface(); + } + + #endregion + + #region Update Methods + + /// + /// Performs per-frame updates regularly + /// + /// + /// + private void Heartbeat(object sender) + { + Update(); + } + + /// + /// Performs per-frame updates on the scene, this should be the central scene loop + /// + public override void Update() + { + int maintc = 0; + while (!shuttingdown) + { + maintc = System.Environment.TickCount; + + TimeSpan SinceLastFrame = DateTime.Now - m_lastupdate; + // Aquire a lock so only one update call happens at once + //updateLock.WaitOne(); + float physicsFPS = 0; + //m_log.Info("sadfadf" + m_neighbours.Count.ToString()); + int agentsInScene = m_sceneGraph.GetRootAgentCount() + m_sceneGraph.GetChildAgentCount(); + + if (agentsInScene > 21) + { + if (m_update_entities == 1) + { + m_update_entities = 5; + StatsReporter.SetUpdateMS(6000); + } + } + else + { + if (m_update_entities == 5) + { + m_update_entities = 1; + StatsReporter.SetUpdateMS(3000); + } + } + + frameMS = System.Environment.TickCount; + try + { + // Increment the frame counter + m_frame++; + + // Loop it + if (m_frame == Int32.MaxValue) + m_frame = 0; + + physicsMS2 = System.Environment.TickCount; + if ((m_frame % m_update_physics == 0) && m_physics_enabled) + m_sceneGraph.UpdatePreparePhysics(); + physicsMS2 = System.Environment.TickCount - physicsMS2; + + if (m_frame % m_update_entitymovement == 0) + m_sceneGraph.UpdateEntityMovement(); + + physicsMS = System.Environment.TickCount; + if ((m_frame % m_update_physics == 0) && m_physics_enabled) + physicsFPS = m_sceneGraph.UpdatePhysics( + Math.Max(SinceLastFrame.TotalSeconds, m_timespan) + ); + if (m_frame % m_update_physics == 0 && SynchronizeScene != null) + SynchronizeScene(this); + + physicsMS = System.Environment.TickCount - physicsMS; + physicsMS += physicsMS2; + + otherMS = System.Environment.TickCount; + // run through all entities looking for updates (slow) + if (m_frame % m_update_entities == 0) + m_sceneGraph.UpdateEntities(); + + // run through entities that have scheduled themselves for + // updates looking for updates(faster) + if (m_frame % m_update_entitiesquick == 0) + m_sceneGraph.ProcessUpdates(); + + // Run through scenepresences looking for updates + if (m_frame % m_update_presences == 0) + m_sceneGraph.UpdatePresences(); + + // Delete temp-on-rez stuff + if (m_frame % m_update_backup == 0) + CleanTempObjects(); + + if (Region_Status != RegionStatus.SlaveScene) + { + if (m_frame % m_update_events == 0) + UpdateEvents(); + + if (m_frame % m_update_backup == 0) + { + UpdateStorageBackup(); + } + + if (m_frame % m_update_terrain == 0) + UpdateTerrain(); + + if (m_frame % m_update_land == 0) + UpdateLand(); + otherMS = System.Environment.TickCount - otherMS; + // if (m_frame%m_update_avatars == 0) + // UpdateInWorldTime(); + StatsReporter.AddPhysicsFPS(physicsFPS); + StatsReporter.AddTimeDilation(m_timedilation); + StatsReporter.AddFPS(1); + StatsReporter.AddInPackets(0); + StatsReporter.SetRootAgents(m_sceneGraph.GetRootAgentCount()); + StatsReporter.SetChildAgents(m_sceneGraph.GetChildAgentCount()); + StatsReporter.SetObjects(m_sceneGraph.GetTotalObjectsCount()); + StatsReporter.SetActiveObjects(m_sceneGraph.GetActiveObjectsCount()); + frameMS = System.Environment.TickCount - frameMS; + StatsReporter.addFrameMS(frameMS); + StatsReporter.addPhysicsMS(physicsMS); + StatsReporter.addOtherMS(otherMS); + StatsReporter.SetActiveScripts(m_sceneGraph.GetActiveScriptsCount()); + StatsReporter.addScriptLines(m_sceneGraph.GetScriptLPS()); + } + } + catch (NotImplementedException) + { + throw; + } + catch (AccessViolationException e) + { + m_log.Error("[Scene]: Failed with exception " + e.ToString() + " On Region: " + RegionInfo.RegionName); + } + //catch (NullReferenceException e) + //{ + // m_log.Error("[Scene]: Failed with exception " + e.ToString() + " On Region: " + RegionInfo.RegionName); + //} + catch (InvalidOperationException e) + { + m_log.Error("[Scene]: Failed with exception " + e.ToString() + " On Region: " + RegionInfo.RegionName); + } + catch (Exception e) + { + m_log.Error("[Scene]: Failed with exception " + e.ToString() + " On Region: " + RegionInfo.RegionName); + } + finally + { + //updateLock.ReleaseMutex(); + // Get actual time dilation + float tmpval = (m_timespan / (float)SinceLastFrame.TotalSeconds); + + // If actual time dilation is greater then one, we're catching up, so subtract + // the amount that's greater then 1 from the time dilation + if (tmpval > 1.0) + { + tmpval = tmpval - (tmpval - 1.0f); + } + m_timedilation = tmpval; + + m_lastupdate = DateTime.Now; + } + maintc = System.Environment.TickCount - maintc; + maintc = (int)(m_timespan * 1000) - maintc; + + if ((maintc < (m_timespan * 1000)) && maintc > 0) + Thread.Sleep(maintc); + } + } + + private void SendSimStatsPackets(SimStats stats) + { + List StatSendAgents = GetScenePresences(); + foreach (ScenePresence agent in StatSendAgents) + { + if (!agent.IsChildAgent) + { + agent.ControllingClient.SendSimStats(stats); + } + } + } + + private void UpdateLand() + { + if (LandChannel != null) + { + if (LandChannel.IsLandPrimCountTainted()) + { + EventManager.TriggerParcelPrimCountUpdate(); + } + } + } + + private void UpdateTerrain() + { + EventManager.TriggerTerrainTick(); + } + + private void UpdateStorageBackup() + { + if (!m_backingup) + { + m_backingup = true; + Thread backupthread = new Thread(Backup); + backupthread.Name = "BackupWriter"; + backupthread.IsBackground = true; + backupthread.Start(); + } + } + + private void UpdateEvents() + { + m_eventManager.TriggerOnFrame(); + } + + /// + /// Perform delegate action on all clients subscribing to updates from this region. + /// + /// + public void Broadcast(Action whatToDo) + { + ForEachScenePresence(delegate(ScenePresence presence) { whatToDo(presence.ControllingClient); }); + } + + /// + /// Backup the scene. This acts as the main method of the backup thread. + /// + /// + public void Backup() + { + lock (m_returns) + { + EventManager.TriggerOnBackup(m_storageManager.DataStore); + m_backingup = false; + + foreach (KeyValuePair ret in m_returns) + { + UUID transaction = UUID.Random(); + + GridInstantMessage msg = new GridInstantMessage(); + msg.fromAgentID = new Guid(UUID.Zero.ToString()); // From server + msg.toAgentID = new Guid(ret.Key.ToString()); + msg.imSessionID = new Guid(transaction.ToString()); + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.fromAgentName = "Server"; + msg.dialog = (byte)19; // Object msg + msg.fromGroup = false; + msg.offline = (byte)1; + msg.ParentEstateID = RegionInfo.EstateSettings.ParentEstateID; + msg.Position = Vector3.Zero; + msg.RegionID = RegionInfo.RegionID.Guid; + msg.binaryBucket = new byte[0]; + if (ret.Value.count > 1) + msg.message = string.Format("Your {0} objects were returned from {1} in region {2} due to {3}", ret.Value.count, ret.Value.location.ToString(), RegionInfo.RegionName, ret.Value.reason); + else + msg.message = string.Format("Your object {0} was returned from {1} in region {2} due to {3}", ret.Value.objectName, ret.Value.location.ToString(), RegionInfo.RegionName, ret.Value.reason); + + IMessageTransferModule tr = RequestModuleInterface(); + if (tr != null) + tr.SendInstantMessage(msg, delegate(bool success) {} ); + } + m_returns.Clear(); + } + } + + public void ForceSceneObjectBackup(SceneObjectGroup group) + { + if (group != null) + { + group.ProcessBackup(m_storageManager.DataStore, true); + } + } + + public void AddReturn(UUID agentID, string objectName, Vector3 location, string reason) + { + lock (m_returns) + { + if (m_returns.ContainsKey(agentID)) + { + ReturnInfo info = m_returns[agentID]; + info.count++; + m_returns[agentID] = info; + } + else + { + ReturnInfo info = new ReturnInfo(); + info.count = 1; + info.objectName = objectName; + info.location = location; + info.reason = reason; + m_returns[agentID] = info; + } + } + } + + #endregion + + #region Load Terrain + + public void ExportWorldMap(string fileName) + { + List mapBlocks = + m_sceneGridService.RequestNeighbourMapBlocks((int)(RegionInfo.RegionLocX - 9), + (int)(RegionInfo.RegionLocY - 9), + (int)(RegionInfo.RegionLocX + 9), + (int)(RegionInfo.RegionLocY + 9)); + List textures = new List(); + List bitImages = new List(); + + foreach (MapBlockData mapBlock in mapBlocks) + { + AssetBase texAsset = AssetCache.GetAsset(mapBlock.MapImageId, true); + + if (texAsset != null) + { + textures.Add(texAsset); + } + else + { + texAsset = AssetCache.GetAsset(mapBlock.MapImageId, true); + if (texAsset != null) + { + textures.Add(texAsset); + } + } + } + + foreach (AssetBase asset in textures) + { + ManagedImage managedImage; + Image image; + + if (OpenJPEG.DecodeToImage(asset.Data, out managedImage, out image)) + bitImages.Add(image); + } + + Bitmap mapTexture = new Bitmap(2560, 2560); + Graphics g = Graphics.FromImage(mapTexture); + SolidBrush sea = new SolidBrush(Color.DarkBlue); + g.FillRectangle(sea, 0, 0, 2560, 2560); + + for (int i = 0; i < mapBlocks.Count; i++) + { + ushort x = (ushort)((mapBlocks[i].X - RegionInfo.RegionLocX) + 10); + ushort y = (ushort)((mapBlocks[i].Y - RegionInfo.RegionLocY) + 10); + g.DrawImage(bitImages[i], (x * 128), (y * 128), 128, 128); + } + mapTexture.Save(fileName, ImageFormat.Jpeg); + } + + public void SaveTerrain() + { + m_storageManager.DataStore.StoreTerrain(Heightmap.GetDoubles(), RegionInfo.RegionID); + } + + /// + /// Loads the World heightmap + /// + public override void LoadWorldMap() + { + try + { + double[,] map = m_storageManager.DataStore.LoadTerrain(RegionInfo.RegionID); + if (map == null) + { + m_log.Info("[TERRAIN]: No default terrain. Generating a new terrain."); + Heightmap = new TerrainChannel(); + + m_storageManager.DataStore.StoreTerrain(Heightmap.GetDoubles(), RegionInfo.RegionID); + } + else + { + Heightmap = new TerrainChannel(map); + } + + } + catch (Exception e) + { + m_log.Warn("[TERRAIN]: Scene.cs: LoadWorldMap() - Failed with exception " + e.ToString()); + } + } + + /// + /// Register this region with a grid service + /// + /// Thrown if registration of the region itself fails. + public void RegisterRegionWithGrid() + { + RegisterCommsEvents(); + + // These two 'commands' *must be* next to each other or sim rebooting fails. + m_sceneGridService.RegisterRegion(m_interregionCommsOut, RegionInfo); + m_sceneGridService.InformNeighborsThatRegionisUp(RegionInfo); + + Dictionary dGridSettings = m_sceneGridService.GetGridSettings(); + + if (dGridSettings.ContainsKey("allow_forceful_banlines")) + { + if (dGridSettings["allow_forceful_banlines"] != "TRUE") + { + m_log.Info("[GRID]: Grid is disabling forceful parcel banlists"); + EventManager.TriggerSetAllowForcefulBan(false); + } + else + { + m_log.Info("[GRID]: Grid is allowing forceful parcel banlists"); + EventManager.TriggerSetAllowForcefulBan(true); + } + } + } + + /// + /// + /// + public void CreateTerrainTexture(bool temporary) + { + //create a texture asset of the terrain + IMapImageGenerator terrain = RequestModuleInterface(); + + // Cannot create a map for a nonexistant heightmap yet. + if (Heightmap == null) + return; + + if (terrain == null) + { + #region Fallback default maptile generation + + int tc = System.Environment.TickCount; + m_log.Info("[MAPTILE]: Generating Maptile Step 1: Terrain"); + Bitmap mapbmp = new Bitmap(256, 256); + double[,] hm = Heightmap.GetDoubles(); + bool ShadowDebugContinue = true; + //Color prim = Color.FromArgb(120, 120, 120); + //Vector3 RayEnd = new Vector3(0, 0, 0); + //Vector3 RayStart = new Vector3(0, 0, 0); + //Vector3 direction = new Vector3(0, 0, -1); + //Vector3 AXOrigin = new Vector3(); + //Vector3 AXdirection = new Vector3(); + //Ray testRay = new Ray(); + //EntityIntersection rt = new EntityIntersection(); + bool terraincorruptedwarningsaid = false; + + float low = 255; + float high = 0; + for (int x = 0; x < 256; x++) + { + for (int y = 0; y < 256; y++) + { + float hmval = (float)hm[x, y]; + if (hmval < low) + low = hmval; + if (hmval > high) + high = hmval; + } + } + + float mid = (high + low) * 0.5f; + + // temporary initializer + float hfvalue = (float)m_regInfo.RegionSettings.WaterHeight; + float hfvaluecompare = hfvalue; + float hfdiff = hfvalue; + int hfdiffi = 0; + + for (int x = 0; x < 256; x++) + { + //int tc = System.Environment.TickCount; + for (int y = 0; y < 256; y++) + { + //RayEnd = new Vector3(x, y, 0); + //RayStart = new Vector3(x, y, 255); + + //direction = Vector3.Norm(RayEnd - RayStart); + //AXOrigin = new Vector3(RayStart.X, RayStart.Y, RayStart.Z); + //AXdirection = new Vector3(direction.X, direction.Y, direction.Z); + + //testRay = new Ray(AXOrigin, AXdirection); + //rt = m_sceneGraph.GetClosestIntersectingPrim(testRay); + + //if (rt.HitTF) + //{ + //mapbmp.SetPixel(x, y, prim); + //} + //else + //{ + //float tmpval = (float)hm[x, y]; + float heightvalue = (float)hm[x, y]; + + if (heightvalue > (float)m_regInfo.RegionSettings.WaterHeight) + { + // scale height value + heightvalue = low + mid * (heightvalue - low) / mid; + + if (heightvalue > 255) + heightvalue = 255; + + if (heightvalue < 0) + heightvalue = 0; + + if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue)) + heightvalue = 0; + + try + { + Color green = Color.FromArgb((int)heightvalue, 100, (int)heightvalue); + + // Y flip the cordinates + mapbmp.SetPixel(x, (256 - y) - 1, green); + + //X + // . + // + // Shade the terrain for shadows + if ((x - 1 > 0) && (y - 1 > 0)) + { + hfvalue = (float)hm[x, y]; + hfvaluecompare = (float)hm[x - 1, y - 1]; + + if (Single.IsInfinity(hfvalue) || Single.IsNaN(hfvalue)) + hfvalue = 0; + + if (Single.IsInfinity(hfvaluecompare) || Single.IsNaN(hfvaluecompare)) + hfvaluecompare = 0; + + hfdiff = hfvaluecompare - hfvalue; + + if (hfdiff > 0.3f) + { + + } + else if (hfdiff < -0.3f) + { + // We have to desaturate and blacken the land at the same time + // we use floats, colors use bytes, so shrink are space down to + // 0-255 + + try + { + hfdiffi = Math.Abs((int)((hfdiff * 4) + (hfdiff * 0.5))) + 1; + if (hfdiff % 1 != 0) + { + hfdiffi = hfdiffi + Math.Abs((int)(((hfdiff % 1) * 0.5f) * 10f) - 1); + } + } + catch (System.OverflowException) + { + m_log.Debug("[MAPTILE]: Shadow failed at value: " + hfdiff.ToString()); + ShadowDebugContinue = false; + } + + if (ShadowDebugContinue) + { + if ((256 - y) - 1 > 0) + { + Color Shade = mapbmp.GetPixel(x - 1, (256 - y) - 1); + + int r = Shade.R; + + int g = Shade.G; + int b = Shade.B; + Shade = Color.FromArgb((r - hfdiffi > 0) ? r - hfdiffi : 0, (g - hfdiffi > 0) ? g - hfdiffi : 0, (b - hfdiffi > 0) ? b - hfdiffi : 0); + //Console.WriteLine("d:" + hfdiff.ToString() + ", i:" + hfdiffi + ", pos: " + x + "," + y + " - R:" + Shade.R.ToString() + ", G:" + Shade.G.ToString() + ", B:" + Shade.G.ToString()); + mapbmp.SetPixel(x - 1, (256 - y) - 1, Shade); + } + } + } + } + } + catch (System.ArgumentException) + { + if (!terraincorruptedwarningsaid) + { + m_log.WarnFormat("[MAPIMAGE]: Your terrain is corrupted in region {0}, it might take a few minutes to generate the map image depending on the corruption level", RegionInfo.RegionName); + terraincorruptedwarningsaid = true; + } + Color black = Color.Black; + mapbmp.SetPixel(x, (256 - y) - 1, black); + } + } + else + { + // Y flip the cordinates + heightvalue = (float)m_regInfo.RegionSettings.WaterHeight - heightvalue; + if (heightvalue > 19) + heightvalue = 19; + if (heightvalue < 0) + heightvalue = 0; + + heightvalue = 100 - (heightvalue * 100) / 19; + + if (heightvalue > 255) + heightvalue = 255; + + if (heightvalue < 0) + heightvalue = 0; + + if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue)) + heightvalue = 0; + + try + { + Color water = Color.FromArgb((int)heightvalue, (int)heightvalue, 255); + mapbmp.SetPixel(x, (256 - y) - 1, water); + } + catch (System.ArgumentException) + { + if (!terraincorruptedwarningsaid) + { + m_log.WarnFormat("[MAPIMAGE]: Your terrain is corrupted in region {0}, it might take a few minutes to generate the map image depending on the corruption level", RegionInfo.RegionName); + terraincorruptedwarningsaid = true; + } + Color black = Color.Black; + mapbmp.SetPixel(x, (256 - y) - 1, black); + } + } + } + //} + + //tc = System.Environment.TickCount - tc; + //m_log.Info("[MAPTILE]: Completed One row in " + tc + " ms"); + } + + m_log.Info("[MAPTILE]: Generating Maptile Step 1: Done in " + (System.Environment.TickCount - tc) + " ms"); + + bool drawPrimVolume = true; + + try + { + IConfig startupConfig = m_config.Configs["Startup"]; + drawPrimVolume = startupConfig.GetBoolean("DrawPrimOnMapTile", true); + } + catch + { + m_log.Warn("[MAPTILE]: Failed to load StartupConfig"); + } + + if (drawPrimVolume) + { + tc = System.Environment.TickCount; + m_log.Info("[MAPTILE]: Generating Maptile Step 2: Object Volume Profile"); + List objs = GetEntities(); + + lock (objs) + { + foreach (EntityBase obj in objs) + { + // Only draw the contents of SceneObjectGroup + if (obj is SceneObjectGroup) + { + SceneObjectGroup mapdot = (SceneObjectGroup)obj; + Color mapdotspot = Color.Gray; // Default color when prim color is white + // Loop over prim in group + foreach (SceneObjectPart part in mapdot.Children.Values) + { + if (part == null) + continue; + + // Draw if the object is at least 1 meter wide in any direction + if (part.Scale.X > 1f || part.Scale.Y > 1f || part.Scale.Z > 1f) + { + // Try to get the RGBA of the default texture entry.. + // + try + { + if (part == null) + continue; + + if (part.Shape == null) + continue; + + if (part.Shape.PCode == (byte)PCode.Tree || part.Shape.PCode == (byte)PCode.NewTree) + continue; // eliminates trees from this since we don't really have a good tree representation + // if you want tree blocks on the map comment the above line and uncomment the below line + //mapdotspot = Color.PaleGreen; + + if (part.Shape.Textures == null) + continue; + + if (part.Shape.Textures.DefaultTexture == null) + continue; + + Color4 texcolor = part.Shape.Textures.DefaultTexture.RGBA; + + // Not sure why some of these are null, oh well. + + int colorr = 255 - (int)(texcolor.R * 255f); + int colorg = 255 - (int)(texcolor.G * 255f); + int colorb = 255 - (int)(texcolor.B * 255f); + + if (!(colorr == 255 && colorg == 255 && colorb == 255)) + { + //Try to set the map spot color + try + { + // If the color gets goofy somehow, skip it *shakes fist at Color4 + mapdotspot = Color.FromArgb(colorr, colorg, colorb); + } + catch (ArgumentException) + { + } + } + } + catch (IndexOutOfRangeException) + { + // Windows Array + } + catch (ArgumentOutOfRangeException) + { + // Mono Array + } + + Vector3 pos = part.GetWorldPosition(); + + // skip prim outside of retion + if (pos.X < 0f || pos.X > 256f || pos.Y < 0f || pos.Y > 256f) + continue; + + // skip prim in non-finite position + if (Single.IsNaN(pos.X) || Single.IsNaN(pos.Y) || Single.IsInfinity(pos.X) + || Single.IsInfinity(pos.Y)) + continue; + + // Figure out if object is under 256m above the height of the terrain + bool isBelow256AboveTerrain = false; + + try + { + isBelow256AboveTerrain = (pos.Z < ((float)hm[(int)pos.X, (int)pos.Y] + 256f)); + } + catch (Exception) + { + } + + if (isBelow256AboveTerrain) + { + // Translate scale by rotation so scale is represented properly when object is rotated + Vector3 scale = part.Shape.Scale; + Quaternion rot = part.GetWorldRotation(); + scale *= rot; + + // negative scales don't work in this situation + scale.X = Math.Abs(scale.X); + scale.Y = Math.Abs(scale.Y); + scale.Z = Math.Abs(scale.Z); + + // This scaling isn't very accurate and doesn't take into account the face rotation :P + int mapdrawstartX = (int)(pos.X - scale.X); + int mapdrawstartY = (int)(pos.Y - scale.Y); + int mapdrawendX = (int)(pos.X + scale.X); + int mapdrawendY = (int)(pos.Y + scale.Y); + + // If object is beyond the edge of the map, don't draw it to avoid errors + if (mapdrawstartX < 0 || mapdrawstartX > 255 || mapdrawendX < 0 || mapdrawendX > 255 + || mapdrawstartY < 0 || mapdrawstartY > 255 || mapdrawendY < 0 + || mapdrawendY > 255) + continue; + + int wy = 0; + + bool breakYN = false; // If we run into an error drawing, break out of the + // loop so we don't lag to death on error handling + for (int wx = mapdrawstartX; wx < mapdrawendX; wx++) + { + for (wy = mapdrawstartY; wy < mapdrawendY; wy++) + { + //m_log.InfoFormat("[MAPDEBUG]: {0},{1}({2})", wx, (255 - wy),wy); + try + { + // Remember, flip the y! + mapbmp.SetPixel(wx, (255 - wy), mapdotspot); + } + catch (ArgumentException) + { + breakYN = true; + } + + if (breakYN) + break; + } + + if (breakYN) + break; + } + } // Object is within 256m Z of terrain + } // object is at least a meter wide + } // loop over group children + } // entitybase is sceneobject group + } // foreach loop over entities + } // lock entities objs + + m_log.Info("[MAPTILE]: Generating Maptile Step 2: Done in " + (System.Environment.TickCount - tc) + " ms"); + } // end if drawPrimOnMaptle + + byte[] data; + try + { + data = OpenJPEG.EncodeFromImage(mapbmp, false); + } + catch (Exception) + { + return; + } + + LazySaveGeneratedMaptile(data,temporary); + + #endregion + } + else + { + // Use the module to generate the maptile. + byte[] data = terrain.WriteJpeg2000Image("defaultstripe.png"); + if (data != null) + { + LazySaveGeneratedMaptile(data,temporary); + } + } + } + + public void LazySaveGeneratedMaptile(byte[] data, bool temporary) + { + // Overwrites the local Asset cache with new maptile data + // Assets are single write, this causes the asset server to ignore this update, + // but the local asset cache does not + + // this is on purpose! The net result of this is the region always has the most up to date + // map tile while protecting the (grid) asset database from bloat caused by a new asset each + // time a mapimage is generated! + + UUID lastMapRegionUUID = m_regInfo.lastMapUUID; + + int lastMapRefresh = 0; + int twoDays = 172800; + int RefreshSeconds = twoDays; + + try + { + lastMapRefresh = Convert.ToInt32(m_regInfo.lastMapRefresh); + } + catch (ArgumentException) + { + } + catch (FormatException) + { + } + catch (OverflowException) + { + } + + UUID TerrainImageUUID = UUID.Random(); + + if (lastMapRegionUUID == UUID.Zero || (lastMapRefresh + RefreshSeconds) < Util.UnixTimeSinceEpoch()) + { + m_regInfo.SaveLastMapUUID(TerrainImageUUID); + + m_log.Warn("[MAPTILE]: STORING MAPTILE IMAGE"); + } + else + { + TerrainImageUUID = lastMapRegionUUID; + m_log.Warn("[MAPTILE]: REUSING OLD MAPTILE IMAGE ID"); + } + + m_regInfo.RegionSettings.TerrainImageID = TerrainImageUUID; + + AssetBase asset = new AssetBase(); + asset.Metadata.FullID = m_regInfo.RegionSettings.TerrainImageID; + asset.Data = data; + asset.Metadata.Name = "terrainImage_" + m_regInfo.RegionID.ToString() + "_" + lastMapRefresh.ToString(); + asset.Metadata.Description = RegionInfo.RegionName; + + asset.Metadata.Type = 0; + asset.Metadata.Temporary = temporary; + AssetCache.AddAsset(asset); + } + + #endregion + + #region Load Land + + public void loadAllLandObjectsFromStorage(UUID regionID) + { + m_log.Info("[SCENE]: Loading land objects from storage"); + List landData = m_storageManager.DataStore.LoadLandObjects(regionID); + + if (LandChannel != null) + { + if (landData.Count == 0) + { + EventManager.TriggerNoticeNoLandDataFromStorage(); + } + else + { + EventManager.TriggerIncomingLandDataFromStorage(landData); + } + } + else + { + m_log.Error("[SCENE]: Land Channel is not defined. Cannot load from storage!"); + } + } + + #endregion + + #region Primitives Methods + + /// + /// Loads the World's objects + /// + public virtual void LoadPrimsFromStorage(UUID regionID) + { + m_log.Info("[SCENE]: Loading objects from datastore"); + + List PrimsFromDB = m_storageManager.DataStore.LoadObjects(regionID); + foreach (SceneObjectGroup group in PrimsFromDB) + { + if (group.RootPart == null) + { + m_log.ErrorFormat("[SCENE] Found a SceneObjectGroup with m_rootPart == null and {0} children", + group.Children == null ? 0 : group.Children.Count); + } + + AddRestoredSceneObject(group, true, true); + SceneObjectPart rootPart = group.GetChildPart(group.UUID); + rootPart.ObjectFlags &= ~(uint)PrimFlags.Scripted; + rootPart.TrimPermissions(); + group.CheckSculptAndLoad(); + //rootPart.DoPhysicsPropertyUpdate(UsePhysics, true); + } + + m_log.Info("[SCENE]: Loaded " + PrimsFromDB.Count.ToString() + " SceneObject(s)"); + } + + public Vector3 GetNewRezLocation(Vector3 RayStart, Vector3 RayEnd, UUID RayTargetID, Quaternion rot, byte bypassRayCast, byte RayEndIsIntersection, bool frontFacesOnly, Vector3 scale, bool FaceCenter) + { + Vector3 pos = Vector3.Zero; + if (RayEndIsIntersection == (byte)1) + { + pos = RayEnd; + return pos; + } + + if (RayTargetID != UUID.Zero) + { + SceneObjectPart target = GetSceneObjectPart(RayTargetID); + + Vector3 direction = Vector3.Normalize(RayEnd - RayStart); + Vector3 AXOrigin = new Vector3(RayStart.X, RayStart.Y, RayStart.Z); + Vector3 AXdirection = new Vector3(direction.X, direction.Y, direction.Z); + + if (target != null) + { + pos = target.AbsolutePosition; + //m_log.Info("[OBJECT_REZ]: TargetPos: " + pos.ToString() + ", RayStart: " + RayStart.ToString() + ", RayEnd: " + RayEnd.ToString() + ", Volume: " + Util.GetDistanceTo(RayStart,RayEnd).ToString() + ", mag1: " + Util.GetMagnitude(RayStart).ToString() + ", mag2: " + Util.GetMagnitude(RayEnd).ToString()); + + // TODO: Raytrace better here + + //EntityIntersection ei = m_sceneGraph.GetClosestIntersectingPrim(new Ray(AXOrigin, AXdirection)); + Ray NewRay = new Ray(AXOrigin, AXdirection); + + // Ray Trace against target here + EntityIntersection ei = target.TestIntersectionOBB(NewRay, Quaternion.Identity, frontFacesOnly, FaceCenter); + + // Un-comment out the following line to Get Raytrace results printed to the console. + // m_log.Info("[RAYTRACERESULTS]: Hit:" + ei.HitTF.ToString() + " Point: " + ei.ipoint.ToString() + " Normal: " + ei.normal.ToString()); + float ScaleOffset = 0.5f; + + // If we hit something + if (ei.HitTF) + { + Vector3 scaleComponent = new Vector3(ei.AAfaceNormal.X, ei.AAfaceNormal.Y, ei.AAfaceNormal.Z); + if (scaleComponent.X != 0) ScaleOffset = scale.X; + if (scaleComponent.Y != 0) ScaleOffset = scale.Y; + if (scaleComponent.Z != 0) ScaleOffset = scale.Z; + ScaleOffset = Math.Abs(ScaleOffset); + Vector3 intersectionpoint = new Vector3(ei.ipoint.X, ei.ipoint.Y, ei.ipoint.Z); + Vector3 normal = new Vector3(ei.normal.X, ei.normal.Y, ei.normal.Z); + // Set the position to the intersection point + Vector3 offset = (normal * (ScaleOffset / 2f)); + pos = (intersectionpoint + offset); + + // Un-offset the prim (it gets offset later by the consumer method) + pos.Z -= 0.25F; + } + + return pos; + } + else + { + // We don't have a target here, so we're going to raytrace all the objects in the scene. + + EntityIntersection ei = m_sceneGraph.GetClosestIntersectingPrim(new Ray(AXOrigin, AXdirection), true, false); + + // Un-comment the following line to print the raytrace results to the console. + //m_log.Info("[RAYTRACERESULTS]: Hit:" + ei.HitTF.ToString() + " Point: " + ei.ipoint.ToString() + " Normal: " + ei.normal.ToString()); + + if (ei.HitTF) + { + pos = new Vector3(ei.ipoint.X, ei.ipoint.Y, ei.ipoint.Z); + } else + { + // fall back to our stupid functionality + pos = RayEnd; + } + + return pos; + } + } + else + { + // fall back to our stupid functionality + pos = RayEnd; + return pos; + } + } + + public virtual void AddNewPrim(UUID ownerID, UUID groupID, Vector3 RayEnd, Quaternion rot, PrimitiveBaseShape shape, + byte bypassRaycast, Vector3 RayStart, UUID RayTargetID, + byte RayEndIsIntersection) + { + Vector3 pos = GetNewRezLocation(RayStart, RayEnd, RayTargetID, rot, bypassRaycast, RayEndIsIntersection, true, new Vector3(0.5f, 0.5f, 0.5f), false); + + if (Permissions.CanRezObject(1, ownerID, pos)) + { + // rez ON the ground, not IN the ground + pos.Z += 0.25F; + + AddNewPrim(ownerID, groupID, pos, rot, shape); + } + } + + public virtual SceneObjectGroup AddNewPrim( + UUID ownerID, UUID groupID, Vector3 pos, Quaternion rot, PrimitiveBaseShape shape) + { + //m_log.DebugFormat( + // "[SCENE]: Scene.AddNewPrim() pcode {0} called for {1} in {2}", shape.PCode, ownerID, RegionInfo.RegionName); + + // If an entity creator has been registered for this prim type then use that + if (m_entityCreators.ContainsKey((PCode)shape.PCode)) + return m_entityCreators[(PCode)shape.PCode].CreateEntity(ownerID, groupID, pos, rot, shape); + + // Otherwise, use this default creation code; + SceneObjectGroup sceneObject = new SceneObjectGroup(ownerID, pos, rot, shape); + AddNewSceneObject(sceneObject, true); + sceneObject.SetGroup(groupID, null); + + return sceneObject; + } + + /// + /// Add an object into the scene that has come from storage + /// + /// + /// + /// + /// If true, changes to the object will be reflected in its persisted data + /// If false, the persisted data will not be changed even if the object in the scene is changed + /// + /// + /// If true, we won't persist this object until it changes + /// If false, we'll persist this object immediately + /// + /// + /// true if the object was added, false if an object with the same uuid was already in the scene + /// + public bool AddRestoredSceneObject( + SceneObjectGroup sceneObject, bool attachToBackup, bool alreadyPersisted) + { + return m_sceneGraph.AddRestoredSceneObject(sceneObject, attachToBackup, alreadyPersisted); + } + + /// + /// Add a newly created object to the scene + /// + /// + /// + /// If true, the object is made persistent into the scene. + /// If false, the object will not persist over server restarts + /// + public bool AddNewSceneObject(SceneObjectGroup sceneObject, bool attachToBackup) + { + return m_sceneGraph.AddNewSceneObject(sceneObject, attachToBackup); + } + + /// + /// Delete every object from the scene + /// + public void DeleteAllSceneObjects() + { + lock (Entities) + { + ICollection entities = new List(Entities); + + foreach (EntityBase e in entities) + { + if (e is SceneObjectGroup) + DeleteSceneObject((SceneObjectGroup)e, false); + } + } + } + + /// + /// Synchronously delete the given object from the scene. + /// + /// Object Id + /// Suppress broadcasting changes to other clients. + public void DeleteSceneObject(SceneObjectGroup group, bool silent) + { + //SceneObjectPart rootPart = group.GetChildPart(group.UUID); + + // Serialise calls to RemoveScriptInstances to avoid + // deadlocking on m_parts inside SceneObjectGroup + lock (m_deleting_scene_object) + { + group.RemoveScriptInstances(); + } + + foreach (SceneObjectPart part in group.Children.Values) + { + if (part.IsJoint() && ((part.ObjectFlags&(uint)PrimFlags.Physics) != 0) ) + { + PhysicsScene.RequestJointDeletion(part.Name); // FIXME: what if the name changed? + } + else if (part.PhysActor != null) + { + PhysicsScene.RemovePrim(part.PhysActor); + part.PhysActor = null; + } + } +// if (rootPart.PhysActor != null) +// { +// PhysicsScene.RemovePrim(rootPart.PhysActor); +// rootPart.PhysActor = null; +// } + + if (UnlinkSceneObject(group.UUID, false)) + { + EventManager.TriggerObjectBeingRemovedFromScene(group); + EventManager.TriggerParcelPrimCountTainted(); + } + + group.DeleteGroup(silent); + } + + /// + /// Unlink the given object from the scene. Unlike delete, this just removes the record of the object - the + /// object itself is not destroyed. + /// + /// Id of object. + /// true if the object was in the scene, false if it was not + /// If true, only deletes from scene, but keeps object in database. + public bool UnlinkSceneObject(UUID uuid, bool softDelete) + { + if (m_sceneGraph.DeleteSceneObject(uuid, softDelete)) + { + if (!softDelete) + { + m_storageManager.DataStore.RemoveObject(uuid, + m_regInfo.RegionID); + } + + return true; + } + + return false; + } + + /// + /// Move the given scene object into a new region depending on which region its absolute position has moved + /// into. + /// + /// This method locates the new region handle and offsets the prim position for the new region + /// + /// the attempted out of region position of the scene object + /// the scene object that we're crossing + public void CrossPrimGroupIntoNewRegion(Vector3 attemptedPosition, SceneObjectGroup grp, bool silent) + { + if (grp == null) + return; + if (grp.IsDeleted) + return; + + if (grp.RootPart.DIE_AT_EDGE) + { + // We remove the object here + try + { + DeleteSceneObject(grp, false); + } + catch (Exception) + { + m_log.Warn("[DATABASE]: exception when trying to remove the prim that crossed the border."); + } + return; + } + + int thisx = (int)RegionInfo.RegionLocX; + int thisy = (int)RegionInfo.RegionLocY; + + ulong newRegionHandle = 0; + Vector3 pos = attemptedPosition; + + if (attemptedPosition.X > Constants.RegionSize + 0.1f) + { + pos.X = ((pos.X - Constants.RegionSize)); + newRegionHandle + = Util.UIntsToLong((uint)((thisx + 1) * Constants.RegionSize), (uint)(thisy * Constants.RegionSize)); + // x + 1 + } + else if (attemptedPosition.X < -0.1f) + { + pos.X = ((pos.X + Constants.RegionSize)); + newRegionHandle + = Util.UIntsToLong((uint)((thisx - 1) * Constants.RegionSize), (uint)(thisy * Constants.RegionSize)); + // x - 1 + } + + if (attemptedPosition.Y > Constants.RegionSize + 0.1f) + { + pos.Y = ((pos.Y - Constants.RegionSize)); + newRegionHandle + = Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy + 1) * Constants.RegionSize)); + // y + 1 + } + else if (attemptedPosition.Y < -0.1f) + { + pos.Y = ((pos.Y + Constants.RegionSize)); + newRegionHandle + = Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy - 1) * Constants.RegionSize)); + // y - 1 + } + + // Offset the positions for the new region across the border + Vector3 oldGroupPosition = grp.RootPart.GroupPosition; + grp.OffsetForNewRegion(pos); + + // If we fail to cross the border, then reset the position of the scene object on that border. + if (!CrossPrimGroupIntoNewRegion(newRegionHandle, grp, silent)) + { + grp.OffsetForNewRegion(oldGroupPosition); + grp.ScheduleGroupForFullUpdate(); + } + } + + /// + /// Move the given scene object into a new region + /// + /// + /// Scene Object Group that we're crossing + /// + /// true if the crossing itself was successful, false on failure + /// FIMXE: we still return true if the crossing object was not successfully deleted from the originating region + /// + public bool CrossPrimGroupIntoNewRegion(ulong newRegionHandle, SceneObjectGroup grp, bool silent) + { + bool successYN = false; + grp.RootPart.UpdateFlag = 0; + int primcrossingXMLmethod = 0; + + if (newRegionHandle != 0) + { + string objectState = grp.GetStateSnapshot(); + + successYN + = m_sceneGridService.PrimCrossToNeighboringRegion( + newRegionHandle, grp.UUID, m_serialiser.SaveGroupToXml2(grp), primcrossingXMLmethod); + if (successYN && (objectState != "") && m_allowScriptCrossings) + { + successYN = m_sceneGridService.PrimCrossToNeighboringRegion( + newRegionHandle, grp.UUID, objectState, 100); + } + + if (successYN) + { + // We remove the object here + try + { + DeleteSceneObject(grp, silent); + } + catch (Exception e) + { + m_log.ErrorFormat( + "[INTERREGION]: Exception deleting the old object left behind on a border crossing for {0}, {1}", + grp, e); + } + } + else + { + if (!grp.IsDeleted) + { + if (grp.RootPart.PhysActor != null) + { + grp.RootPart.PhysActor.CrossingFailure(); + } + } + + m_log.ErrorFormat("[INTERREGION]: Prim crossing failed for {0}", grp); + } + } + else + { + m_log.Error("[INTERREGION]: region handle was unexpectedly 0 in Scene.CrossPrimGroupIntoNewRegion()"); + } + + return successYN; + } + + /// + /// Handle a scene object that is crossing into this region from another. + /// + /// + /// + /// + /// + /// + public bool IncomingInterRegionPrimGroup(UUID primID, string objXMLData, int XMLMethod) + { + + if (XMLMethod == 0) + { + m_log.DebugFormat("[INTERREGION]: A new prim {0} arrived from a neighbor", primID); + SceneObjectGroup sceneObject = m_serialiser.DeserializeGroupFromXml2(objXMLData); + + // If the user is banned, we won't let any of their objects + // enter. Period. + // + if (m_regInfo.EstateSettings.IsBanned(sceneObject.OwnerID)) + { + m_log.Info("[INTERREGION]: Denied prim crossing for "+ + "banned avatar"); + + return false; + } + + // Force allocation of new LocalId + // + foreach (SceneObjectPart p in sceneObject.Children.Values) + p.LocalId = 0; + + if (sceneObject.RootPart.Shape.PCode == (byte)PCode.Prim) + { + if (sceneObject.RootPart.Shape.State != 0) + { + // Fix up attachment Parent Local ID + // + ScenePresence sp = GetScenePresence(sceneObject.OwnerID); + + uint parentLocalID = 0; + if (sp != null) + parentLocalID = sp.LocalId; + + sceneObject.RootPart.IsAttachment = true; + sceneObject.RootPart.SetParentLocalId(parentLocalID); + + AddRestoredSceneObject(sceneObject, false, false); + + // Handle attachment special case + // + SceneObjectPart RootPrim = GetSceneObjectPart(primID); + + if (RootPrim != null) + { + SceneObjectGroup grp = RootPrim.ParentGroup; + + RootPrim.SetParentLocalId(parentLocalID); + + if (grp != null) + { + m_log.DebugFormat("[ATTACHMENT]: Received "+ + "attachment {0}, inworld asset id {1}", + grp.RootPart.LastOwnerID.ToString(), + grp.UUID.ToString()); + + if (sp != null) + { + grp.SetFromAssetID(grp.RootPart.LastOwnerID); + m_log.DebugFormat("[ATTACHMENT]: Attach "+ + "to avatar {0}", + sp.UUID.ToString()); + AttachObject(sp.ControllingClient, + grp.LocalId, (uint)0, + grp.GroupRotation, + grp.AbsolutePosition, false); + grp.SendGroupFullUpdate(); + } + else + { + RootPrim.RemFlag(PrimFlags.TemporaryOnRez); + RootPrim.AddFlag(PrimFlags.TemporaryOnRez); + } + } + } + } + else + { + AddRestoredSceneObject(sceneObject, true, false); + + if (!Permissions.CanObjectEntry(sceneObject.UUID, + true, sceneObject.AbsolutePosition)) + { + // Deny non attachments based on parcel settings + // + m_log.Info("[INTERREGION]: Denied prim crossing "+ + "because of parcel settings"); + + DeleteSceneObject(sceneObject, false); + + return false; + } + } + } + } + else if ((XMLMethod == 100) && m_allowScriptCrossings) + { + m_log.Warn("[INTERREGION]: Prim state data arrived from a neighbor"); + XmlDocument doc = new XmlDocument(); + doc.LoadXml(objXMLData); + + XmlNodeList rootL = doc.GetElementsByTagName("ScriptData"); + if (rootL.Count == 1) + { + XmlNode rootNode = rootL[0]; + if (rootNode != null) + { + XmlNodeList partL = rootNode.ChildNodes; + + foreach (XmlNode part in partL) + { + XmlNodeList nodeL = part.ChildNodes; + + switch (part.Name) + { + case "Assemblies": + foreach (XmlNode asm in nodeL) + { + string fn = asm.Attributes.GetNamedItem("Filename").Value; + + Byte[] filedata = Convert.FromBase64String(asm.InnerText); + string path = Path.Combine("ScriptEngines", RegionInfo.RegionID.ToString()); + path = Path.Combine(path, fn); + + if (!File.Exists(path)) + { + FileStream fs = File.Create(path); + fs.Write(filedata, 0, filedata.Length); + fs.Close(); + } + } + break; + case "ScriptStates": + foreach (XmlNode st in nodeL) + { + string id = st.Attributes.GetNamedItem("UUID").Value; + UUID uuid = new UUID(id); + XmlNode state = st.ChildNodes[0]; + + XmlDocument sdoc = new XmlDocument(); + XmlNode sxmlnode = sdoc.CreateNode( + XmlNodeType.XmlDeclaration, + "", ""); + sdoc.AppendChild(sxmlnode); + + XmlNode newnode = sdoc.ImportNode(state, true); + sdoc.AppendChild(newnode); + + string spath = Path.Combine("ScriptEngines", RegionInfo.RegionID.ToString()); + spath = Path.Combine(spath, uuid.ToString()); + FileStream sfs = File.Create(spath + ".state"); + System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); + Byte[] buf = enc.GetBytes(sdoc.InnerXml); + sfs.Write(buf, 0, buf.Length); + sfs.Close(); + } + break; + } + } + } + } + + SceneObjectPart RootPrim = GetSceneObjectPart(primID); + RootPrim.ParentGroup.CreateScriptInstances(0, false, DefaultScriptEngine, 1); + + return true; + } + + return true; + } + + #endregion + + #region Add/Remove Avatar Methods + + public override void AddNewClient(IClientAPI client) + { + SubscribeToClientEvents(client); + ScenePresence presence; + + if (m_restorePresences.ContainsKey(client.AgentId)) + { + m_log.DebugFormat("[SCENE]: Restoring agent {0} {1} in {2}", client.Name, client.AgentId, RegionInfo.RegionName); + + presence = m_restorePresences[client.AgentId]; + m_restorePresences.Remove(client.AgentId); + + // This is one of two paths to create avatars that are + // used. This tends to get called more in standalone + // than grid, not really sure why, but as such needs + // an explicity appearance lookup here. + AvatarAppearance appearance = null; + GetAvatarAppearance(client, out appearance); + presence.Appearance = appearance; + + presence.initializeScenePresence(client, RegionInfo, this); + + m_sceneGraph.AddScenePresence(presence); + + lock (m_restorePresences) + { + Monitor.PulseAll(m_restorePresences); + } + } + else + { + m_log.DebugFormat( + "[SCENE]: Adding new child agent for {0} in {1}", + client.Name, RegionInfo.RegionName); + + CommsManager.UserProfileCacheService.AddNewUser(client.AgentId); + + CreateAndAddScenePresence(client); + } + + m_LastLogin = System.Environment.TickCount; + EventManager.TriggerOnNewClient(client); + } + + protected virtual void SubscribeToClientEvents(IClientAPI client) + { + client.OnRegionHandShakeReply += SendLayerData; + client.OnAddPrim += AddNewPrim; + client.OnUpdatePrimGroupPosition += m_sceneGraph.UpdatePrimPosition; + client.OnUpdatePrimSinglePosition += m_sceneGraph.UpdatePrimSinglePosition; + client.OnUpdatePrimGroupRotation += m_sceneGraph.UpdatePrimRotation; + client.OnUpdatePrimGroupMouseRotation += m_sceneGraph.UpdatePrimRotation; + client.OnUpdatePrimSingleRotation += m_sceneGraph.UpdatePrimSingleRotation; + client.OnUpdatePrimScale += m_sceneGraph.UpdatePrimScale; + client.OnUpdatePrimGroupScale += m_sceneGraph.UpdatePrimGroupScale; + client.OnUpdateExtraParams += m_sceneGraph.UpdateExtraParam; + client.OnUpdatePrimShape += m_sceneGraph.UpdatePrimShape; + client.OnUpdatePrimTexture += m_sceneGraph.UpdatePrimTexture; + client.OnTeleportLocationRequest += RequestTeleportLocation; + client.OnTeleportLandmarkRequest += RequestTeleportLandmark; + client.OnObjectSelect += SelectPrim; + client.OnObjectDeselect += DeselectPrim; + client.OnGrabUpdate += m_sceneGraph.MoveObject; + client.OnDeRezObject += DeRezObject; + client.OnRezObject += RezObject; + client.OnRezSingleAttachmentFromInv += RezSingleAttachment; + client.OnDetachAttachmentIntoInv += DetachSingleAttachmentToInv; + client.OnObjectAttach += m_sceneGraph.AttachObject; + client.OnObjectDetach += m_sceneGraph.DetachObject; + client.OnObjectDrop += m_sceneGraph.DropObject; + client.OnNameFromUUIDRequest += CommsManager.HandleUUIDNameRequest; + client.OnObjectDescription += m_sceneGraph.PrimDescription; + client.OnObjectName += m_sceneGraph.PrimName; + client.OnObjectClickAction += m_sceneGraph.PrimClickAction; + client.OnObjectMaterial += m_sceneGraph.PrimMaterial; + client.OnLinkObjects += m_sceneGraph.LinkObjects; + client.OnDelinkObjects += m_sceneGraph.DelinkObjects; + client.OnObjectDuplicate += m_sceneGraph.DuplicateObject; + client.OnObjectDuplicateOnRay += doObjectDuplicateOnRay; + client.OnUpdatePrimFlags += m_sceneGraph.UpdatePrimFlags; + client.OnRequestObjectPropertiesFamily += m_sceneGraph.RequestObjectPropertiesFamily; + client.OnRequestGodlikePowers += handleRequestGodlikePowers; + client.OnGodKickUser += HandleGodlikeKickUser; + client.OnObjectPermissions += HandleObjectPermissionsUpdate; + client.OnCreateNewInventoryItem += CreateNewInventoryItem; + client.OnCreateNewInventoryFolder += HandleCreateInventoryFolder; + client.OnUpdateInventoryFolder += HandleUpdateInventoryFolder; + client.OnMoveInventoryFolder += HandleMoveInventoryFolder; + client.OnFetchInventoryDescendents += HandleFetchInventoryDescendents; + client.OnPurgeInventoryDescendents += HandlePurgeInventoryDescendents; + client.OnFetchInventory += HandleFetchInventory; + client.OnUpdateInventoryItem += UpdateInventoryItemAsset; + client.OnCopyInventoryItem += CopyInventoryItem; + client.OnMoveInventoryItem += MoveInventoryItem; + client.OnRemoveInventoryItem += RemoveInventoryItem; + client.OnRemoveInventoryFolder += RemoveInventoryFolder; + client.OnRezScript += RezScript; + client.OnRequestTaskInventory += RequestTaskInventory; + client.OnRemoveTaskItem += RemoveTaskInventory; + client.OnUpdateTaskInventory += UpdateTaskInventory; + client.OnMoveTaskItem += ClientMoveTaskInventoryItem; + client.OnGrabObject += ProcessObjectGrab; + client.OnDeGrabObject += ProcessObjectDeGrab; + client.OnMoneyTransferRequest += ProcessMoneyTransferRequest; + client.OnParcelBuy += ProcessParcelBuy; + client.OnAvatarPickerRequest += ProcessAvatarPickerRequest; + client.OnObjectIncludeInSearch += m_sceneGraph.MakeObjectSearchable; + client.OnTeleportHomeRequest += TeleportClientHome; + client.OnSetStartLocationRequest += SetHomeRezPoint; + client.OnUndo += m_sceneGraph.HandleUndo; + client.OnObjectGroupRequest += m_sceneGraph.HandleObjectGroupUpdate; + client.OnParcelReturnObjectsRequest += LandChannel.ReturnObjectsInParcel; + client.OnParcelSetOtherCleanTime += LandChannel.SetParcelOtherCleanTime; + client.OnObjectSaleInfo += ObjectSaleInfo; + client.OnScriptReset += ProcessScriptReset; + client.OnGetScriptRunning += GetScriptRunning; + client.OnSetScriptRunning += SetScriptRunning; + client.OnRegionHandleRequest += RegionHandleRequest; + client.OnUnackedTerrain += TerrainUnAcked; + + client.OnObjectOwner += ObjectOwner; + + if (StatsReporter != null) + client.OnNetworkStatsUpdate += StatsReporter.AddPacketsFromClientStats; + + // EventManager.TriggerOnNewClient(client); + } + + /// + /// Teleport an avatar to their home region + /// + /// + /// + public virtual void TeleportClientHome(UUID agentId, IClientAPI client) + { + UserProfileData UserProfile = CommsManager.UserService.GetUserProfile(agentId); + if (UserProfile != null) + { + RegionInfo regionInfo = CommsManager.GridService.RequestNeighbourInfo(UserProfile.HomeRegionID); + if (regionInfo == null) + { + regionInfo = CommsManager.GridService.RequestNeighbourInfo(UserProfile.HomeRegion); + if (regionInfo != null) // home region can be away temporarily, too + { + UserProfile.HomeRegionID = regionInfo.RegionID; + CommsManager.UserService.UpdateUserProfile(UserProfile); + } + } + if (regionInfo == null) + { + // can't find the Home region: Tell viewer and abort + client.SendTeleportFailed("Your home-region could not be found."); + return; + } + RequestTeleportLocation( + client, regionInfo.RegionHandle, UserProfile.HomeLocation, UserProfile.HomeLookAt, + (uint)(TPFlags.SetLastToTarget | TPFlags.ViaHome)); + } + } + + public void doObjectDuplicateOnRay(uint localID, uint dupeFlags, UUID AgentID, UUID GroupID, + UUID RayTargetObj, Vector3 RayEnd, Vector3 RayStart, + bool BypassRaycast, bool RayEndIsIntersection, bool CopyCenters, bool CopyRotates) + { + Vector3 pos; + const bool frontFacesOnly = true; + //m_log.Info("HITTARGET: " + RayTargetObj.ToString() + ", COPYTARGET: " + localID.ToString()); + SceneObjectPart target = GetSceneObjectPart(localID); + SceneObjectPart target2 = GetSceneObjectPart(RayTargetObj); + + if (target != null && target2 != null) + { + Vector3 direction = Vector3.Normalize(RayEnd - RayStart); + Vector3 AXOrigin = new Vector3(RayStart.X, RayStart.Y, RayStart.Z); + Vector3 AXdirection = new Vector3(direction.X, direction.Y, direction.Z); + + if (target2.ParentGroup != null) + { + pos = target2.AbsolutePosition; + //m_log.Info("[OBJECT_REZ]: TargetPos: " + pos.ToString() + ", RayStart: " + RayStart.ToString() + ", RayEnd: " + RayEnd.ToString() + ", Volume: " + Util.GetDistanceTo(RayStart,RayEnd).ToString() + ", mag1: " + Util.GetMagnitude(RayStart).ToString() + ", mag2: " + Util.GetMagnitude(RayEnd).ToString()); + + // TODO: Raytrace better here + + //EntityIntersection ei = m_sceneGraph.GetClosestIntersectingPrim(new Ray(AXOrigin, AXdirection)); + Ray NewRay = new Ray(AXOrigin, AXdirection); + + // Ray Trace against target here + EntityIntersection ei = target2.TestIntersectionOBB(NewRay, Quaternion.Identity, frontFacesOnly, CopyCenters); + + // Un-comment out the following line to Get Raytrace results printed to the console. + //m_log.Info("[RAYTRACERESULTS]: Hit:" + ei.HitTF.ToString() + " Point: " + ei.ipoint.ToString() + " Normal: " + ei.normal.ToString()); + float ScaleOffset = 0.5f; + + // If we hit something + if (ei.HitTF) + { + Vector3 scale = target.Scale; + Vector3 scaleComponent = new Vector3(ei.AAfaceNormal.X, ei.AAfaceNormal.Y, ei.AAfaceNormal.Z); + if (scaleComponent.X != 0) ScaleOffset = scale.X; + if (scaleComponent.Y != 0) ScaleOffset = scale.Y; + if (scaleComponent.Z != 0) ScaleOffset = scale.Z; + ScaleOffset = Math.Abs(ScaleOffset); + Vector3 intersectionpoint = new Vector3(ei.ipoint.X, ei.ipoint.Y, ei.ipoint.Z); + Vector3 normal = new Vector3(ei.normal.X, ei.normal.Y, ei.normal.Z); + Vector3 offset = normal * (ScaleOffset / 2f); + pos = intersectionpoint + offset; + + // stick in offset format from the original prim + pos = pos - target.ParentGroup.AbsolutePosition; + if (CopyRotates) + { + Quaternion worldRot = target2.GetWorldRotation(); + + // SceneObjectGroup obj = m_sceneGraph.DuplicateObject(localID, pos, target.GetEffectiveObjectFlags(), AgentID, GroupID, worldRot); + m_sceneGraph.DuplicateObject(localID, pos, target.GetEffectiveObjectFlags(), AgentID, GroupID, worldRot); + //obj.Rotation = worldRot; + //obj.UpdateGroupRotation(worldRot); + } + else + { + m_sceneGraph.DuplicateObject(localID, pos, target.GetEffectiveObjectFlags(), AgentID, GroupID); + } + } + + return; + } + + return; + } + } + + public virtual void SetHomeRezPoint(IClientAPI remoteClient, ulong regionHandle, Vector3 position, Vector3 lookAt, uint flags) + { + UserProfileData UserProfile = CommsManager.UserService.GetUserProfile(remoteClient.AgentId); + if (UserProfile != null) + { + // I know I'm ignoring the regionHandle provided by the teleport location request. + // reusing the TeleportLocationRequest delegate, so regionHandle isn't valid + UserProfile.HomeRegionID = RegionInfo.RegionID; + // TODO: The next line can be removed, as soon as only homeRegionID based UserServers are around. + // TODO: The HomeRegion property can be removed then, too + UserProfile.HomeRegion = RegionInfo.RegionHandle; + UserProfile.HomeLocation = position; + UserProfile.HomeLookAt = lookAt; + CommsManager.UserService.UpdateUserProfile(UserProfile); + + // FUBAR ALERT: this needs to be "Home position set." so the viewer saves a home-screenshot. + m_dialogModule.SendAlertToUser(remoteClient, "Home position set."); + } + else + { + m_dialogModule.SendAlertToUser(remoteClient, "Set Home request Failed."); + } + } + + /// + /// Create a child agent scene presence and add it to this scene. + /// + /// + /// + protected virtual ScenePresence CreateAndAddScenePresence(IClientAPI client) + { + AvatarAppearance appearance = null; + GetAvatarAppearance(client, out appearance); + + ScenePresence avatar = m_sceneGraph.CreateAndAddChildScenePresence(client, appearance); + //avatar.KnownRegions = GetChildrenSeeds(avatar.UUID); + return avatar; + } + + /// + /// Get the avatar apperance for the given client. + /// + /// + /// + public void GetAvatarAppearance(IClientAPI client, out AvatarAppearance appearance) + { + appearance = new AvatarAppearance(); + + try + { + if (m_AvatarFactory != null) + { + if (m_AvatarFactory.TryGetAvatarAppearance(client.AgentId, out appearance)) + return; + } + } + catch (Exception e) + { + m_log.ErrorFormat("[APPEARANCE]: Problem fetching appearance for avatar {0}, {1}", + client.Name, e); + } + + m_log.Warn("[APPEARANCE]: Appearance not found, returning default"); + } + + /// + /// Remove the given client from the scene. + /// + /// + public override void RemoveClient(UUID agentID) + { + bool childagentYN = false; + ScenePresence avatar = GetScenePresence(agentID); + if (avatar != null) + { + childagentYN = avatar.IsChildAgent; + } + + try + { + m_log.DebugFormat( + "[SCENE]: Removing {0} agent {1} from region {2}", + (childagentYN ? "child" : "root"), agentID, RegionInfo.RegionName); + + m_sceneGraph.removeUserCount(!childagentYN); + CapsModule.RemoveCapsHandler(agentID); + + if (avatar.Scene.NeedSceneCacheClear(avatar.UUID)) + { + CommsManager.UserProfileCacheService.RemoveUser(agentID); + } + + if (!avatar.IsChildAgent) + { + m_sceneGridService.LogOffUser(agentID, RegionInfo.RegionID, RegionInfo.RegionHandle, avatar.AbsolutePosition, avatar.Lookat); + //List childknownRegions = new List(); + //List ckn = avatar.KnownChildRegionHandles; + //for (int i = 0; i < ckn.Count; i++) + //{ + // childknownRegions.Add(ckn[i]); + //} + List regions = new List(avatar.KnownChildRegionHandles); + regions.Remove(RegionInfo.RegionHandle); + m_sceneGridService.SendCloseChildAgentConnections(agentID, regions); + + } + m_eventManager.TriggerClientClosed(agentID); + } + catch (NullReferenceException) + { + // We don't know which count to remove it from + // Avatar is already disposed :/ + } + + m_eventManager.TriggerOnRemovePresence(agentID); + Broadcast(delegate(IClientAPI client) + { + try + { + client.SendKillObject(avatar.RegionHandle, avatar.LocalId); + } + catch (NullReferenceException) + { + //We can safely ignore null reference exceptions. It means the avatar are dead and cleaned up anyway. + } + }); + + ForEachScenePresence( + delegate(ScenePresence presence) { presence.CoarseLocationChange(); }); + + IAgentAssetTransactions agentTransactions = this.RequestModuleInterface(); + if (agentTransactions != null) + { + agentTransactions.RemoveAgentAssetTransactions(agentID); + } + + m_sceneGraph.RemoveScenePresence(agentID); + + try + { + avatar.Close(); + } + catch (NullReferenceException) + { + //We can safely ignore null reference exceptions. It means the avatar are dead and cleaned up anyway. + } + catch (Exception e) + { + m_log.Error("[SCENE] Scene.cs:RemoveClient exception: " + e.ToString()); + } + + // Remove client agent from profile, so new logins will work + if (!childagentYN) + { + m_sceneGridService.ClearUserAgent(agentID); + } + + //m_log.InfoFormat("[SCENE] Memory pre GC {0}", System.GC.GetTotalMemory(false)); + //m_log.InfoFormat("[SCENE] Memory post GC {0}", System.GC.GetTotalMemory(true)); + } + + public void HandleRemoveKnownRegionsFromAvatar(UUID avatarID, List regionslst) + { + ScenePresence av = GetScenePresence(avatarID); + if (av != null) + { + lock (av) + { + + for (int i = 0; i < regionslst.Count; i++) + { + av.KnownChildRegionHandles.Remove(regionslst[i]); + } + } + } + } + + public override void CloseAllAgents(uint circuitcode) + { + // Called by ClientView to kill all circuit codes + ClientManager.CloseAllAgents(circuitcode); + } + + public void NotifyMyCoarseLocationChange() + { + ForEachScenePresence(delegate(ScenePresence presence) { presence.CoarseLocationChange(); }); + } + + #endregion + + #region Entities + + public void SendKillObject(uint localID) + { + SceneObjectPart part = GetSceneObjectPart(localID); + if (part != null) // It is a prim + { + if (part.ParentGroup != null && !part.ParentGroup.IsDeleted) // Valid + { + if (part.ParentGroup.RootPart != part) // Child part + return; + } + } + Broadcast(delegate(IClientAPI client) { client.SendKillObject(m_regionHandle, localID); }); + } + + #endregion + + #region RegionComms + + /// + /// Register the methods that should be invoked when this scene receives various incoming events + /// + public void RegisterCommsEvents() + { + m_sceneGridService.OnExpectUser += NewUserConnection; + m_sceneGridService.OnAvatarCrossingIntoRegion += AgentCrossing; + m_sceneGridService.OnCloseAgentConnection += IncomingCloseAgent; + m_sceneGridService.OnRegionUp += OtherRegionUp; + //m_sceneGridService.OnChildAgentUpdate += IncomingChildAgentDataUpdate; + m_sceneGridService.OnExpectPrim += IncomingInterRegionPrimGroup; + //m_sceneGridService.OnRemoveKnownRegionFromAvatar += HandleRemoveKnownRegionsFromAvatar; + m_sceneGridService.OnLogOffUser += HandleLogOffUserFromGrid; + m_sceneGridService.KiPrimitive += SendKillObject; + m_sceneGridService.OnGetLandData += GetLandData; + + if (m_interregionCommsIn != null) + { + m_log.Debug("[SCENE]: Registering with InterregionCommsIn"); + m_interregionCommsIn.OnChildAgentUpdate += IncomingChildAgentDataUpdate; + } + else + m_log.Debug("[SCENE]: Unable to register with InterregionCommsIn"); + + } + + /// + /// Deregister this scene from receiving incoming region events + /// + public void UnRegisterRegionWithComms() + { + m_sceneGridService.KiPrimitive -= SendKillObject; + m_sceneGridService.OnLogOffUser -= HandleLogOffUserFromGrid; + //m_sceneGridService.OnRemoveKnownRegionFromAvatar -= HandleRemoveKnownRegionsFromAvatar; + m_sceneGridService.OnExpectPrim -= IncomingInterRegionPrimGroup; + //m_sceneGridService.OnChildAgentUpdate -= IncomingChildAgentDataUpdate; + m_sceneGridService.OnRegionUp -= OtherRegionUp; + m_sceneGridService.OnExpectUser -= NewUserConnection; + m_sceneGridService.OnAvatarCrossingIntoRegion -= AgentCrossing; + m_sceneGridService.OnCloseAgentConnection -= IncomingCloseAgent; + m_sceneGridService.OnGetLandData -= GetLandData; + + if (m_interregionCommsIn != null) + m_interregionCommsIn.OnChildAgentUpdate -= IncomingChildAgentDataUpdate; + + m_sceneGridService.Close(); + } + + /// + /// Do the work necessary to initiate a new user connection for a particular scene. + /// At the moment, this consists of setting up the caps infrastructure + /// + /// + /// + public void NewUserConnection(AgentCircuitData agent) + { + CapsModule.NewUserConnection(agent); + + ScenePresence sp = m_sceneGraph.GetScenePresence(agent.AgentID); + if (sp != null) + { + m_log.DebugFormat( + "[SCENE]: Adjusting known seeds for existing agent {0} in {1}", + agent.AgentID, RegionInfo.RegionName); + + sp.AdjustKnownSeeds(); + + return; + } + + // Don't disable this log message - it's too helpful + m_log.DebugFormat( + "[CONNECTION BEGIN]: Region {0} told of incoming client {1} {2} {3} (circuit code {4})", + RegionInfo.RegionName, agent.firstname, agent.lastname, agent.AgentID, agent.circuitcode); + + if (m_regInfo.EstateSettings.IsBanned(agent.AgentID)) + { + m_log.WarnFormat( + "[CONNECTION BEGIN]: Denied access to: {0} at {1} because the user is on the region banlist", + agent.AgentID, RegionInfo.RegionName); + } + + CapsModule.AddCapsHandler(agent.AgentID); + + if (!agent.child) + { + // Honor parcel landing type and position. + ILandObject land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y); + if (land != null) + { + if (land.landData.LandingType == (byte)1 && land.landData.UserLocation != Vector3.Zero) + { + agent.startpos = land.landData.UserLocation; + } + } + } + + m_authenticateHandler.AddNewCircuit(agent.circuitcode, agent); + + // rewrite session_id + CachedUserInfo userinfo = CommsManager.UserProfileCacheService.GetUserDetails(agent.AgentID); + + if (userinfo != null) + { + userinfo.SessionID = agent.SessionID; + } + else + { + m_log.WarnFormat( + "[CONNECTION BEGIN]: We couldn't find a User Info record for {0}. This is usually an indication that the UUID we're looking up is invalid", agent.AgentID); + } + } + + public void UpdateCircuitData(AgentCircuitData data) + { + m_authenticateHandler.UpdateAgentData(data); + } + + public bool ChangeCircuitCode(uint oldcc, uint newcc) + { + return m_authenticateHandler.TryChangeCiruitCode(oldcc, newcc); + } + + protected void HandleLogOffUserFromGrid(UUID AvatarID, UUID RegionSecret, string message) + { + ScenePresence loggingOffUser = null; + loggingOffUser = GetScenePresence(AvatarID); + if (loggingOffUser != null) + { + UUID localRegionSecret = UUID.Zero; + bool parsedsecret = UUID.TryParse(m_regInfo.regionSecret, out localRegionSecret); + + // Region Secret is used here in case a new sessionid overwrites an old one on the user server. + // Will update the user server in a few revisions to use it. + + if (RegionSecret == loggingOffUser.ControllingClient.SecureSessionId || (parsedsecret && RegionSecret == localRegionSecret)) + { + m_sceneGridService.SendCloseChildAgentConnections(loggingOffUser.UUID, new List(loggingOffUser.KnownRegions.Keys)); + loggingOffUser.ControllingClient.Kick(message); + // Give them a second to receive the message! + System.Threading.Thread.Sleep(1000); + loggingOffUser.ControllingClient.Close(true); + } + else + { + m_log.Info("[USERLOGOFF]: System sending the LogOff user message failed to sucessfully authenticate"); + } + } + else + { + m_log.InfoFormat("[USERLOGOFF]: Got a logoff request for {0} but the user isn't here. The user might already have been logged out", AvatarID.ToString()); + } + } + + /// + /// Triggered when an agent crosses into this sim. Also happens on initial login. + /// + /// + /// + /// + public virtual void AgentCrossing(UUID agentID, Vector3 position, bool isFlying) + { + ScenePresence presence; + + lock (m_scenePresences) + { + m_scenePresences.TryGetValue(agentID, out presence); + } + + if (presence != null) + { + try + { + presence.MakeRootAgent(position, isFlying); + } + catch (Exception e) + { + m_log.ErrorFormat("[SCENE]: Unable to do agent crossing, exception {0}", e); + } + } + else + { + m_log.ErrorFormat( + "[SCENE]: Could not find presence for agent {0} crossing into scene {1}", + agentID, RegionInfo.RegionName); + } + } + + public virtual bool IncomingChildAgentDataUpdate(AgentData cAgentData) + { +// m_log.DebugFormat( +// "[SCENE]: Incoming child agent update for {0} in {1}", cAgentData.AgentID, RegionInfo.RegionName); + + // We have to wait until the viewer contacts this region after receiving EAC. + // That calls AddNewClient, which finally creates the ScenePresence + ScenePresence childAgentUpdate = WaitGetScenePresence(cAgentData.AgentID); + if (childAgentUpdate != null) + { + childAgentUpdate.ChildAgentDataUpdate(cAgentData); + return true; + } + + return false; + } + + public virtual bool IncomingChildAgentDataUpdate(AgentPosition cAgentData) + { + //Console.WriteLine(" XXX Scene IncomingChildAgentDataUpdate POSITION in " + RegionInfo.RegionName); + ScenePresence childAgentUpdate = GetScenePresence(cAgentData.AgentID); + if (childAgentUpdate != null) + { + // I can't imagine *yet* why we would get an update if the agent is a root agent.. + // however to avoid a race condition crossing borders.. + if (childAgentUpdate.IsChildAgent) + { + uint rRegionX = (uint)(cAgentData.RegionHandle >> 40); + uint rRegionY = (((uint)(cAgentData.RegionHandle)) >> 8); + uint tRegionX = RegionInfo.RegionLocX; + uint tRegionY = RegionInfo.RegionLocY; + //Send Data to ScenePresence + childAgentUpdate.ChildAgentDataUpdate(cAgentData, tRegionX, tRegionY, rRegionX, rRegionY); + // Not Implemented: + //TODO: Do we need to pass the message on to one of our neighbors? + } + + return true; + } + + return false; + } + + protected virtual ScenePresence WaitGetScenePresence(UUID agentID) + { + int ntimes = 10; + ScenePresence childAgentUpdate = null; + while ((childAgentUpdate = GetScenePresence(agentID)) == null && (ntimes-- > 0)) + Thread.Sleep(1000); + return childAgentUpdate; + + } + + public virtual bool IncomingReleaseAgent(UUID id) + { + return m_sceneGridService.ReleaseAgent(id); + } + + public void SendReleaseAgent(ulong regionHandle, UUID id, string uri) + { + m_interregionCommsOut.SendReleaseAgent(regionHandle, id, uri); + } + + /// + /// Tell a single agent to disconnect from the region. + /// + /// + /// + public bool IncomingCloseAgent(UUID agentID) + { + //m_log.DebugFormat("[SCENE]: Processing incoming close agent for {0}", agentID); + + ScenePresence presence = m_sceneGraph.GetScenePresence(agentID); + if (presence != null) + { + // Nothing is removed here, so down count it as such + if (presence.IsChildAgent) + { + m_sceneGraph.removeUserCount(false); + } + else + { + m_sceneGraph.removeUserCount(true); + } + + // Don't do this to root agents on logout, it's not nice for the viewer + if (presence.IsChildAgent) + { + // Tell a single agent to disconnect from the region. + IEventQueue eq = RequestModuleInterface(); + if (eq != null) + { + eq.DisableSimulator(RegionInfo.RegionHandle, agentID); + } + else + presence.ControllingClient.SendShutdownConnectionNotice(); + } + + presence.ControllingClient.Close(true); + return true; + } + + // Agent not here + return false; + } + + /// + /// Tell neighboring regions about this agent + /// When the regions respond with a true value, + /// tell the agents about the region. + /// + /// We have to tell the regions about the agents first otherwise it'll deny them access + /// + /// + /// + public void InformClientOfNeighbours(ScenePresence presence) + { + m_sceneGridService.EnableNeighbourChildAgents(presence, m_neighbours); + } + + /// + /// Tell a neighboring region about this agent + /// + /// + /// + public void InformClientOfNeighbor(ScenePresence presence, RegionInfo region) + { + m_sceneGridService.InformNeighborChildAgent(presence, region, m_neighbours); + } + + /// + /// Requests information about this region from gridcomms + /// + /// + /// + public RegionInfo RequestNeighbouringRegionInfo(ulong regionHandle) + { + return m_sceneGridService.RequestNeighbouringRegionInfo(regionHandle); + } + + /// + /// Requests textures for map from minimum region to maximum region in world cordinates + /// + /// + /// + /// + /// + /// + public void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY) + { + m_log.InfoFormat("[MAPBLOCK]: {0}-{1}, {2}-{3}", minX, minY, maxX, maxY); + m_sceneGridService.RequestMapBlocks(remoteClient, minX, minY, maxX, maxY); + } + + /// + /// Tries to teleport agent to other region. + /// + /// + /// + /// + /// + /// + public void RequestTeleportLocation(IClientAPI remoteClient, string regionName, Vector3 position, + Vector3 lookat, uint teleportFlags) + { + RegionInfo regionInfo = m_sceneGridService.RequestClosestRegion(regionName); + if (regionInfo == null) + { + // can't find the region: Tell viewer and abort + remoteClient.SendTeleportFailed("The region '" + regionName + "' could not be found."); + return; + } + + RequestTeleportLocation(remoteClient, regionInfo.RegionHandle, position, lookat, teleportFlags); + } + + /// + /// Tries to teleport agent to other region. + /// + /// + /// + /// + /// + /// + public void RequestTeleportLocation(IClientAPI remoteClient, ulong regionHandle, Vector3 position, + Vector3 lookAt, uint teleportFlags) + { + ScenePresence sp = null; + lock (m_scenePresences) + { + if (m_scenePresences.ContainsKey(remoteClient.AgentId)) + sp = m_scenePresences[remoteClient.AgentId]; + } + + if (sp != null) + { + m_sceneGridService.RequestTeleportToLocation(sp, regionHandle, + position, lookAt, teleportFlags); + } + } + + /// + /// Tries to teleport agent to landmark. + /// + /// + /// + /// + public void RequestTeleportLandmark(IClientAPI remoteClient, UUID regionID, Vector3 position) + { + RegionInfo info = CommsManager.GridService.RequestNeighbourInfo(regionID); + + if (info == null) + { + // can't find the region: Tell viewer and abort + remoteClient.SendTeleportFailed("The teleport destination could not be found."); + return; + } + + ScenePresence sp = null; + lock (m_scenePresences) + { + if (m_scenePresences.ContainsKey(remoteClient.AgentId)) + sp = m_scenePresences[remoteClient.AgentId]; + } + if (sp != null) + { + m_sceneGridService.RequestTeleportToLocation(sp, info.RegionHandle, + position, Vector3.Zero, (uint)(TPFlags.SetLastToTarget | TPFlags.ViaLandmark)); + } + } + + /// + /// Agent is crossing the border into a neighbouring region. Tell the neighbour about it! + /// + /// + /// + /// + /// + /// + public bool InformNeighbourOfCrossing(ulong regionHandle, UUID agentID, Vector3 position, bool isFlying) + { + return m_sceneGridService.CrossToNeighbouringRegion(regionHandle, agentID, position, isFlying); + } + + public void SendOutChildAgentUpdates(AgentPosition cadu, ScenePresence presence) + { + m_sceneGridService.SendChildAgentDataUpdate(cadu, presence); + } + + #endregion + + #region Other Methods + + public void SetObjectCapacity(int objects) + { + // Region specific config overrides global + // + if (RegionInfo.ObjectCapacity != 0) + objects = RegionInfo.ObjectCapacity; + + if (StatsReporter != null) + { + StatsReporter.SetObjectCapacity(objects); + } + objectCapacity = objects; + } + + public List GetFriendList(UUID avatarID) + { + return CommsManager.GetUserFriendList(avatarID); + } + + public Dictionary GetFriendRegionInfos(List uuids) + { + return CommsManager.GetFriendRegionInfos(uuids); + } + + public List InformFriendsInOtherRegion(UUID agentId, ulong destRegionHandle, List friends, bool online) + { + return CommsManager.InformFriendsInOtherRegion(agentId, destRegionHandle, friends, online); + } + + public bool TriggerTerminateFriend(ulong regionHandle, UUID agentID, UUID exFriendID) + { + return CommsManager.TriggerTerminateFriend(regionHandle, agentID, exFriendID); + } + + public virtual void StoreAddFriendship(UUID ownerID, UUID friendID, uint perms) + { + m_sceneGridService.AddNewUserFriend(ownerID, friendID, perms); + } + + public virtual void StoreUpdateFriendship(UUID ownerID, UUID friendID, uint perms) + { + m_sceneGridService.UpdateUserFriendPerms(ownerID, friendID, perms); + } + + public virtual void StoreRemoveFriendship(UUID ownerID, UUID ExfriendID) + { + m_sceneGridService.RemoveUserFriend(ownerID, ExfriendID); + } + + public void AddPacketStats(int inPackets, int outPackets, int unAckedBytes) + { + StatsReporter.AddInPackets(inPackets); + StatsReporter.AddOutPackets(outPackets); + StatsReporter.AddunAckedBytes(unAckedBytes); + } + + public void AddAgentTime(int ms) + { + StatsReporter.addFrameMS(ms); + StatsReporter.addAgentMS(ms); + } + + public void AddAgentUpdates(int count) + { + StatsReporter.AddAgentUpdates(count); + } + + public void AddPendingDownloads(int count) + { + StatsReporter.addPendingDownload(count); + } + + #endregion + + #region Alert Methods + + /// + /// Handle a request for admin rights + /// + /// + /// + /// + /// + public void handleRequestGodlikePowers(UUID agentID, UUID sessionID, UUID token, bool godLike, + IClientAPI controllingClient) + { + ScenePresence sp = null; + + lock (m_scenePresences) + { + // User needs to be logged into this sim + m_scenePresences.TryGetValue(agentID, out sp); + } + + if (sp != null) + { + if (godLike == false) + { + sp.GrantGodlikePowers(agentID, sessionID, token, godLike); + return; + } + + // First check that this is the sim owner + if (Permissions.IsGod(agentID)) + { + // Next we check for spoofing..... + UUID testSessionID = sp.ControllingClient.SessionId; + if (sessionID == testSessionID) + { + if (sessionID == controllingClient.SessionId) + { + //m_log.Info("godlike: " + godLike.ToString()); + sp.GrantGodlikePowers(agentID, testSessionID, token, godLike); + } + } + } + else + { + m_dialogModule.SendAlertToUser(agentID, "Request for god powers denied"); + } + } + } + + /// + /// Kicks User specified from the simulator. This logs them off of the grid + /// If the client gets the UUID: 44e87126e7944ded05b37c42da3d5cdb it assumes + /// that you're kicking it even if the avatar's UUID isn't the UUID that the + /// agent is assigned + /// + /// The person doing the kicking + /// The session of the person doing the kicking + /// the person that is being kicked + /// This isn't used apparently + /// The message to send to the user after it's been turned into a field + public void HandleGodlikeKickUser(UUID godID, UUID sessionID, UUID agentID, uint kickflags, byte[] reason) + { + // For some reason the client sends this seemingly hard coded UUID for kicking everyone. Dun-know. + UUID kickUserID = new UUID("44e87126e7944ded05b37c42da3d5cdb"); + lock (m_scenePresences) + { + if (m_scenePresences.ContainsKey(agentID) || agentID == kickUserID) + { + if (Permissions.IsGod(godID)) + { + if (agentID == kickUserID) + { + ClientManager.ForEachClient(delegate(IClientAPI controller) + { + if (controller.AgentId != godID) + controller.Kick(Utils.BytesToString(reason)); + } + ); + + // This is a bit crude. It seems the client will be null before it actually stops the thread + // The thread will kill itself eventually :/ + // Is there another way to make sure *all* clients get this 'inter region' message? + ClientManager.ForEachClient(delegate(IClientAPI controller) + { + ScenePresence p = GetScenePresence(controller.AgentId); + bool childagent = p != null && p.IsChildAgent; + if (controller.AgentId != godID && !childagent) + // Do we really want to kick the initiator of this madness? + { + controller.Close(true); + } + } + ); + } + else + { + m_sceneGraph.removeUserCount(!m_scenePresences[agentID].IsChildAgent); + + m_scenePresences[agentID].ControllingClient.Kick(Utils.BytesToString(reason)); + m_scenePresences[agentID].ControllingClient.Close(true); + } + } + else + { + m_dialogModule.SendAlertToUser(godID, "Kick request denied"); + } + } + } + } + + public void HandleObjectPermissionsUpdate(IClientAPI controller, UUID agentID, UUID sessionID, byte field, uint localId, uint mask, byte set) + { + // Check for spoofing.. since this is permissions we're talking about here! + if ((controller.SessionId == sessionID) && (controller.AgentId == agentID)) + { + // Tell the object to do permission update + if (localId != 0) + { + SceneObjectGroup chObjectGroup = GetGroupByPrim(localId); + if (chObjectGroup != null) + { + chObjectGroup.UpdatePermissions(agentID, field, localId, mask, set); + } + } + } + } + + /// + /// Handle an alert command from the console. + /// FIXME: Command parsing code really shouldn't be in this core Scene class. + /// + /// + public void HandleAlertCommand(string[] commandParams) + { + if (commandParams[0] == "general") + { + string message = CombineParams(commandParams, 1); + m_dialogModule.SendGeneralAlert(message); + } + else + { + string message = CombineParams(commandParams, 2); + m_dialogModule.SendAlertToUser(commandParams[0], commandParams[1], message, false); + } + } + + private string CombineParams(string[] commandParams, int pos) + { + string result = String.Empty; + for (int i = pos; i < commandParams.Length; i++) + { + result += commandParams[i] + " "; + } + return result; + } + + #endregion + + /// + /// Causes all clients to get a full object update on all of the objects in the scene. + /// + public void ForceClientUpdate() + { + List EntityList = GetEntities(); + + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + ((SceneObjectGroup)ent).ScheduleGroupForFullUpdate(); + } + } + } + + /// + /// This is currently only used for scale (to scale to MegaPrim size) + /// There is a console command that calls this in OpenSimMain + /// + /// + public void HandleEditCommand(string[] cmdparams) + { + Console.WriteLine("Searching for Primitive: '" + cmdparams[0] + "'"); + + List EntityList = GetEntities(); + + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + SceneObjectPart part = ((SceneObjectGroup)ent).GetChildPart(((SceneObjectGroup)ent).UUID); + if (part != null) + { + if (part.Name == cmdparams[0]) + { + part.Resize( + new Vector3(Convert.ToSingle(cmdparams[1]), Convert.ToSingle(cmdparams[2]), + Convert.ToSingle(cmdparams[3]))); + + Console.WriteLine("Edited scale of Primitive: " + part.Name); + } + } + } + } + } + + public override void Show(string[] showParams) + { + base.Show(showParams); + + switch (showParams[0]) + { + case "users": + m_log.Error("Current Region: " + RegionInfo.RegionName); + m_log.ErrorFormat("{0,-16}{1,-16}{2,-25}{3,-25}{4,-16}{5,-16}{6,-16}", "Firstname", "Lastname", + "Agent ID", "Session ID", "Circuit", "IP", "World"); + + foreach (ScenePresence scenePresence in GetAvatars()) + { + m_log.ErrorFormat("{0,-16}{1,-16}{2,-25}{3,-25}{4,-16},{5,-16}{6,-16}", + scenePresence.Firstname, + scenePresence.Lastname, + scenePresence.UUID, + scenePresence.ControllingClient.AgentId, + "Unknown", + "Unknown", + RegionInfo.RegionName); + } + + break; + } + } + + #region Script Handling Methods + + /// + /// Console command handler to send script command to script engine. + /// + /// + public void SendCommandToPlugins(string[] args) + { + m_eventManager.TriggerOnPluginConsole(args); + } + + public double GetLandHeight(int x, int y) + { + return Heightmap[x, y]; + } + + public UUID GetLandOwner(float x, float y) + { + ILandObject land = LandChannel.GetLandObject(x, y); + if (land == null) + { + return UUID.Zero; + } + else + { + return land.landData.OwnerID; + } + } + + public LandData GetLandData(float x, float y) + { + return LandChannel.GetLandObject(x, y).landData; + } + + public LandData GetLandData(uint x, uint y) + { + m_log.DebugFormat("[SCENE] returning land for {0},{1}", x, y); + return LandChannel.GetLandObject((int)x, (int)y).landData; + } + + public void SetLandMusicURL(float x, float y, string url) + { + ILandObject land = LandChannel.GetLandObject(x, y); + if (land == null) + { + return; + } + else + { + land.landData.MusicURL = url; + land.sendLandUpdateToAvatarsOverMe(); + return; + } + } + + public void SetLandMediaURL(float x, float y, string url) + { + ILandObject land = LandChannel.GetLandObject(x, y); + + if (land == null) + { + return; + } + + else + { + land.landData.MediaURL = url; + land.sendLandUpdateToAvatarsOverMe(); + return; + } + } + + public RegionInfo RequestClosestRegion(string name) + { + return m_sceneGridService.RequestClosestRegion(name); + } + + #endregion + + #region Script Engine + + private List ScriptEngines = new List(); + public bool DumpAssetsToFile; + + /// + /// + /// + /// + public void AddScriptEngine(ScriptEngineInterface scriptEngine) + { + ScriptEngines.Add(scriptEngine); + scriptEngine.InitializeEngine(this); + } + + public void TriggerObjectChanged(uint localID, uint change) + { + m_eventManager.TriggerOnScriptChangedEvent(localID, change); + } + + public void TriggerAtTargetEvent(uint localID, uint handle, Vector3 targetpos, Vector3 currentpos) + { + m_eventManager.TriggerAtTargetEvent(localID, handle, targetpos, currentpos); + } + + public void TriggerNotAtTargetEvent(uint localID) + { + m_eventManager.TriggerNotAtTargetEvent(localID); + } + + private bool ScriptDanger(SceneObjectPart part,Vector3 pos) + { + ILandObject parcel = LandChannel.GetLandObject(pos.X, pos.Y); + if (part != null) + { + if (parcel != null) + { + if ((parcel.landData.Flags & (uint)Parcel.ParcelFlags.AllowOtherScripts) != 0) + { + return true; + } + else if ((parcel.landData.Flags & (uint)Parcel.ParcelFlags.AllowGroupScripts) != 0) + { + if (part.OwnerID == parcel.landData.OwnerID || (parcel.landData.IsGroupOwned && part.GroupID == parcel.landData.GroupID) || Permissions.IsGod(part.OwnerID)) + { + return true; + } + else + { + return false; + } + } + else + { + if (part.OwnerID == parcel.landData.OwnerID) + { + return true; + } + else + { + return false; + } + } + } + else + { + + if (pos.X > 0f && pos.X < Constants.RegionSize && pos.Y > 0f && pos.Y < Constants.RegionSize) + { + // The only time parcel != null when an object is inside a region is when + // there is nothing behind the landchannel. IE, no land plugin loaded. + return true; + } + else + { + // The object is outside of this region. Stop piping events to it. + return false; + } + } + } + else + { + return false; + } + } + + public bool ScriptDanger(uint localID, Vector3 pos) + { + SceneObjectPart part = GetSceneObjectPart(localID); + if (part != null) + { + return ScriptDanger(part, pos); + } + else + { + return false; + } + } + + public bool PipeEventsForScript(uint localID) + { + SceneObjectPart part = GetSceneObjectPart(localID); + if (part != null) + { + // Changed so that child prims of attachments return ScriptDanger for their parent, so that + // their scripts will actually run. + // -- Leaf, Tue Aug 12 14:17:05 EDT 2008 + SceneObjectPart parent = part.ParentGroup.RootPart; + if (parent != null && parent.IsAttachment) + return ScriptDanger(parent, parent.GetWorldPosition()); + else + return ScriptDanger(part, part.GetWorldPosition()); + } + else + { + return false; + } + } + + #endregion + + #region SceneGraph wrapper methods + + /// + /// + /// + /// + /// + public UUID ConvertLocalIDToFullID(uint localID) + { + return m_sceneGraph.ConvertLocalIDToFullID(localID); + } + + public void SwapRootAgentCount(bool rootChildChildRootTF) + { + m_sceneGraph.SwapRootChildAgent(rootChildChildRootTF); + } + + public void AddPhysicalPrim(int num) + { + m_sceneGraph.AddPhysicalPrim(num); + } + + public void RemovePhysicalPrim(int num) + { + m_sceneGraph.RemovePhysicalPrim(num); + } + + //The idea is to have a group of method that return a list of avatars meeting some requirement + // ie it could be all m_scenePresences within a certain range of the calling prim/avatar. + + /// + /// Return a list of all avatars in this region. + /// This list is a new object, so it can be iterated over without locking. + /// + /// + public List GetAvatars() + { + return m_sceneGraph.GetAvatars(); + } + + /// + /// Return a list of all ScenePresences in this region. This returns child agents as well as root agents. + /// This list is a new object, so it can be iterated over without locking. + /// + /// + public List GetScenePresences() + { + return m_sceneGraph.GetScenePresences(); + } + + /// + /// Request a filtered list of ScenePresences in this region. + /// This list is a new object, so it can be iterated over without locking. + /// + /// + /// + public List GetScenePresences(FilterAvatarList filter) + { + return m_sceneGraph.GetScenePresences(filter); + } + + /// + /// Request a scene presence by UUID + /// + /// + /// + public ScenePresence GetScenePresence(UUID avatarID) + { + return m_sceneGraph.GetScenePresence(avatarID); + } + + public override bool PresenceChildStatus(UUID avatarID) + { + ScenePresence cp = GetScenePresence(avatarID); + + // FIXME: This is really crap - some logout code is relying on a NullReferenceException to halt its processing + // This needs to be fixed properly by cleaning up the logout code. + //if (cp != null) + // return cp.IsChildAgent; + + //return false; + + return cp.IsChildAgent; + } + + /// + /// + /// + /// + public void ForEachScenePresence(Action action) + { + // We don't want to try to send messages if there are no avatars. + if (m_scenePresences != null) + { + try + { + List presenceList = GetScenePresences(); + foreach (ScenePresence presence in presenceList) + { + action(presence); + } + } + catch (Exception e) + { + m_log.Info("[BUG]: " + e.ToString()); + } + } + } + + /// + /// + /// + /// + // public void ForEachObject(Action action) + // { + // List presenceList; + // + // lock (m_sceneObjects) + // { + // presenceList = new List(m_sceneObjects.Values); + // } + // + // foreach (SceneObjectGroup presence in presenceList) + // { + // action(presence); + // } + // } + + /// + /// Get a named prim contained in this scene (will return the first + /// found, if there are more than one prim with the same name) + /// + /// + /// + public SceneObjectPart GetSceneObjectPart(string name) + { + return m_sceneGraph.GetSceneObjectPart(name); + } + + /// + /// Get a prim via its local id + /// + /// + /// + public SceneObjectPart GetSceneObjectPart(uint localID) + { + return m_sceneGraph.GetSceneObjectPart(localID); + } + + /// + /// Get a prim via its UUID + /// + /// + /// + public SceneObjectPart GetSceneObjectPart(UUID fullID) + { + return m_sceneGraph.GetSceneObjectPart(fullID); + } + + public bool TryGetAvatar(UUID avatarId, out ScenePresence avatar) + { + return m_sceneGraph.TryGetAvatar(avatarId, out avatar); + } + + public bool TryGetAvatarByName(string avatarName, out ScenePresence avatar) + { + return m_sceneGraph.TryGetAvatarByName(avatarName, out avatar); + } + + public void ForEachClient(Action action) + { + m_sceneGraph.ForEachClient(action); + } + + /// + /// Returns a list of the entities in the scene. This is a new list so operations perform on the list itself + /// will not affect the original list of objects in the scene. + /// + /// + public List GetEntities() + { + return m_sceneGraph.GetEntities(); + } + + #endregion + + #region Avatar Appearance Default + + public static void GetDefaultAvatarAppearance(out AvatarWearable[] wearables, out byte[] visualParams) + { + visualParams = GetDefaultVisualParams(); + wearables = AvatarWearable.DefaultWearables; + } + + private static byte[] GetDefaultVisualParams() + { + byte[] visualParams; + visualParams = new byte[218]; + for (int i = 0; i < 218; i++) + { + visualParams[i] = 100; + } + return visualParams; + } + + #endregion + + public void ParcelMediaSetTime(float time) + { + //should be doing this by parcel, but as its only for testing + // The use of Thread.Sleep here causes the following compiler error under mono 1.2.4 + // OpenSim/Region/Environment/Scenes/Scene.cs(3675,17): error CS0103: The name `Thread' does not exist + // in the context of `<>c__CompilerGenerated17' + // MW said it was okay to comment the body of this method out for now since the code is experimental + // and will be replaced anyway +// ForEachClient(delegate(IClientAPI client) +// { +// client.SendParcelMediaCommand((uint)(2), ParcelMediaCommandEnum.Pause, 0); +// Thread.Sleep(10); +// client.SendParcelMediaCommand((uint)(64), ParcelMediaCommandEnum.Time, time); +// Thread.Sleep(200); +// client.SendParcelMediaCommand((uint)(4), ParcelMediaCommandEnum.Play, 0); +// }); + } + + public void RegionHandleRequest(IClientAPI client, UUID regionID) + { + RegionInfo info; + if (regionID == RegionInfo.RegionID) + info = RegionInfo; + else + info = CommsManager.GridService.RequestNeighbourInfo(regionID); + + if (info != null) + client.SendRegionHandle(regionID, info.RegionHandle); + } + + public void TerrainUnAcked(IClientAPI client, int patchX, int patchY) + { + //Console.WriteLine("Terrain packet unacked, resending patch: " + patchX + " , " + patchY); + client.SendLayerData(patchX, patchY, Heightmap.GetFloatsSerialised()); + } + + public void SetRootAgentScene(UUID agentID) + { + IInventoryTransferModule inv = RequestModuleInterface(); + if (inv == null) + return; + + inv.SetRootAgentScene(agentID, this); + + EventManager.TriggerSetRootAgentScene(agentID, this); + } + + public bool NeedSceneCacheClear(UUID agentID) + { + IInventoryTransferModule inv = RequestModuleInterface(); + if (inv == null) + return true; + + return inv.NeedSceneCacheClear(agentID, this); + } + + public void ObjectSaleInfo(IClientAPI client, UUID agentID, UUID sessionID, uint localID, byte saleType, int salePrice) + { + SceneObjectPart part = GetSceneObjectPart(localID); + if (part == null || part.ParentGroup == null) + return; + + if (part.ParentGroup.IsDeleted) + return; + + part = part.ParentGroup.RootPart; + + part.ObjectSaleType = saleType; + part.SalePrice = salePrice; + + part.ParentGroup.HasGroupChanged = true; + + part.GetProperties(client); + } + + public bool PerformObjectBuy(IClientAPI remoteClient, UUID categoryID, + uint localID, byte saleType) + { + SceneObjectPart part = GetSceneObjectPart(localID); + + if (part == null) + return false; + + if (part.ParentGroup == null) + return false; + + SceneObjectGroup group = part.ParentGroup; + + switch (saleType) + { + case 1: // Sell as original (in-place sale) + uint effectivePerms=group.GetEffectivePermissions(); + + if ((effectivePerms & (uint)PermissionMask.Transfer) == 0) + { + m_dialogModule.SendAlertToUser(remoteClient, "This item doesn't appear to be for sale"); + return false; + } + + group.SetOwnerId(remoteClient.AgentId); + group.SetRootPartOwner(part, remoteClient.AgentId, + remoteClient.ActiveGroupId); + + List partList = + new List(group.Children.Values); + + if (Permissions.PropagatePermissions()) + { + foreach (SceneObjectPart child in partList) + { + child.Inventory.ChangeInventoryOwner(remoteClient.AgentId); + child.ApplyNextOwnerPermissions(); + } + } + + part.ObjectSaleType = 0; + part.SalePrice = 10; + + group.HasGroupChanged = true; + part.GetProperties(remoteClient); + part.ScheduleFullUpdate(); + + break; + + case 2: // Sell a copy + string sceneObjectXml = group.ToXmlString(); + + CachedUserInfo userInfo = + CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId); + + if (userInfo != null) + { + uint perms=group.GetEffectivePermissions(); + + if ((perms & (uint)PermissionMask.Transfer) == 0) + { + m_dialogModule.SendAlertToUser(remoteClient, "This item doesn't appear to be for sale"); + return false; + } + + AssetBase asset = CreateAsset( + group.GetPartName(localID), + group.GetPartDescription(localID), + (sbyte)AssetType.Object, + Utils.StringToBytes(sceneObjectXml)); + AssetCache.AddAsset(asset); + + InventoryItemBase item = new InventoryItemBase(); + item.Creator = part.CreatorID; + + item.ID = UUID.Random(); + item.Owner = remoteClient.AgentId; + item.AssetID = asset.Metadata.FullID; + item.Description = asset.Metadata.Description; + item.Name = asset.Metadata.Name; + item.AssetType = asset.Metadata.Type; + item.InvType = (int)InventoryType.Object; + item.Folder = categoryID; + + uint nextPerms=(perms & 7) << 13; + if ((nextPerms & (uint)PermissionMask.Copy) == 0) + perms &= ~(uint)PermissionMask.Copy; + if ((nextPerms & (uint)PermissionMask.Transfer) == 0) + perms &= ~(uint)PermissionMask.Transfer; + if ((nextPerms & (uint)PermissionMask.Modify) == 0) + perms &= ~(uint)PermissionMask.Modify; + + item.BasePermissions = perms & part.NextOwnerMask; + item.CurrentPermissions = perms & part.NextOwnerMask; + item.NextPermissions = part.NextOwnerMask; + item.EveryOnePermissions = part.EveryoneMask & + part.NextOwnerMask; + item.GroupPermissions = part.GroupMask & + part.NextOwnerMask; + item.CurrentPermissions |= 8; // Slam! + item.CreationDate = Util.UnixTimeSinceEpoch(); + + userInfo.AddItem(item); + remoteClient.SendInventoryItemCreateUpdate(item); + } + else + { + m_dialogModule.SendAlertToUser(remoteClient, "Cannot buy now. Your inventory is unavailable"); + return false; + } + break; + + case 3: // Sell contents + List invList = part.Inventory.GetInventoryList(); + + bool okToSell = true; + + foreach (UUID invID in invList) + { + TaskInventoryItem item = part.Inventory.GetInventoryItem(invID); + if ((item.CurrentPermissions & + (uint)PermissionMask.Transfer) == 0) + { + okToSell = false; + break; + } + } + + if (!okToSell) + { + m_dialogModule.SendAlertToUser( + remoteClient, "This item's inventory doesn't appear to be for sale"); + return false; + } + + if (invList.Count > 0) + MoveTaskInventoryItems(remoteClient.AgentId, part.Name, + part, invList); + break; + } + + return true; + } + + public void CleanTempObjects() + { + List objs = GetEntities(); + + foreach (EntityBase obj in objs) + { + if (obj is SceneObjectGroup) + { + SceneObjectGroup grp = (SceneObjectGroup)obj; + + if (!grp.IsDeleted) + { + if ((grp.RootPart.Flags & PrimFlags.TemporaryOnRez) != 0) + { + if (grp.RootPart.Expires <= DateTime.Now) + DeleteSceneObject(grp, false); + } + } + } + } + } + + public void DeleteFromStorage(UUID uuid) + { + m_storageManager.DataStore.RemoveObject(uuid, m_regInfo.RegionID); + } + + public int GetHealth() + { + int health=1; // Start at 1, means we're up + + // A login in the last 4 mins? We can't be doing too badly + // + if ((System.Environment.TickCount - m_LastLogin) < 240000) + health++; + + return 0; + } + + // This callback allows the PhysicsScene to call back to its caller (the SceneGraph) and + // update non-physical objects like the joint proxy objects that represent the position + // of the joints in the scene. + + // This routine is normally called from within a lock (OdeLock) from within the OdePhysicsScene + // WARNING: be careful of deadlocks here if you manipulate the scene. Remember you are being called + // from within the OdePhysicsScene. + + protected internal void jointMoved(PhysicsJoint joint) + { + // m_parentScene.PhysicsScene.DumpJointInfo(); // non-thread-locked version; we should already be in a lock (OdeLock) when this callback is invoked + // FIXME: this causes a sequential lookup of all objects in the scene; use a dictionary + SceneObjectPart jointProxyObject = GetSceneObjectPart(joint.ObjectNameInScene); + if (jointProxyObject == null) + { + jointErrorMessage(joint, "WARNING, joint proxy not found, name " + joint.ObjectNameInScene); + return; + } + + // now update the joint proxy object in the scene to have the position of the joint as returned by the physics engine + SceneObjectPart trackedBody = GetSceneObjectPart(joint.TrackedBodyName); // FIXME: causes a sequential lookup + if (trackedBody == null) return; // the actor may have been deleted but the joint still lingers around a few frames waiting for deletion. during this time, trackedBody is NULL to prevent further motion of the joint proxy. + jointProxyObject.Velocity = trackedBody.Velocity; + jointProxyObject.RotationalVelocity = trackedBody.RotationalVelocity; + switch (joint.Type) + { + case PhysicsJointType.Ball: + { + PhysicsVector jointAnchor = PhysicsScene.GetJointAnchor(joint); + Vector3 proxyPos = new Vector3(jointAnchor.X, jointAnchor.Y, jointAnchor.Z); + jointProxyObject.ParentGroup.UpdateGroupPosition(proxyPos); // schedules the entire group for a terse update + } + break; + + case PhysicsJointType.Hinge: + { + PhysicsVector jointAnchor = PhysicsScene.GetJointAnchor(joint); + + // Normally, we would just ask the physics scene to return the axis for the joint. + // Unfortunately, ODE sometimes returns <0,0,0> for the joint axis, which should + // never occur. Therefore we cannot rely on ODE to always return a correct joint axis. + // Therefore the following call does not always work: + //PhysicsVector phyJointAxis = _PhyScene.GetJointAxis(joint); + + // instead we compute the joint orientation by saving the original joint orientation + // relative to one of the jointed bodies, and applying this transformation + // to the current position of the jointed bodies (the tracked body) to compute the + // current joint orientation. + + if (joint.TrackedBodyName == null) + { + jointErrorMessage(joint, "joint.TrackedBodyName is null, joint " + joint.ObjectNameInScene); + } + + Vector3 proxyPos = new Vector3(jointAnchor.X, jointAnchor.Y, jointAnchor.Z); + Quaternion q = trackedBody.RotationOffset * joint.LocalRotation; + + jointProxyObject.ParentGroup.UpdateGroupPosition(proxyPos); // schedules the entire group for a terse update + jointProxyObject.ParentGroup.UpdateGroupRotation(q); // schedules the entire group for a terse update + } + break; + } + } + + // This callback allows the PhysicsScene to call back to its caller (the SceneGraph) and + // update non-physical objects like the joint proxy objects that represent the position + // of the joints in the scene. + + // This routine is normally called from within a lock (OdeLock) from within the OdePhysicsScene + // WARNING: be careful of deadlocks here if you manipulate the scene. Remember you are being called + // from within the OdePhysicsScene. + protected internal void jointDeactivated(PhysicsJoint joint) + { + //m_log.Debug("[NINJA] SceneGraph.jointDeactivated, joint:" + joint.ObjectNameInScene); + // FIXME: this causes a sequential lookup of all objects in the scene; use a dictionary + SceneObjectPart jointProxyObject = GetSceneObjectPart(joint.ObjectNameInScene); + if (jointProxyObject == null) + { + jointErrorMessage(joint, "WARNING, trying to deactivate (stop interpolation of) joint proxy, but not found, name " + joint.ObjectNameInScene); + return; + } + + // turn the proxy non-physical, which also stops its client-side interpolation + bool wasUsingPhysics = ((jointProxyObject.ObjectFlags & (uint)PrimFlags.Physics) != 0); + if (wasUsingPhysics) + { + jointProxyObject.UpdatePrimFlags(false, false, true, false); // FIXME: possible deadlock here; check to make sure all the scene alterations set into motion here won't deadlock + } + } + + // This callback allows the PhysicsScene to call back to its caller (the SceneGraph) and + // alert the user of errors by using the debug channel in the same way that scripts alert + // the user of compile errors. + + // This routine is normally called from within a lock (OdeLock) from within the OdePhysicsScene + // WARNING: be careful of deadlocks here if you manipulate the scene. Remember you are being called + // from within the OdePhysicsScene. + public void jointErrorMessage(PhysicsJoint joint, string message) + { + // FIXME: this causes a sequential lookup of all objects in the scene; use a dictionary + if (joint != null) + { + if (joint.ErrorMessageCount > PhysicsJoint.maxErrorMessages) + return; + + SceneObjectPart jointProxyObject = GetSceneObjectPart(joint.ObjectNameInScene); + if (jointProxyObject != null) + { + SimChat(Utils.StringToBytes("[NINJA] " + message), + ChatTypeEnum.DebugChannel, + 2147483647, + jointProxyObject.AbsolutePosition, + jointProxyObject.Name, + jointProxyObject.UUID, + false); + + joint.ErrorMessageCount++; + + if (joint.ErrorMessageCount > PhysicsJoint.maxErrorMessages) + { + SimChat(Utils.StringToBytes("[NINJA] Too many messages for this joint, suppressing further messages."), + ChatTypeEnum.DebugChannel, + 2147483647, + jointProxyObject.AbsolutePosition, + jointProxyObject.Name, + jointProxyObject.UUID, + false); + } + } + else + { + // couldn't find the joint proxy object; the error message is silently suppressed + } + } + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/SceneBase.cs b/OpenSim/Region/Framework/Scenes/SceneBase.cs new file mode 100644 index 0000000..6d61e9f --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/SceneBase.cs @@ -0,0 +1,419 @@ +/* + * 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 OpenSim 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 System.Threading; +using OpenMetaverse; +using log4net; +using OpenSim.Framework; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.Framework.Scenes +{ + public abstract class SceneBase : IScene + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + #region Events + + public event restart OnRestart; + + #endregion + + #region Fields + + /// + /// All the region modules attached to this scene. + /// + public Dictionary Modules + { + get { return m_modules; } + } + protected Dictionary m_modules = new Dictionary(); + + /// + /// The module interfaces available from this scene. + /// + protected Dictionary > ModuleInterfaces = new Dictionary >(); + + protected Dictionary ModuleAPIMethods = new Dictionary(); + protected Dictionary m_moduleCommanders = new Dictionary(); + + /// + /// Registered classes that are capable of creating entities. + /// + protected Dictionary m_entityCreators = new Dictionary(); + + //API module interfaces + + /// + /// The last allocated local prim id. When a new local id is requested, the next number in the sequence is + /// dispensed. + /// + protected uint m_lastAllocatedLocalId = 720000; + + private readonly Mutex _primAllocateMutex = new Mutex(false); + + private readonly ClientManager m_clientManager = new ClientManager(); + + public ClientManager ClientManager + { + get { return m_clientManager; } + } + + protected ulong m_regionHandle; + protected string m_regionName; + protected RegionInfo m_regInfo; + + //public TerrainEngine Terrain; + public ITerrainChannel Heightmap; + + /// + /// Allows retrieval of land information for this scene. + /// + public ILandChannel LandChannel; + + /// + /// Manage events that occur in this scene (avatar movement, script rez, etc.). Commonly used by region modules + /// to subscribe to scene events. + /// + public EventManager EventManager + { + get { return m_eventManager; } + } + protected EventManager m_eventManager; + + protected ScenePermissions m_permissions; + public ScenePermissions Permissions + { + get { return m_permissions; } + } + + protected string m_datastore; + + private AssetCache m_assetCache; + + public AssetCache AssetCache + { + get { return m_assetCache; } + set { m_assetCache = value; } + } + + protected RegionStatus m_regStatus; + + public RegionStatus Region_Status + { + get { return m_regStatus; } + set { m_regStatus = value; } + } + + #endregion + + #region Update Methods + + /// + /// Normally called once every frame/tick to let the world preform anything required (like running the physics simulation) + /// + public abstract void Update(); + + #endregion + + #region Terrain Methods + + /// + /// Loads the World heightmap + /// + public abstract void LoadWorldMap(); + + /// + /// Send the region heightmap to the client + /// + /// Client to send to + public virtual void SendLayerData(IClientAPI RemoteClient) + { + RemoteClient.SendLayerData(Heightmap.GetFloatsSerialised()); + } + + #endregion + + #region Add/Remove Agent/Avatar + + /// + /// Register the new client with the scene. The client starts off as a child agent - the later agent crossing + /// will promote it to a root agent during login. + /// + /// + /// Remove a client from the scene + /// + /// + public abstract void RemoveClient(UUID agentID); + + public abstract void CloseAllAgents(uint circuitcode); + + #endregion + + /// + /// + /// + /// + public virtual RegionInfo RegionInfo + { + get { return m_regInfo; } + } + + #region admin stuff + + /// + /// Region Restart - Seconds till restart. + /// + /// + public virtual void Restart(int seconds) + { + m_log.Error("[REGION]: passing Restart Message up the namespace"); + restart handlerPhysicsCrash = OnRestart; + if (handlerPhysicsCrash != null) + handlerPhysicsCrash(RegionInfo); + } + + public virtual bool PresenceChildStatus(UUID avatarID) + { + return false; + } + + public abstract bool OtherRegionUp(RegionInfo thisRegion); + + public virtual string GetSimulatorVersion() + { + return "OpenSimulator Server"; + } + + #endregion + + #region Shutdown + + /// + /// Tidy before shutdown + /// + public virtual void Close() + { + // Shut down all non shared modules. + foreach (IRegionModule module in Modules.Values) + { + if (!module.IsSharedModule) + { + module.Close(); + } + } + Modules.Clear(); + + try + { + EventManager.TriggerShutdown(); + } + catch (Exception e) + { + m_log.Error("[SCENE]: SceneBase.cs: Close() - Failed with exception " + e.ToString()); + } + } + + #endregion + + /// + /// Returns a new unallocated local ID + /// + /// A brand new local ID + protected internal uint AllocateLocalId() + { + uint myID; + + _primAllocateMutex.WaitOne(); + myID = ++m_lastAllocatedLocalId; + _primAllocateMutex.ReleaseMutex(); + + return myID; + } + + #region Module Methods + + /// + /// Add a module to this scene. + /// + /// + /// + public void AddModule(string name, IRegionModule module) + { + if (!Modules.ContainsKey(name)) + { + Modules.Add(name, module); + } + } + + public void RegisterModuleCommander(ICommander commander) + { + lock (m_moduleCommanders) + { + m_moduleCommanders.Add(commander.Name, commander); + } + } + + /// + /// Get a module commander + /// + /// + /// The module commander, null if no module commander with that name was found + public ICommander GetCommander(string name) + { + lock (m_moduleCommanders) + { + if (m_moduleCommanders.ContainsKey(name)) + return m_moduleCommanders[name]; + } + + return null; + } + + public Dictionary GetCommanders() + { + return m_moduleCommanders; + } + + /// + /// Register an interface to a region module. This allows module methods to be called directly as + /// well as via events. If there is already a module registered for this interface, it is not replaced + /// (is this the best behaviour?) + /// + /// + public void RegisterModuleInterface(M mod) + { + if (!ModuleInterfaces.ContainsKey(typeof(M))) + { + List l = new List(); + l.Add(mod); + ModuleInterfaces.Add(typeof(M), l); + + if (mod is IEntityCreator) + { + IEntityCreator entityCreator = (IEntityCreator)mod; + foreach (PCode pcode in entityCreator.CreationCapabilities) + { + m_entityCreators[pcode] = entityCreator; + } + } + } + } + + public void StackModuleInterface(M mod) + { + List l; + if (ModuleInterfaces.ContainsKey(typeof(M))) + l = ModuleInterfaces[typeof(M)]; + else + l = new List(); + + if (l.Contains(mod)) + return; + + l.Add(mod); + + if (mod is IEntityCreator) + { + IEntityCreator entityCreator = (IEntityCreator)mod; + foreach (PCode pcode in entityCreator.CreationCapabilities) + { + m_entityCreators[pcode] = entityCreator; + } + } + + ModuleInterfaces[typeof(M)] = l; + } + + /// + /// For the given interface, retrieve the region module which implements it. + /// + /// null if there is no registered module implementing that interface + public T RequestModuleInterface() + { + if (ModuleInterfaces.ContainsKey(typeof(T))) + { + return (T)ModuleInterfaces[typeof(T)][0]; + } + else + { + return default(T); + } + } + + /// + /// For the given interface, retrieve an array of region modules that implement it. + /// + /// an empty array if there are no registered modules implementing that interface + public T[] RequestModuleInterfaces() + { + if (ModuleInterfaces.ContainsKey(typeof(T))) + { + List ret = new List(); + + foreach (Object o in ModuleInterfaces[typeof(T)]) + ret.Add((T)o); + return ret.ToArray(); + } + else + { + return new T[] { default(T) }; + } + } + + #endregion + + /// + /// Shows various details about the sim based on the parameters supplied by the console command in openSimMain. + /// + /// What to show + public virtual void Show(string[] showParams) + { + switch (showParams[0]) + { + case "modules": + m_log.Error("The currently loaded modules in " + RegionInfo.RegionName + " are:"); + foreach (IRegionModule module in Modules.Values) + { + if (!module.IsSharedModule) + { + m_log.Error("Region Module: " + module.Name); + } + } + break; + } + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs b/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs new file mode 100644 index 0000000..a2b2537 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs @@ -0,0 +1,1095 @@ +/* + * 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 OpenSim 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.Net; +using System.Reflection; +using System.Threading; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using log4net; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.Communications.Capabilities; +using OpenSim.Region.Framework.Interfaces; +using OSD = OpenMetaverse.StructuredData.OSD; + +namespace OpenSim.Region.Framework.Scenes +{ + public delegate void KiPrimitiveDelegate(uint localID); + + public delegate void RemoveKnownRegionsFromAvatarList(UUID avatarID, List regionlst); + + public class SceneCommunicationService //one instance per region + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected CommunicationsManager m_commsProvider; + protected IInterregionCommsOut m_interregionCommsOut; + protected RegionInfo m_regionInfo; + + protected RegionCommsListener regionCommsHost; + + protected List m_agentsInTransit; + + public event AgentCrossing OnAvatarCrossingIntoRegion; + public event ExpectUserDelegate OnExpectUser; + public event ExpectPrimDelegate OnExpectPrim; + public event CloseAgentConnection OnCloseAgentConnection; + public event PrimCrossing OnPrimCrossingIntoRegion; + public event RegionUp OnRegionUp; + public event ChildAgentUpdate OnChildAgentUpdate; + //public event RemoveKnownRegionsFromAvatarList OnRemoveKnownRegionFromAvatar; + public event LogOffUser OnLogOffUser; + public event GetLandData OnGetLandData; + + private AgentCrossing handlerAvatarCrossingIntoRegion = null; // OnAvatarCrossingIntoRegion; + private ExpectUserDelegate handlerExpectUser = null; // OnExpectUser; + private ExpectPrimDelegate handlerExpectPrim = null; // OnExpectPrim; + private CloseAgentConnection handlerCloseAgentConnection = null; // OnCloseAgentConnection; + private PrimCrossing handlerPrimCrossingIntoRegion = null; // OnPrimCrossingIntoRegion; + private RegionUp handlerRegionUp = null; // OnRegionUp; + private ChildAgentUpdate handlerChildAgentUpdate = null; // OnChildAgentUpdate; + //private RemoveKnownRegionsFromAvatarList handlerRemoveKnownRegionFromAvatar = null; // OnRemoveKnownRegionFromAvatar; + private LogOffUser handlerLogOffUser = null; + private GetLandData handlerGetLandData = null; // OnGetLandData + + public KiPrimitiveDelegate KiPrimitive; + + public SceneCommunicationService(CommunicationsManager commsMan) + { + m_commsProvider = commsMan; + m_agentsInTransit = new List(); + } + + /// + /// Register a region with the grid + /// + /// + /// Thrown if region registration fails. + public void RegisterRegion(IInterregionCommsOut comms_out, RegionInfo regionInfos) + { + m_interregionCommsOut = comms_out; + + m_regionInfo = regionInfos; + m_commsProvider.GridService.gdebugRegionName = regionInfos.RegionName; + m_commsProvider.InterRegion.rdebugRegionName = regionInfos.RegionName; + regionCommsHost = m_commsProvider.GridService.RegisterRegion(m_regionInfo); + + if (regionCommsHost != null) + { + //m_log.Info("[INTER]: " + debugRegionName + ": SceneCommunicationService: registered with gridservice and got" + regionCommsHost.ToString()); + + regionCommsHost.debugRegionName = regionInfos.RegionName; + regionCommsHost.OnExpectPrim += IncomingPrimCrossing; + regionCommsHost.OnExpectUser += NewUserConnection; + regionCommsHost.OnAvatarCrossingIntoRegion += AgentCrossing; + regionCommsHost.OnCloseAgentConnection += CloseConnection; + regionCommsHost.OnRegionUp += newRegionUp; + regionCommsHost.OnChildAgentUpdate += ChildAgentUpdate; + regionCommsHost.OnLogOffUser += GridLogOffUser; + regionCommsHost.OnGetLandData += FetchLandData; + } + else + { + //m_log.Info("[INTER]: " + debugRegionName + ": SceneCommunicationService: registered with gridservice and got null"); + } + } + + public RegionInfo RequestClosestRegion(string name) + { + return m_commsProvider.GridService.RequestClosestRegion(name); + } + + public void Close() + { + if (regionCommsHost != null) + { + regionCommsHost.OnLogOffUser -= GridLogOffUser; + regionCommsHost.OnChildAgentUpdate -= ChildAgentUpdate; + regionCommsHost.OnRegionUp -= newRegionUp; + regionCommsHost.OnExpectUser -= NewUserConnection; + regionCommsHost.OnExpectPrim -= IncomingPrimCrossing; + regionCommsHost.OnAvatarCrossingIntoRegion -= AgentCrossing; + regionCommsHost.OnCloseAgentConnection -= CloseConnection; + regionCommsHost.OnGetLandData -= FetchLandData; + + try + { + m_commsProvider.GridService.DeregisterRegion(m_regionInfo); + } + catch (Exception e) + { + m_log.ErrorFormat( + "[GRID]: Deregistration of region {0} from the grid failed - {1}. Continuing", + m_regionInfo.RegionName, e); + } + + regionCommsHost = null; + } + } + + #region CommsManager Event handlers + + /// + /// + /// + /// + /// + /// + protected void NewUserConnection(AgentCircuitData agent) + { + handlerExpectUser = OnExpectUser; + if (handlerExpectUser != null) + { + //m_log.Info("[INTER]: " + debugRegionName + ": SceneCommunicationService: OnExpectUser Fired for User:" + agent.firstname + " " + agent.lastname); + handlerExpectUser(agent); + } + } + + protected void GridLogOffUser(UUID AgentID, UUID RegionSecret, string message) + { + handlerLogOffUser = OnLogOffUser; + if (handlerLogOffUser != null) + { + handlerLogOffUser(AgentID, RegionSecret, message); + } + } + + protected bool newRegionUp(RegionInfo region) + { + handlerRegionUp = OnRegionUp; + if (handlerRegionUp != null) + { + //m_log.Info("[INTER]: " + debugRegionName + ": SceneCommunicationService: newRegionUp Fired for User:" + region.RegionName); + handlerRegionUp(region); + } + return true; + } + + protected bool ChildAgentUpdate(ChildAgentDataUpdate cAgentData) + { + handlerChildAgentUpdate = OnChildAgentUpdate; + if (handlerChildAgentUpdate != null) + handlerChildAgentUpdate(cAgentData); + + + return true; + } + + protected void AgentCrossing(UUID agentID, Vector3 position, bool isFlying) + { + handlerAvatarCrossingIntoRegion = OnAvatarCrossingIntoRegion; + if (handlerAvatarCrossingIntoRegion != null) + { + handlerAvatarCrossingIntoRegion(agentID, position, isFlying); + } + } + + protected bool IncomingPrimCrossing(UUID primID, String objXMLData, int XMLMethod) + { + handlerExpectPrim = OnExpectPrim; + if (handlerExpectPrim != null) + { + return handlerExpectPrim(primID, objXMLData, XMLMethod); + } + else + { + return false; + } + + } + + protected void PrimCrossing(UUID primID, Vector3 position, bool isPhysical) + { + handlerPrimCrossingIntoRegion = OnPrimCrossingIntoRegion; + if (handlerPrimCrossingIntoRegion != null) + { + handlerPrimCrossingIntoRegion(primID, position, isPhysical); + } + } + + protected bool CloseConnection(UUID agentID) + { + m_log.Debug("[INTERREGION]: Incoming Agent Close Request for agent: " + agentID); + + handlerCloseAgentConnection = OnCloseAgentConnection; + if (handlerCloseAgentConnection != null) + { + return handlerCloseAgentConnection(agentID); + } + + return false; + } + + protected LandData FetchLandData(uint x, uint y) + { + handlerGetLandData = OnGetLandData; + if (handlerGetLandData != null) + { + return handlerGetLandData(x, y); + } + return null; + } + + #endregion + + #region Inform Client of Neighbours + + private delegate void InformClientOfNeighbourDelegate( + ScenePresence avatar, AgentCircuitData a, SimpleRegionInfo reg, IPEndPoint endPoint, bool newAgent); + + private void InformClientOfNeighbourCompleted(IAsyncResult iar) + { + InformClientOfNeighbourDelegate icon = (InformClientOfNeighbourDelegate) iar.AsyncState; + icon.EndInvoke(iar); + } + + /// + /// Async component for informing client of which neighbours exist + /// + /// + /// This needs to run asynchronesously, as a network timeout may block the thread for a long while + /// + /// + /// + /// + /// + private void InformClientOfNeighbourAsync(ScenePresence avatar, AgentCircuitData a, SimpleRegionInfo reg, + IPEndPoint endPoint, bool newAgent) + { + // Let's wait just a little to give time to originating regions to catch up with closing child agents + // after a cross here + Thread.Sleep(500); + + uint x, y; + Utils.LongToUInts(reg.RegionHandle, out x, out y); + x = x / Constants.RegionSize; + y = y / Constants.RegionSize; + m_log.Info("[INTERGRID]: Starting to inform client about neighbour " + x + ", " + y + "(" + endPoint.ToString() + ")"); + + string capsPath = "http://" + reg.ExternalHostName + ":" + reg.HttpPort + + "/CAPS/" + a.CapsPath + "0000/"; + + //bool regionAccepted = m_commsProvider.InterRegion.InformRegionOfChildAgent(reg.RegionHandle, a); + bool regionAccepted = m_interregionCommsOut.SendCreateChildAgent(reg.RegionHandle, a); + + if (regionAccepted && newAgent) + { + IEventQueue eq = avatar.Scene.RequestModuleInterface(); + if (eq != null) + { + eq.EnableSimulator(reg.RegionHandle, endPoint, avatar.UUID); + eq.EstablishAgentCommunication(avatar.UUID, endPoint, capsPath); + m_log.DebugFormat("[CAPS]: Sending new CAPS seed url {0} to client {1} in region {2}", + capsPath, avatar.UUID, avatar.Scene.RegionInfo.RegionName); + } + else + { + avatar.ControllingClient.InformClientOfNeighbour(reg.RegionHandle, endPoint); + // TODO: make Event Queue disablable! + } + + m_log.Info("[INTERGRID]: Completed inform client about neighbour " + endPoint.ToString()); + } + } + + public void RequestNeighbors(RegionInfo region) + { + // List neighbours = + m_commsProvider.GridService.RequestNeighbours(m_regionInfo.RegionLocX, m_regionInfo.RegionLocY); + //IPEndPoint blah = new IPEndPoint(); + + //blah.Address = region.RemotingAddress; + //blah.Port = region.RemotingPort; + } + + /// + /// This informs all neighboring regions about agent "avatar". + /// Calls an asynchronous method to do so.. so it doesn't lag the sim. + /// + public void EnableNeighbourChildAgents(ScenePresence avatar, List lstneighbours) + { + List neighbours = new List(); + + //m_commsProvider.GridService.RequestNeighbours(m_regionInfo.RegionLocX, m_regionInfo.RegionLocY); + for (int i = 0; i < lstneighbours.Count; i++) + { + // We don't want to keep sending to regions that consistently fail on comms. + if (!(lstneighbours[i].commFailTF)) + { + neighbours.Add(new SimpleRegionInfo(lstneighbours[i])); + } + } + // we're going to be using the above code once neighbour cache is correct. Currently it doesn't appear to be + // So we're temporarily going back to the old method of grabbing it from the Grid Server Every time :/ + neighbours = + m_commsProvider.GridService.RequestNeighbours(m_regionInfo.RegionLocX, m_regionInfo.RegionLocY); + + /// We need to find the difference between the new regions where there are no child agents + /// and the regions where there are already child agents. We only send notification to the former. + List neighbourHandles = NeighbourHandles(neighbours); // on this region + neighbourHandles.Add(avatar.Scene.RegionInfo.RegionHandle); // add this region too + List previousRegionNeighbourHandles + = new List(avatar.Scene.CapsModule.GetChildrenSeeds(avatar.UUID).Keys); + List newRegions = NewNeighbours(neighbourHandles, previousRegionNeighbourHandles); + List oldRegions = OldNeighbours(neighbourHandles, previousRegionNeighbourHandles); + + //Dump("Current Neighbors", neighbourHandles); + //Dump("Previous Neighbours", previousRegionNeighbourHandles); + //Dump("New Neighbours", newRegions); + //Dump("Old Neighbours", oldRegions); + + /// Update the scene presence's known regions here on this region + avatar.DropOldNeighbours(oldRegions); + + /// Collect as many seeds as possible + Dictionary seeds + = new Dictionary(avatar.Scene.CapsModule.GetChildrenSeeds(avatar.UUID)); + + //Console.WriteLine(" !!! No. of seeds: " + seeds.Count); + if (!seeds.ContainsKey(avatar.Scene.RegionInfo.RegionHandle)) + seeds.Add(avatar.Scene.RegionInfo.RegionHandle, avatar.ControllingClient.RequestClientInfo().CapsPath); + + /// Create the necessary child agents + List cagents = new List(); + foreach (SimpleRegionInfo neighbour in neighbours) + { + if (neighbour.RegionHandle != avatar.Scene.RegionInfo.RegionHandle) + { + + AgentCircuitData agent = avatar.ControllingClient.RequestClientInfo(); + agent.BaseFolder = UUID.Zero; + agent.InventoryFolder = UUID.Zero; + agent.startpos = new Vector3(128, 128, 70); + agent.child = true; + + if (newRegions.Contains(neighbour.RegionHandle)) + { + agent.CapsPath = CapsUtil.GetRandomCapsObjectPath(); + avatar.AddNeighbourRegion(neighbour.RegionHandle, agent.CapsPath); + seeds.Add(neighbour.RegionHandle, agent.CapsPath); + } + else + agent.CapsPath = avatar.Scene.CapsModule.GetChildSeed(avatar.UUID, neighbour.RegionHandle); + + cagents.Add(agent); + } + } + + /// Update all child agent with everyone's seeds + foreach (AgentCircuitData a in cagents) + { + a.ChildrenCapSeeds = new Dictionary(seeds); + } + // These two are the same thing! + avatar.Scene.CapsModule.SetChildrenSeed(avatar.UUID, seeds); + avatar.KnownRegions = seeds; + //avatar.Scene.DumpChildrenSeeds(avatar.UUID); + //avatar.DumpKnownRegions(); + + bool newAgent = false; + int count = 0; + foreach (SimpleRegionInfo neighbour in neighbours) + { + // Don't do it if there's already an agent in that region + if (newRegions.Contains(neighbour.RegionHandle)) + newAgent = true; + else + newAgent = false; + + if (neighbour.RegionHandle != avatar.Scene.RegionInfo.RegionHandle) + { + InformClientOfNeighbourDelegate d = InformClientOfNeighbourAsync; + try + { + d.BeginInvoke(avatar, cagents[count], neighbour, neighbour.ExternalEndPoint, newAgent, + InformClientOfNeighbourCompleted, + d); + } + catch (Exception e) + { + m_log.ErrorFormat( + "[REGIONINFO]: Could not resolve external hostname {0} for region {1} ({2}, {3}). {4}", + neighbour.ExternalHostName, + neighbour.RegionHandle, + neighbour.RegionLocX, + neighbour.RegionLocY, + e); + + // FIXME: Okay, even though we've failed, we're still going to throw the exception on, + // since I don't know what will happen if we just let the client continue + + // XXX: Well, decided to swallow the exception instead for now. Let us see how that goes. + // throw e; + + } + } + count++; + } + } + + /// + /// This informs a single neighboring region about agent "avatar". + /// Calls an asynchronous method to do so.. so it doesn't lag the sim. + /// + public void InformNeighborChildAgent(ScenePresence avatar, SimpleRegionInfo region, List neighbours) + { + AgentCircuitData agent = avatar.ControllingClient.RequestClientInfo(); + agent.BaseFolder = UUID.Zero; + agent.InventoryFolder = UUID.Zero; + agent.startpos = new Vector3(128, 128, 70); + agent.child = true; + + InformClientOfNeighbourDelegate d = InformClientOfNeighbourAsync; + d.BeginInvoke(avatar, agent, region, region.ExternalEndPoint, true, + InformClientOfNeighbourCompleted, + d); + } + + #endregion + + public delegate void InformNeighbourThatRegionUpDelegate(RegionInfo region, ulong regionhandle); + + private void InformNeighborsThatRegionisUpCompleted(IAsyncResult iar) + { + InformNeighbourThatRegionUpDelegate icon = (InformNeighbourThatRegionUpDelegate) iar.AsyncState; + icon.EndInvoke(iar); + } + + /// + /// Asynchronous call to information neighbouring regions that this region is up + /// + /// + /// + private void InformNeighboursThatRegionIsUpAsync(RegionInfo region, ulong regionhandle) + { + m_log.Info("[INTERGRID]: Starting to inform neighbors that I'm here"); + //RegionUpData regiondata = new RegionUpData(region.RegionLocX, region.RegionLocY, region.ExternalHostName, region.InternalEndPoint.Port); + + bool regionAccepted = + m_commsProvider.InterRegion.RegionUp(new SerializableRegionInfo(region), regionhandle); + + if (regionAccepted) + { + m_log.Info("[INTERGRID]: Completed informing neighbors that I'm here"); + handlerRegionUp = OnRegionUp; + + // yes, we're notifying ourselves. + if (handlerRegionUp != null) + handlerRegionUp(region); + } + else + { + m_log.Warn("[INTERGRID]: Failed to inform neighbors that I'm here."); + } + } + + /// + /// Called by scene when region is initialized (not always when it's listening for agents) + /// This is an inter-region message that informs the surrounding neighbors that the sim is up. + /// + public void InformNeighborsThatRegionisUp(RegionInfo region) + { + //m_log.Info("[INTER]: " + debugRegionName + ": SceneCommunicationService: Sending InterRegion Notification that region is up " + region.RegionName); + + + List neighbours = new List(); + // This stays uncached because we don't already know about our neighbors at this point. + neighbours = m_commsProvider.GridService.RequestNeighbours(m_regionInfo.RegionLocX, m_regionInfo.RegionLocY); + if (neighbours != null) + { + for (int i = 0; i < neighbours.Count; i++) + { + InformNeighbourThatRegionUpDelegate d = InformNeighboursThatRegionIsUpAsync; + + d.BeginInvoke(region, neighbours[i].RegionHandle, + InformNeighborsThatRegionisUpCompleted, + d); + } + } + + //bool val = m_commsProvider.InterRegion.RegionUp(new SerializableRegionInfo(region)); + } + + public delegate void SendChildAgentDataUpdateDelegate(AgentPosition cAgentData, ulong regionHandle); + + /// + /// This informs all neighboring regions about the settings of it's child agent. + /// Calls an asynchronous method to do so.. so it doesn't lag the sim. + /// + /// This contains information, such as, Draw Distance, Camera location, Current Position, Current throttle settings, etc. + /// + /// + private void SendChildAgentDataUpdateAsync(AgentPosition cAgentData, ulong regionHandle) + { + //m_log.Info("[INTERGRID]: Informing neighbors about my agent in " + m_regionInfo.RegionName); + try + { + //m_commsProvider.InterRegion.ChildAgentUpdate(regionHandle, cAgentData); + m_interregionCommsOut.SendChildAgentUpdate(regionHandle, cAgentData); + } + catch + { + // Ignore; we did our best + } + + //if (regionAccepted) + //{ + // //m_log.Info("[INTERGRID]: Completed sending a neighbor an update about my agent"); + //} + //else + //{ + // //m_log.Info("[INTERGRID]: Failed sending a neighbor an update about my agent"); + //} + + } + + private void SendChildAgentDataUpdateCompleted(IAsyncResult iar) + { + SendChildAgentDataUpdateDelegate icon = (SendChildAgentDataUpdateDelegate) iar.AsyncState; + icon.EndInvoke(iar); + } + + public void SendChildAgentDataUpdate(AgentPosition cAgentData, ScenePresence presence) + { + // This assumes that we know what our neighbors are. + try + { + foreach (ulong regionHandle in presence.KnownChildRegionHandles) + { + if (regionHandle != m_regionInfo.RegionHandle) + { + SendChildAgentDataUpdateDelegate d = SendChildAgentDataUpdateAsync; + d.BeginInvoke(cAgentData, regionHandle, + SendChildAgentDataUpdateCompleted, + d); + } + } + } + catch (InvalidOperationException) + { + // We're ignoring a collection was modified error because this data gets old and outdated fast. + } + + } + + public delegate void SendCloseChildAgentDelegate(UUID agentID, ulong regionHandle); + + /// + /// This Closes child agents on neighboring regions + /// Calls an asynchronous method to do so.. so it doesn't lag the sim. + /// + protected void SendCloseChildAgentAsync(UUID agentID, ulong regionHandle) + { + + m_log.Debug("[INTERGRID]: Sending close agent to " + regionHandle); + // let's do our best, but there's not much we can do if the neighbour doesn't accept. + + //m_commsProvider.InterRegion.TellRegionToCloseChildConnection(regionHandle, agentID); + m_interregionCommsOut.SendCloseAgent(regionHandle, agentID); + } + + private void SendCloseChildAgentCompleted(IAsyncResult iar) + { + SendCloseChildAgentDelegate icon = (SendCloseChildAgentDelegate)iar.AsyncState; + icon.EndInvoke(iar); + } + + public void SendCloseChildAgentConnections(UUID agentID, List regionslst) + { + foreach (ulong handle in regionslst) + { + SendCloseChildAgentDelegate d = SendCloseChildAgentAsync; + d.BeginInvoke(agentID, handle, + SendCloseChildAgentCompleted, + d); + } + } + + /// + /// Helper function to request neighbors from grid-comms + /// + /// + /// + public virtual RegionInfo RequestNeighbouringRegionInfo(ulong regionHandle) + { + //m_log.Info("[INTER]: " + debugRegionName + ": SceneCommunicationService: Sending Grid Services Request about neighbor " + regionHandle.ToString()); + return m_commsProvider.GridService.RequestNeighbourInfo(regionHandle); + } + + /// + /// Helper function to request neighbors from grid-comms + /// + /// + /// + public virtual RegionInfo RequestNeighbouringRegionInfo(UUID regionID) + { + //m_log.Info("[INTER]: " + debugRegionName + ": SceneCommunicationService: Sending Grid Services Request about neighbor " + regionID); + return m_commsProvider.GridService.RequestNeighbourInfo(regionID); + } + + /// + /// Requests map blocks in area of minX, maxX, minY, MaxY in world cordinates + /// + /// + /// + /// + /// + public virtual void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY) + { + List mapBlocks; + mapBlocks = m_commsProvider.GridService.RequestNeighbourMapBlocks(minX - 4, minY - 4, minX + 4, minY + 4); + remoteClient.SendMapBlock(mapBlocks, 0); + } + + /// + /// Try to teleport an agent to a new region. + /// + /// + /// + /// + /// + /// + public virtual void RequestTeleportToLocation(ScenePresence avatar, ulong regionHandle, Vector3 position, + Vector3 lookAt, uint teleportFlags) + { + if (!avatar.Scene.Permissions.CanTeleport(avatar.UUID)) + return; + + bool destRegionUp = true; + + IEventQueue eq = avatar.Scene.RequestModuleInterface(); + + if (regionHandle == m_regionInfo.RegionHandle) + { + m_log.DebugFormat( + "[SCENE COMMUNICATION SERVICE]: RequestTeleportToLocation {0} within {1}", + position, m_regionInfo.RegionName); + + // Teleport within the same region + if (position.X < 0 || position.X > Constants.RegionSize || position.Y < 0 || position.Y > Constants.RegionSize || position.Z < 0) + { + Vector3 emergencyPos = new Vector3(128, 128, 128); + + m_log.WarnFormat( + "[SCENE COMMUNICATION SERVICE]: RequestTeleportToLocation() was given an illegal position of {0} for avatar {1}, {2}. Substituting {3}", + position, avatar.Name, avatar.UUID, emergencyPos); + position = emergencyPos; + } + + // TODO: Get proper AVG Height + float localAVHeight = 1.56f; + float posZLimit = (float)avatar.Scene.GetLandHeight((int)position.X, (int)position.Y); + float newPosZ = posZLimit + localAVHeight; + if (posZLimit >= (position.Z - (localAVHeight / 2)) && !(Single.IsInfinity(newPosZ) || Single.IsNaN(newPosZ))) + { + position.Z = newPosZ; + } + + // Only send this if the event queue is null + if (eq == null) + avatar.ControllingClient.SendTeleportLocationStart(); + + avatar.ControllingClient.SendLocalTeleport(position, lookAt, teleportFlags); + avatar.Teleport(position); + } + else + { + RegionInfo reg = RequestNeighbouringRegionInfo(regionHandle); + if (reg != null) + { + m_log.DebugFormat( + "[SCENE COMMUNICATION SERVICE]: RequestTeleportToLocation to {0} in {1}", + position, reg.RegionName); + + if (eq == null) + avatar.ControllingClient.SendTeleportLocationStart(); + + // Let's do DNS resolution only once in this process, please! + // This may be a costly operation. The reg.ExternalEndPoint field is not a passive field, + // it's actually doing a lot of work. + IPEndPoint endPoint = reg.ExternalEndPoint; + if (endPoint.Address == null) + { + // Couldn't resolve the name. Can't TP, because the viewer wants IP addresses. + destRegionUp = false; + } + + if (destRegionUp) + { + uint newRegionX = (uint)(reg.RegionHandle >> 40); + uint newRegionY = (((uint)(reg.RegionHandle)) >> 8); + uint oldRegionX = (uint)(m_regionInfo.RegionHandle >> 40); + uint oldRegionY = (((uint)(m_regionInfo.RegionHandle)) >> 8); + + // Fixing a bug where teleporting while sitting results in the avatar ending up removed from + // both regions + if (avatar.ParentID != (uint)0) + avatar.StandUp(); + + if (!avatar.ValidateAttachments()) + { + avatar.ControllingClient.SendTeleportFailed("Inconsistent attachment state"); + return; + } + + // the avatar.Close below will clear the child region list. We need this below for (possibly) + // closing the child agents, so save it here (we need a copy as it is Clear()-ed). + //List childRegions = new List(avatar.GetKnownRegionList()); + // Compared to ScenePresence.CrossToNewRegion(), there's no obvious code to handle a teleport + // failure at this point (unlike a border crossing failure). So perhaps this can never fail + // once we reach here... + //avatar.Scene.RemoveCapsHandler(avatar.UUID); + + string capsPath = String.Empty; + AgentCircuitData agentCircuit = avatar.ControllingClient.RequestClientInfo(); + agentCircuit.BaseFolder = UUID.Zero; + agentCircuit.InventoryFolder = UUID.Zero; + agentCircuit.startpos = position; + agentCircuit.child = true; + + if (Util.IsOutsideView(oldRegionX, newRegionX, oldRegionY, newRegionY)) + { + // brand new agent, let's create a new caps seed + agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath(); + } + + // Let's create an agent there if one doesn't exist yet. + //if (!m_commsProvider.InterRegion.InformRegionOfChildAgent(reg.RegionHandle, agentCircuit)) + if (!m_interregionCommsOut.SendCreateChildAgent(reg.RegionHandle, agentCircuit)) + { + avatar.ControllingClient.SendTeleportFailed("Destination is not accepting teleports."); + return; + } + + // OK, it got this agent. Let's close some child agents + avatar.CloseChildAgents(newRegionX, newRegionY); + + if (Util.IsOutsideView(oldRegionX, newRegionX, oldRegionY, newRegionY)) + { + capsPath + = "http://" + + reg.ExternalHostName + + ":" + + reg.HttpPort + + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath); + + if (eq != null) + { + eq.EnableSimulator(reg.RegionHandle, endPoint, avatar.UUID); + + // ES makes the client send a UseCircuitCode message to the destination, + // which triggers a bunch of things there. + // So let's wait + Thread.Sleep(2000); + + eq.EstablishAgentCommunication(avatar.UUID, endPoint, capsPath); + } + else + { + avatar.ControllingClient.InformClientOfNeighbour(reg.RegionHandle, endPoint); + } + } + else + { + agentCircuit.CapsPath = avatar.Scene.CapsModule.GetChildSeed(avatar.UUID, reg.RegionHandle); + capsPath = "http://" + reg.ExternalHostName + ":" + reg.HttpPort + + "/CAPS/" + agentCircuit.CapsPath + "0000/"; + } + + // Expect avatar crossing is a heavy-duty function at the destination. + // That is where MakeRoot is called, which fetches appearance and inventory. + // Plus triggers OnMakeRoot, which spawns a series of asynchronous updates. + //m_commsProvider.InterRegion.ExpectAvatarCrossing(reg.RegionHandle, avatar.ControllingClient.AgentId, + // position, false); + + //{ + // avatar.ControllingClient.SendTeleportFailed("Problem with destination."); + // // We should close that agent we just created over at destination... + // List lst = new List(); + // lst.Add(reg.RegionHandle); + // SendCloseChildAgentAsync(avatar.UUID, lst); + // return; + //} + + SetInTransit(avatar.UUID); + // Let's send a full update of the agent. This is a synchronous call. + AgentData agent = new AgentData(); + avatar.CopyTo(agent); + agent.Position = position; + agent.CallbackURI = "http://" + m_regionInfo.ExternalHostName + ":" + m_regionInfo.HttpPort + + "/agent/" + avatar.UUID.ToString() + "/" + avatar.Scene.RegionInfo.RegionHandle.ToString() + "/release/"; + + m_interregionCommsOut.SendChildAgentUpdate(reg.RegionHandle, agent); + + m_log.DebugFormat( + "[CAPS]: Sending new CAPS seed url {0} to client {1}", capsPath, avatar.UUID); + + + if (eq != null) + { + eq.TeleportFinishEvent(reg.RegionHandle, 13, endPoint, + 4, teleportFlags, capsPath, avatar.UUID); + } + else + { + avatar.ControllingClient.SendRegionTeleport(reg.RegionHandle, 13, endPoint, 4, + teleportFlags, capsPath); + } + + // TeleportFinish makes the client send CompleteMovementIntoRegion (at the destination), which + // trigers a whole shebang of things there, including MakeRoot. So let's wait for confirmation + // that the client contacted the destination before we send the attachments and close things here. + if (!WaitForCallback(avatar.UUID)) + { + // Client never contacted destination. Let's restore everything back + avatar.ControllingClient.SendTeleportFailed("Problems connecting to destination."); + + ResetFromTransit(avatar.UUID); + + // Yikes! We should just have a ref to scene here. + avatar.Scene.InformClientOfNeighbours(avatar); + + // Finally, kill the agent we just created at the destination. + m_interregionCommsOut.SendCloseAgent(reg.RegionHandle, avatar.UUID); + + return; + } + + // Can't go back from here + if (KiPrimitive != null) + { + KiPrimitive(avatar.LocalId); + } + + avatar.MakeChildAgent(); + + // CrossAttachmentsIntoNewRegion is a synchronous call. We shouldn't need to wait after it + avatar.CrossAttachmentsIntoNewRegion(reg.RegionHandle, true); + + // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone + + if (Util.IsOutsideView(oldRegionX, newRegionX, oldRegionY, newRegionY)) + { + Thread.Sleep(5000); + avatar.Close(); + CloseConnection(avatar.UUID); + } + + // if (teleport success) // seems to be always success here + // the user may change their profile information in other region, + // so the userinfo in UserProfileCache is not reliable any more, delete it + if (avatar.Scene.NeedSceneCacheClear(avatar.UUID)) + { + m_commsProvider.UserProfileCacheService.RemoveUser(avatar.UUID); + m_log.DebugFormat( + "[SCENE COMMUNICATION SERVICE]: User {0} is going to another region, profile cache removed", + avatar.UUID); + } + } + else + { + avatar.ControllingClient.SendTeleportFailed("Remote Region appears to be down"); + } + } + else + { + // TP to a place that doesn't exist (anymore) + // Inform the viewer about that + avatar.ControllingClient.SendTeleportFailed("The region you tried to teleport to doesn't exist anymore"); + + // and set the map-tile to '(Offline)' + uint regX, regY; + Utils.LongToUInts(regionHandle, out regX, out regY); + + MapBlockData block = new MapBlockData(); + block.X = (ushort)(regX / Constants.RegionSize); + block.Y = (ushort)(regY / Constants.RegionSize); + block.Access = 254; // == not there + + List blocks = new List(); + blocks.Add(block); + avatar.ControllingClient.SendMapBlock(blocks, 0); + } + } + } + + protected bool WaitForCallback(UUID id) + { + int count = 20; + while (m_agentsInTransit.Contains(id) && count-- > 0) + { + //Console.WriteLine(" >>> Waiting... " + count); + Thread.Sleep(1000); + } + + if (count > 0) + return true; + else + return false; + } + + public bool ReleaseAgent(UUID id) + { + //Console.WriteLine(" >>> ReleaseAgent called <<< "); + return ResetFromTransit(id); + } + + protected void SetInTransit(UUID id) + { + lock (m_agentsInTransit) + { + if (!m_agentsInTransit.Contains(id)) + m_agentsInTransit.Add(id); + } + } + + protected bool ResetFromTransit(UUID id) + { + lock (m_agentsInTransit) + { + if (m_agentsInTransit.Contains(id)) + { + m_agentsInTransit.Remove(id); + return true; + } + } + return false; + } + + private List NeighbourHandles(List neighbours) + { + List handles = new List(); + foreach (SimpleRegionInfo reg in neighbours) + { + handles.Add(reg.RegionHandle); + } + return handles; + } + + private List NewNeighbours(List currentNeighbours, List previousNeighbours) + { + return currentNeighbours.FindAll(delegate(ulong handle) { return !previousNeighbours.Contains(handle); }); + } + +// private List CommonNeighbours(List currentNeighbours, List previousNeighbours) +// { +// return currentNeighbours.FindAll(delegate(ulong handle) { return previousNeighbours.Contains(handle); }); +// } + + private List OldNeighbours(List currentNeighbours, List previousNeighbours) + { + return previousNeighbours.FindAll(delegate(ulong handle) { return !currentNeighbours.Contains(handle); }); + } + /// + /// Inform a neighbouring region that an avatar is about to cross into it. + /// + /// + /// + /// + public bool CrossToNeighbouringRegion(ulong regionhandle, UUID agentID, Vector3 position, bool isFlying) + { + return m_commsProvider.InterRegion.ExpectAvatarCrossing(regionhandle, agentID, position, isFlying); + } + + public bool PrimCrossToNeighboringRegion(ulong regionhandle, UUID primID, string objData, int XMLMethod) + { + return m_commsProvider.InterRegion.InformRegionOfPrimCrossing(regionhandle, primID, objData, XMLMethod); + } + + public Dictionary GetGridSettings() + { + return m_commsProvider.GridService.GetGridSettings(); + } + + public void LogOffUser(UUID userid, UUID regionid, ulong regionhandle, Vector3 position, Vector3 lookat) + { + m_commsProvider.LogOffUser(userid, regionid, regionhandle, position, lookat); + } + + // deprecated as of 2008-08-27 + public void LogOffUser(UUID userid, UUID regionid, ulong regionhandle, float posx, float posy, float posz) + { + m_commsProvider.LogOffUser(userid, regionid, regionhandle, posx, posy, posz); + } + + public void ClearUserAgent(UUID avatarID) + { + m_commsProvider.UserService.ClearUserAgent(avatarID); + } + + public void AddNewUserFriend(UUID friendlistowner, UUID friend, uint perms) + { + m_commsProvider.AddNewUserFriend(friendlistowner, friend, perms); + } + + public void UpdateUserFriendPerms(UUID friendlistowner, UUID friend, uint perms) + { + m_commsProvider.UpdateUserFriendPerms(friendlistowner, friend, perms); + } + + public void RemoveUserFriend(UUID friendlistowner, UUID friend) + { + m_commsProvider.RemoveUserFriend(friendlistowner, friend); + } + + public List GetUserFriendList(UUID friendlistowner) + { + return m_commsProvider.GetUserFriendList(friendlistowner); + } + + public List RequestNeighbourMapBlocks(int minX, int minY, int maxX, int maxY) + { + return m_commsProvider.GridService.RequestNeighbourMapBlocks(minX, minY, maxX, maxY); + } + + public List GenerateAgentPickerRequestResponse(UUID queryID, string query) + { + return m_commsProvider.GenerateAgentPickerRequestResponse(queryID, query); + } + + public List RequestNamedRegions(string name, int maxNumber) + { + return m_commsProvider.GridService.RequestNamedRegions(name, maxNumber); + } + +// private void Dump(string msg, List handles) +// { +// Console.WriteLine("-------------- HANDLE DUMP ({0}) ---------", msg); +// foreach (ulong handle in handles) +// { +// uint x, y; +// Utils.LongToUInts(handle, out x, out y); +// x = x / Constants.RegionSize; +// y = y / Constants.RegionSize; +// Console.WriteLine("({0}, {1})", x, y); +// } +// } + } +} diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs new file mode 100644 index 0000000..2877dcd --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -0,0 +1,1814 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using OpenMetaverse.Packets; +using log4net; +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes.Types; +using OpenSim.Region.Physics.Manager; + +namespace OpenSim.Region.Framework.Scenes +{ + public delegate void PhysicsCrash(); + + public delegate void ObjectDuplicateDelegate(EntityBase original, EntityBase clone); + + public delegate void ObjectCreateDelegate(EntityBase obj); + + public delegate void ObjectDeleteDelegate(EntityBase obj); + + /// + /// This class used to be called InnerScene and may not yet truly be a SceneGraph. The non scene graph components + /// should be migrated out over time. + /// + public class SceneGraph + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + #region Events + + protected internal event PhysicsCrash UnRecoverableError; + private PhysicsCrash handlerPhysicsCrash = null; + + public event ObjectDuplicateDelegate OnObjectDuplicate; + public event ObjectCreateDelegate OnObjectCreate; + public event ObjectDeleteDelegate OnObjectRemove; + + #endregion + + #region Fields + + protected internal Dictionary ScenePresences = new Dictionary(); + // SceneObjects is not currently populated or used. + //public Dictionary SceneObjects; + protected internal EntityManager Entities = new EntityManager(); +// protected internal Dictionary Entities = new Dictionary(); + protected internal Dictionary RestorePresences = new Dictionary(); + + protected internal BasicQuadTreeNode QuadTree; + + protected RegionInfo m_regInfo; + protected Scene m_parentScene; + protected List m_updateList = new List(); + protected int m_numRootAgents = 0; + protected int m_numPrim = 0; + protected int m_numChildAgents = 0; + protected int m_physicalPrim = 0; + + protected int m_activeScripts = 0; + protected int m_scriptLPS = 0; + + protected internal object m_syncRoot = new object(); + + protected internal PhysicsScene _PhyScene; + + #endregion + + protected internal SceneGraph(Scene parent, RegionInfo regInfo) + { + m_parentScene = parent; + m_regInfo = regInfo; + QuadTree = new BasicQuadTreeNode(null, "/0/", 0, 0, (short)Constants.RegionSize, (short)Constants.RegionSize); + QuadTree.Subdivide(); + QuadTree.Subdivide(); + } + + public PhysicsScene PhysicsScene + { + get { return _PhyScene; } + set + { + // If we're not doing the initial set + // Then we've got to remove the previous + // event handler + + if (_PhyScene != null) + _PhyScene.OnPhysicsCrash -= physicsBasedCrash; + + _PhyScene = value; + + if (_PhyScene != null) + _PhyScene.OnPhysicsCrash += physicsBasedCrash; + } + } + + protected internal void Close() + { + lock (ScenePresences) + { + ScenePresences.Clear(); + } + + Entities.Clear(); + } + + #region Update Methods + + protected internal void UpdatePreparePhysics() + { + // If we are using a threaded physics engine + // grab the latest scene from the engine before + // trying to process it. + + // PhysX does this (runs in the background). + + if (_PhyScene.IsThreaded) + { + _PhyScene.GetResults(); + } + } + + protected internal void UpdateEntities() + { + List updateEntities = GetEntities(); + + foreach (EntityBase entity in updateEntities) + { + entity.Update(); + } + } + + protected internal void UpdatePresences() + { + List updateScenePresences = GetScenePresences(); + foreach (ScenePresence pres in updateScenePresences) + { + pres.Update(); + } + } + + protected internal float UpdatePhysics(double elapsed) + { + lock (m_syncRoot) + { + // Here is where the Scene calls the PhysicsScene. This is a one-way + // interaction; the PhysicsScene cannot access the calling Scene directly. + // But with joints, we want a PhysicsActor to be able to influence a + // non-physics SceneObjectPart. In particular, a PhysicsActor that is connected + // with a joint should be able to move the SceneObjectPart which is the visual + // representation of that joint (for editing and serialization purposes). + // However the PhysicsActor normally cannot directly influence anything outside + // of the PhysicsScene, and the non-physical SceneObjectPart which represents + // the joint in the Scene does not exist in the PhysicsScene. + // + // To solve this, we have an event in the PhysicsScene that is fired when a joint + // has changed position (because one of its associated PhysicsActors has changed + // position). + // + // Therefore, JointMoved and JointDeactivated events will be fired as a result of the following Simulate(). + + return _PhyScene.Simulate((float)elapsed); + } + } + + protected internal void UpdateEntityMovement() + { + List moveEntities = GetEntities(); + + foreach (EntityBase entity in moveEntities) + { + //cfk. This throws occaisional exceptions on a heavily used region + //and I added this null check to try to preclude the exception. + if (entity != null) + entity.UpdateMovement(); + } + } + + #endregion + + #region Entity Methods + + /// + /// Add an object into the scene that has come from storage + /// + /// + /// + /// If true, changes to the object will be reflected in its persisted data + /// If false, the persisted data will not be changed even if the object in the scene is changed + /// + /// + /// If true, we won't persist this object until it changes + /// If false, we'll persist this object immediately + /// + /// + /// true if the object was added, false if an object with the same uuid was already in the scene + /// + protected internal bool AddRestoredSceneObject( + SceneObjectGroup sceneObject, bool attachToBackup, bool alreadyPersisted) + { + if (!alreadyPersisted) + { + sceneObject.ForceInventoryPersistence(); + sceneObject.HasGroupChanged = true; + } + + return AddSceneObject(sceneObject, attachToBackup); + } + + /// + /// Add a newly created object to the scene. This will both update the scene, and send information about the + /// new object to all clients interested in the scene. + /// + /// + /// + /// If true, the object is made persistent into the scene. + /// If false, the object will not persist over server restarts + /// + /// + /// true if the object was added, false if an object with the same uuid was already in the scene + /// + protected internal bool AddNewSceneObject(SceneObjectGroup sceneObject, bool attachToBackup) + { + // Ensure that we persist this new scene object + sceneObject.HasGroupChanged = true; + + return AddSceneObject(sceneObject, attachToBackup); + } + + /// + /// Add an object to the scene. This will both update the scene, and send information about the + /// new object to all clients interested in the scene. + /// + /// + /// + /// If true, the object is made persistent into the scene. + /// If false, the object will not persist over server restarts + /// + /// true if the object was added, false if an object with the same uuid was already in the scene + /// + protected bool AddSceneObject(SceneObjectGroup sceneObject, bool attachToBackup) + { + if (sceneObject == null || sceneObject.RootPart == null || sceneObject.RootPart.UUID == UUID.Zero) + return false; + + if (m_parentScene.m_clampPrimSize) + { + foreach (SceneObjectPart part in sceneObject.Children.Values) + { + Vector3 scale = part.Shape.Scale; + + if (scale.X > m_parentScene.m_maxNonphys) + scale.X = m_parentScene.m_maxNonphys; + if (scale.Y > m_parentScene.m_maxNonphys) + scale.Y = m_parentScene.m_maxNonphys; + if (scale.Z > m_parentScene.m_maxNonphys) + scale.Z = m_parentScene.m_maxNonphys; + + part.Shape.Scale = scale; + } + } + + sceneObject.AttachToScene(m_parentScene); + + lock (sceneObject) + { + if (!Entities.ContainsKey(sceneObject.UUID)) + { + Entities.Add(sceneObject); + m_numPrim += sceneObject.Children.Count; + + if (attachToBackup) + sceneObject.AttachToBackup(); + + if (OnObjectCreate != null) + OnObjectCreate(sceneObject); + + return true; + } + } + + return false; + } + + /// + /// Delete an object from the scene + /// + /// true if the object was deleted, false if there was no object to delete + public bool DeleteSceneObject(UUID uuid, bool resultOfObjectLinked) + { + if (Entities.ContainsKey(uuid)) + { + if (!resultOfObjectLinked) + { + m_numPrim -= ((SceneObjectGroup) Entities[uuid]).Children.Count; + + if ((((SceneObjectGroup)Entities[uuid]).RootPart.Flags & PrimFlags.Physics) == PrimFlags.Physics) + { + RemovePhysicalPrim(((SceneObjectGroup)Entities[uuid]).Children.Count); + } + } + + if (OnObjectRemove != null) + OnObjectRemove(Entities[uuid]); + + Entities.Remove(uuid); + //SceneObjectGroup part; + //((part.RootPart.Flags & PrimFlags.Physics) == PrimFlags.Physics) + + return true; + } + + return false; + } + + /// + /// Add an entity to the list of prims to process on the next update + /// + /// + /// A + /// + protected internal void AddToUpdateList(EntityBase obj) + { + lock (m_updateList) + { + if (!m_updateList.Contains(obj)) + { + m_updateList.Add(obj); + } + } + } + + /// + /// Process all pending updates + /// + protected internal void ProcessUpdates() + { + lock (m_updateList) + { + for (int i = 0; i < m_updateList.Count; i++) + { + EntityBase entity = m_updateList[i]; + + // Don't abort the whole update if one entity happens to give us an exception. + try + { + m_updateList[i].Update(); + } + catch (Exception e) + { + m_log.ErrorFormat( + "[INNER SCENE]: Failed to update {0}, {1} - {2}", entity.Name, entity.UUID, e); + } + } + + m_updateList.Clear(); + } + } + + protected internal void AddPhysicalPrim(int number) + { + m_physicalPrim++; + } + + protected internal void RemovePhysicalPrim(int number) + { + m_physicalPrim--; + } + + protected internal void AddToScriptLPS(int number) + { + m_scriptLPS += number; + } + + protected internal void AddActiveScripts(int number) + { + m_activeScripts += number; + } + + protected internal void DropObject(uint objectLocalID, IClientAPI remoteClient) + { + List EntityList = GetEntities(); + + foreach (EntityBase obj in EntityList) + { + if (obj is SceneObjectGroup) + { + if (((SceneObjectGroup)obj).LocalId == objectLocalID) + { + SceneObjectGroup group = (SceneObjectGroup)obj; + + m_parentScene.DetachSingleAttachmentToGround(group.UUID,remoteClient); + } + } + } + } + + protected internal void DetachObject(uint objectLocalID, IClientAPI remoteClient) + { + List EntityList = GetEntities(); + + foreach (EntityBase obj in EntityList) + { + if (obj is SceneObjectGroup) + { + if (((SceneObjectGroup)obj).LocalId == objectLocalID) + { + SceneObjectGroup group = (SceneObjectGroup)obj; + + //group.DetachToGround(); + m_parentScene.DetachSingleAttachmentToInv(group.GetFromAssetID(),remoteClient); + } + } + } + } + + protected internal void HandleUndo(IClientAPI remoteClient, UUID primId) + { + if (primId != UUID.Zero) + { + SceneObjectPart part = m_parentScene.GetSceneObjectPart(primId); + if (part != null) + part.Undo(); + } + } + + protected internal void HandleObjectGroupUpdate( + IClientAPI remoteClient, UUID GroupID, uint objectLocalID, UUID Garbage) + { + List EntityList = GetEntities(); + + foreach (EntityBase obj in EntityList) + { + if (obj is SceneObjectGroup) + { + if (((SceneObjectGroup)obj).LocalId == objectLocalID) + { + SceneObjectGroup group = (SceneObjectGroup)obj; + + if (group.OwnerID == remoteClient.AgentId) + group.SetGroup(GroupID, remoteClient); + } + } + } + } + + /// + /// Event Handling routine for Attach Object + /// + /// + /// + /// + /// + protected internal void AttachObject(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, Quaternion rot, bool silent) + { + // If we can't take it, we can't attach it! + // + SceneObjectPart part = m_parentScene.GetSceneObjectPart(objectLocalID); + if (part == null) + return; + + if (!m_parentScene.Permissions.CanTakeObject( + part.UUID, remoteClient.AgentId)) + return; + + // Calls attach with a Zero position + // + AttachObject(remoteClient, objectLocalID, AttachmentPt, rot, Vector3.Zero, false); + } + + public SceneObjectGroup RezSingleAttachment( + IClientAPI remoteClient, UUID itemID, uint AttachmentPt) + { + SceneObjectGroup objatt = m_parentScene.RezObject(remoteClient, + itemID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true, + false, false, remoteClient.AgentId, true); + + + if (objatt != null) + { + bool tainted = false; + if (AttachmentPt != 0 && AttachmentPt != objatt.GetAttachmentPoint()) + tainted = true; + + AttachObject(remoteClient, objatt.LocalId, AttachmentPt, Quaternion.Identity, objatt.AbsolutePosition, false); + objatt.ScheduleGroupForFullUpdate(); + if (tainted) + objatt.HasGroupChanged = true; + + // Fire after attach, so we don't get messy perms dialogs + // + objatt.CreateScriptInstances(0, true, m_parentScene.DefaultScriptEngine, 0); + } + return objatt; + } + + // What makes this method odd and unique is it tries to detach using an UUID.... Yay for standards. + // To LocalId or UUID, *THAT* is the question. How now Brown UUID?? + public void DetachSingleAttachmentToInv(UUID itemID, IClientAPI remoteClient) + { + if (itemID == UUID.Zero) // If this happened, someone made a mistake.... + return; + + List EntityList = GetEntities(); + + foreach (EntityBase obj in EntityList) + { + if (obj is SceneObjectGroup) + { + if (((SceneObjectGroup)obj).GetFromAssetID() == itemID) + { + SceneObjectGroup group = (SceneObjectGroup)obj; + group.DetachToInventoryPrep(); + m_log.Debug("[DETACH]: Saving attachpoint: " + ((uint)group.GetAttachmentPoint()).ToString()); + m_parentScene.updateKnownAsset(remoteClient, group, group.GetFromAssetID(), group.OwnerID); + m_parentScene.DeleteSceneObject(group, false); + } + } + } + } + + protected internal void AttachObject( + IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, Quaternion rot, Vector3 attachPos, bool silent) + { + List EntityList = GetEntities(); + foreach (EntityBase obj in EntityList) + { + if (obj is SceneObjectGroup) + { + if (((SceneObjectGroup)obj).LocalId == objectLocalID) + { + SceneObjectGroup group = (SceneObjectGroup)obj; + if (m_parentScene.Permissions.CanTakeObject(obj.UUID, remoteClient.AgentId)) + { + // If the attachment point isn't the same as the one previously used + // set it's offset position = 0 so that it appears on the attachment point + // and not in a weird location somewhere unknown. + if (AttachmentPt != 0 && AttachmentPt != (uint)group.GetAttachmentPoint()) + { + + attachPos = Vector3.Zero; + } + + // AttachmentPt 0 means the client chose to 'wear' the attachment. + if (AttachmentPt == 0) + { + + // Check object for stored attachment point + AttachmentPt = (uint)group.GetAttachmentPoint(); + + + } + + // if we still didn't find a suitable attachment point....... + if (AttachmentPt == 0) + { + // Stick it on left hand with Zero Offset from the attachment point. + AttachmentPt = (uint)AttachmentPoint.LeftHand; + attachPos = Vector3.Zero; + } + + group.SetAttachmentPoint(Convert.ToByte(AttachmentPt)); + group.AbsolutePosition = attachPos; + + // Saves and gets assetID + UUID itemId; + if (group.GetFromAssetID() == UUID.Zero) + { + m_parentScene.attachObjectAssetStore(remoteClient, group, remoteClient.AgentId, out itemId); + } + else + { + itemId = group.GetFromAssetID(); + } + + m_parentScene.AttachObject(remoteClient, AttachmentPt, itemId, group); + + group.AttachToAgent(remoteClient.AgentId, AttachmentPt, attachPos, silent); + // In case it is later dropped again, don't let + // it get cleaned up + // + group.RootPart.RemFlag(PrimFlags.TemporaryOnRez); + group.HasGroupChanged = false; + } + else + { + remoteClient.SendAgentAlertMessage("You don't have sufficient permissions to attach this object", false); + } + + } + } + } + } + + protected internal ScenePresence CreateAndAddChildScenePresence(IClientAPI client, AvatarAppearance appearance) + { + ScenePresence newAvatar = null; + + newAvatar = new ScenePresence(client, m_parentScene, m_regInfo, appearance); + newAvatar.IsChildAgent = true; + + AddScenePresence(newAvatar); + + return newAvatar; + } + + /// + /// Add a presence to the scene + /// + /// + protected internal void AddScenePresence(ScenePresence presence) + { + bool child = presence.IsChildAgent; + + if (child) + { + m_numChildAgents++; + } + else + { + m_numRootAgents++; + presence.AddToPhysicalScene(); + } + + Entities[presence.UUID] = presence; + + lock (ScenePresences) + { + ScenePresences[presence.UUID] = presence; + } + } + + /// + /// Remove a presence from the scene + /// + protected internal void RemoveScenePresence(UUID agentID) + { + if (!Entities.Remove(agentID)) + { + m_log.WarnFormat( + "[SCENE] Tried to remove non-existent scene presence with agent ID {0} from scene Entities list", + agentID); + } + + lock (ScenePresences) + { + if (!ScenePresences.Remove(agentID)) + { + m_log.WarnFormat("[SCENE] Tried to remove non-existent scene presence with agent ID {0} from scene ScenePresences list", agentID); + } +// else +// { +// m_log.InfoFormat("[SCENE] Removed scene presence {0} from scene presences list", agentID); +// } + } + } + + protected internal void SwapRootChildAgent(bool direction_RC_CR_T_F) + { + if (direction_RC_CR_T_F) + { + m_numRootAgents--; + m_numChildAgents++; + } + else + { + m_numChildAgents--; + m_numRootAgents++; + } + } + + protected internal void removeUserCount(bool TypeRCTF) + { + if (TypeRCTF) + { + m_numRootAgents--; + } + else + { + m_numChildAgents--; + } + } + + public void RecalculateStats() + { + List SPList = GetScenePresences(); + int rootcount = 0; + int childcount = 0; + + foreach (ScenePresence user in SPList) + { + if (user.IsChildAgent) + childcount++; + else + rootcount++; + } + m_numRootAgents = rootcount; + m_numChildAgents = childcount; + + } + + public int GetChildAgentCount() + { + // some network situations come in where child agents get closed twice. + if (m_numChildAgents < 0) + { + m_numChildAgents = 0; + } + + return m_numChildAgents; + } + + public int GetRootAgentCount() + { + return m_numRootAgents; + } + + public int GetTotalObjectsCount() + { + return m_numPrim; + } + + public int GetActiveObjectsCount() + { + return m_physicalPrim; + } + + public int GetActiveScriptsCount() + { + return m_activeScripts; + } + + public int GetScriptLPS() + { + int returnval = m_scriptLPS; + m_scriptLPS = 0; + return returnval; + } + + #endregion + + #region Get Methods + + /// + /// Request a List of all scene presences in this scene. This is a new list, so no + /// locking is required to iterate over it. + /// + /// + protected internal List GetScenePresences() + { + lock (ScenePresences) + { + return new List(ScenePresences.Values); + } + } + + protected internal List GetAvatars() + { + List result = + GetScenePresences(delegate(ScenePresence scenePresence) { return !scenePresence.IsChildAgent; }); + + return result; + } + + /// + /// Get the controlling client for the given avatar, if there is one. + /// + /// FIXME: The only user of the method right now is Caps.cs, in order to resolve a client API since it can't + /// use the ScenePresence. This could be better solved in a number of ways - we could establish an + /// OpenSim.Framework.IScenePresence, or move the caps code into a region package (which might be the more + /// suitable solution). + /// + /// + /// null if either the avatar wasn't in the scene, or + /// they do not have a controlling client + /// this used to be protected internal, but that + /// prevents CapabilitiesModule from accessing it + public IClientAPI GetControllingClient(UUID agentId) + { + ScenePresence presence = GetScenePresence(agentId); + + if (presence != null) + { + return presence.ControllingClient; + } + + return null; + } + + /// + /// Request a filtered list of m_scenePresences in this World + /// + /// + protected internal List GetScenePresences(FilterAvatarList filter) + { + // No locking of scene presences here since we're passing back a list... + + List result = new List(); + List ScenePresencesList = GetScenePresences(); + + foreach (ScenePresence avatar in ScenePresencesList) + { + if (filter(avatar)) + { + result.Add(avatar); + } + } + + return result; + } + + /// + /// Request a scene presence by UUID + /// + /// + /// null if the agent was not found + protected internal ScenePresence GetScenePresence(UUID agentID) + { + ScenePresence sp; + + lock (ScenePresences) + { + ScenePresences.TryGetValue(agentID, out sp); + } + + return sp; + } + + /// + /// Get a scene object group that contains the prim with the given local id + /// + /// + /// null if no scene object group containing that prim is found + private SceneObjectGroup GetGroupByPrim(uint localID) + { + //m_log.DebugFormat("Entered GetGroupByPrim with localID {0}", localID); + List EntityList = GetEntities(); + foreach (EntityBase ent in EntityList) + { + //m_log.DebugFormat("Looking at entity {0}", ent.UUID); + if (ent is SceneObjectGroup) + { + if (((SceneObjectGroup)ent).HasChildPrim(localID)) + return (SceneObjectGroup)ent; + } + } + return null; + } + + /// + /// Get a scene object group that contains the prim with the given uuid + /// + /// + /// null if no scene object group containing that prim is found + private SceneObjectGroup GetGroupByPrim(UUID fullID) + { + List EntityList = GetEntities(); + + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + if (((SceneObjectGroup)ent).HasChildPrim(fullID)) + return (SceneObjectGroup)ent; + } + } + return null; + } + + protected internal EntityIntersection GetClosestIntersectingPrim(Ray hray, bool frontFacesOnly, bool faceCenters) + { + // Primitive Ray Tracing + float closestDistance = 280f; + EntityIntersection returnResult = new EntityIntersection(); + List EntityList = GetEntities(); + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + SceneObjectGroup reportingG = (SceneObjectGroup)ent; + EntityIntersection result = reportingG.TestIntersection(hray, frontFacesOnly, faceCenters); + if (result.HitTF) + { + if (result.distance < closestDistance) + { + closestDistance = result.distance; + returnResult = result; + } + } + } + } + return returnResult; + } + + /// + /// Get a part contained in this scene. + /// + /// + /// null if the part was not found + protected internal SceneObjectPart GetSceneObjectPart(uint localID) + { + SceneObjectGroup group = GetGroupByPrim(localID); + + if (group != null) + return group.GetChildPart(localID); + else + return null; + } + + /// + /// Get a named prim contained in this scene (will return the first + /// found, if there are more than one prim with the same name) + /// + /// + /// null if the part was not found + protected internal SceneObjectPart GetSceneObjectPart(string name) + { + List EntityList = GetEntities(); + + // FIXME: use a dictionary here + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + foreach (SceneObjectPart p in ((SceneObjectGroup) ent).GetParts()) + { + if (p.Name==name) + { + return p; + } + } + } + } + return null; + } + + /// + /// Get a part contained in this scene. + /// + /// + /// null if the part was not found + protected internal SceneObjectPart GetSceneObjectPart(UUID fullID) + { + SceneObjectGroup group = GetGroupByPrim(fullID); + + if (group != null) + return group.GetChildPart(fullID); + else + return null; + } + + protected internal bool TryGetAvatar(UUID avatarId, out ScenePresence avatar) + { + ScenePresence presence; + + lock (ScenePresences) + { + if (ScenePresences.TryGetValue(avatarId, out presence)) + { + avatar = presence; + return true; + + //if (!presence.IsChildAgent) + //{ + // avatar = presence; + // return true; + //} + //else + //{ + // m_log.WarnFormat( + // "[INNER SCENE]: Requested avatar {0} could not be found in scene {1} since it is only registered as a child agent!", + // avatarId, m_parentScene.RegionInfo.RegionName); + //} + } + } + + avatar = null; + return false; + } + + protected internal bool TryGetAvatarByName(string avatarName, out ScenePresence avatar) + { + lock (ScenePresences) + { + foreach (ScenePresence presence in ScenePresences.Values) + { + if (!presence.IsChildAgent) + { + string name = presence.ControllingClient.Name; + + if (String.Compare(avatarName, name, true) == 0) + { + avatar = presence; + return true; + } + } + } + } + + avatar = null; + return false; + } + + /// + /// Returns a list of the entities in the scene. This is a new list so no locking is required to iterate over + /// it + /// + /// + protected internal List GetEntities() + { + return Entities.GetEntities(); + } + + public Dictionary GetTopScripts() + { + Dictionary topScripts = new Dictionary(); + + List EntityList = GetEntities(); + int limit = 0; + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + SceneObjectGroup grp = (SceneObjectGroup)ent; + if ((grp.RootPart.GetEffectiveObjectFlags() & (uint)PrimFlags.Scripted) != 0) + { + if (grp.scriptScore >= 0.01) + { + topScripts.Add(grp.LocalId, grp.scriptScore); + limit++; + if (limit >= 100) + { + break; + } + } + grp.scriptScore = 0; + } + } + } + + return topScripts; + } + + #endregion + + #region Other Methods + + protected internal void physicsBasedCrash() + { + handlerPhysicsCrash = UnRecoverableError; + if (handlerPhysicsCrash != null) + { + handlerPhysicsCrash(); + } + } + + protected internal UUID ConvertLocalIDToFullID(uint localID) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + return group.GetPartsFullID(localID); + else + return UUID.Zero; + } + + protected internal void ForEachClient(Action action) + { + List splist = GetScenePresences(); + foreach (ScenePresence presence in splist) + { + try + { + action(presence.ControllingClient); + } + catch (Exception e) + { + // Catch it and move on. This includes situations where splist has inconsistent info + m_log.WarnFormat("[SCENE]: Problem processing action in ForEachClient: ", e.Message); + } + } + } + + #endregion + + #region Client Event handlers + + /// + /// + /// + /// + /// + /// + protected internal void UpdatePrimScale(uint localID, Vector3 scale, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.Permissions.CanEditObject(group.UUID, remoteClient.AgentId)) + { + group.Resize(scale, localID); + } + } + } + + protected internal void UpdatePrimGroupScale(uint localID, Vector3 scale, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.Permissions.CanEditObject(group.UUID, remoteClient.AgentId)) + { + group.GroupResize(scale, localID); + } + } + } + + /// + /// This handles the nifty little tool tip that you get when you drag your mouse over an object + /// Send to the Object Group to process. We don't know enough to service the request + /// + /// + /// + /// + /// + protected internal void RequestObjectPropertiesFamily( + IClientAPI remoteClient, UUID AgentID, uint RequestFlags, UUID ObjectID) + { + SceneObjectGroup group = GetGroupByPrim(ObjectID); + if (group != null) + { + group.ServiceObjectPropertiesFamilyRequest(remoteClient, AgentID, RequestFlags); + } + } + + /// + /// + /// + /// + /// + /// + protected internal void UpdatePrimSingleRotation(uint localID, Quaternion rot, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.Permissions.CanMoveObject(group.UUID, remoteClient.AgentId)) + { + group.UpdateSingleRotation(rot, localID); + } + } + } + + /// + /// + /// + /// + /// + /// + protected internal void UpdatePrimRotation(uint localID, Quaternion rot, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.Permissions.CanMoveObject(group.UUID, remoteClient.AgentId)) + { + group.UpdateGroupRotation(rot); + } + } + } + + /// + /// + /// + /// + /// + /// + /// + protected internal void UpdatePrimRotation(uint localID, Vector3 pos, Quaternion rot, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.Permissions.CanMoveObject(group.UUID, remoteClient.AgentId)) + { + group.UpdateGroupRotation(pos, rot); + } + } + } + + /// + /// Update the position of the given part + /// + /// + /// + /// + protected internal void UpdatePrimSinglePosition(uint localID, Vector3 pos, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.Permissions.CanMoveObject(group.UUID, remoteClient.AgentId) || group.IsAttachment) + { + group.UpdateSinglePosition(pos, localID); + } + } + } + + /// + /// Update the position of the given part + /// + /// + /// + /// + protected internal void UpdatePrimPosition(uint localID, Vector3 pos, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + + // Vector3 oldPos = group.AbsolutePosition; + if (group.IsAttachment || (group.RootPart.Shape.PCode == 9 && group.RootPart.Shape.State != 0)) + { + group.UpdateGroupPosition(pos); + } + else + { + if (m_parentScene.Permissions.CanMoveObject(group.UUID, remoteClient.AgentId) && m_parentScene.Permissions.CanObjectEntry(group.UUID, false, pos)) + { + group.UpdateGroupPosition(pos); + } + } + } + } + + /// + /// + /// + /// + /// + /// + protected internal void UpdatePrimTexture(uint localID, byte[] texture, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.Permissions.CanEditObject(group.UUID,remoteClient.AgentId)) + { + group.UpdateTextureEntry(localID, texture); + } + } + } + + /// + /// + /// + /// + /// + /// + /// This routine seems to get called when a user changes object settings in the viewer. + /// If some one can confirm that, please change the comment according. + protected internal void UpdatePrimFlags(uint localID, bool UsePhysics, bool IsTemporary, bool IsPhantom, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.Permissions.CanEditObject(group.UUID, remoteClient.AgentId)) + { + group.UpdatePrimFlags(localID, UsePhysics, IsTemporary, IsPhantom, false); // VolumeDetect can't be set via UI and will always be off when a change is made there + } + } + } + + /// + /// Move the given object + /// + /// + /// + /// + /// + protected internal void MoveObject(UUID objectID, Vector3 offset, Vector3 pos, IClientAPI remoteClient, List surfaceArgs) + { + SceneObjectGroup group = GetGroupByPrim(objectID); + if (group != null) + { + if (m_parentScene.Permissions.CanMoveObject(group.UUID, remoteClient.AgentId))// && PermissionsMngr.) + { + group.GrabMovement(offset, pos, remoteClient); + } + // This is outside the above permissions condition + // so that if the object is locked the client moving the object + // get's it's position on the simulator even if it was the same as before + // This keeps the moving user's client in sync with the rest of the world. + group.SendGroupTerseUpdate(); + } + } + + /// + /// + /// + /// + /// + protected internal void PrimName(IClientAPI remoteClient, uint primLocalID, string name) + { + SceneObjectGroup group = GetGroupByPrim(primLocalID); + if (group != null) + { + if (m_parentScene.Permissions.CanEditObject(group.UUID, remoteClient.AgentId)) + { + group.SetPartName(Util.CleanString(name), primLocalID); + group.HasGroupChanged = true; + } + } + } + + /// + /// + /// + /// + /// + protected internal void PrimDescription(IClientAPI remoteClient, uint primLocalID, string description) + { + SceneObjectGroup group = GetGroupByPrim(primLocalID); + if (group != null) + { + if (m_parentScene.Permissions.CanEditObject(group.UUID, remoteClient.AgentId)) + { + group.SetPartDescription(Util.CleanString(description), primLocalID); + group.HasGroupChanged = true; + } + } + } + + protected internal void PrimClickAction(IClientAPI remoteClient, uint primLocalID, string clickAction) + { + SceneObjectGroup group = GetGroupByPrim(primLocalID); + if (group != null) + { + if (m_parentScene.Permissions.CanEditObject(group.UUID, remoteClient.AgentId)) + { + SceneObjectPart part = m_parentScene.GetSceneObjectPart(primLocalID); + part.ClickAction = Convert.ToByte(clickAction); + group.HasGroupChanged = true; + } + } + } + + protected internal void PrimMaterial(IClientAPI remoteClient, uint primLocalID, string material) + { + SceneObjectGroup group = GetGroupByPrim(primLocalID); + if (group != null) + { + if (m_parentScene.Permissions.CanEditObject(group.UUID, remoteClient.AgentId)) + { + SceneObjectPart part = m_parentScene.GetSceneObjectPart(primLocalID); + part.Material = Convert.ToByte(material); + group.HasGroupChanged = true; + } + } + } + + protected internal void UpdateExtraParam(UUID agentID, uint primLocalID, ushort type, bool inUse, byte[] data) + { + SceneObjectGroup group = GetGroupByPrim(primLocalID); + + if (group != null) + { + if (m_parentScene.Permissions.CanEditObject(group.UUID,agentID)) + { + group.UpdateExtraParam(primLocalID, type, inUse, data); + } + } + } + + /// + /// + /// + /// + /// + protected internal void UpdatePrimShape(UUID agentID, uint primLocalID, UpdateShapeArgs shapeBlock) + { + SceneObjectGroup group = GetGroupByPrim(primLocalID); + if (group != null) + { + if (m_parentScene.Permissions.CanEditObject(group.GetPartsFullID(primLocalID), agentID)) + { + ObjectShapePacket.ObjectDataBlock shapeData = new ObjectShapePacket.ObjectDataBlock(); + shapeData.ObjectLocalID = shapeBlock.ObjectLocalID; + shapeData.PathBegin = shapeBlock.PathBegin; + shapeData.PathCurve = shapeBlock.PathCurve; + shapeData.PathEnd = shapeBlock.PathEnd; + shapeData.PathRadiusOffset = shapeBlock.PathRadiusOffset; + shapeData.PathRevolutions = shapeBlock.PathRevolutions; + shapeData.PathScaleX = shapeBlock.PathScaleX; + shapeData.PathScaleY = shapeBlock.PathScaleY; + shapeData.PathShearX = shapeBlock.PathShearX; + shapeData.PathShearY = shapeBlock.PathShearY; + shapeData.PathSkew = shapeBlock.PathSkew; + shapeData.PathTaperX = shapeBlock.PathTaperX; + shapeData.PathTaperY = shapeBlock.PathTaperY; + shapeData.PathTwist = shapeBlock.PathTwist; + shapeData.PathTwistBegin = shapeBlock.PathTwistBegin; + shapeData.ProfileBegin = shapeBlock.ProfileBegin; + shapeData.ProfileCurve = shapeBlock.ProfileCurve; + shapeData.ProfileEnd = shapeBlock.ProfileEnd; + shapeData.ProfileHollow = shapeBlock.ProfileHollow; + + group.UpdateShape(shapeData, primLocalID); + } + } + } + + /// + /// Initial method invoked when we receive a link objects request from the client. + /// + /// + /// + /// + protected internal void LinkObjects(IClientAPI client, uint parentPrim, List childPrims) + { + List EntityList = GetEntities(); + + SceneObjectGroup parenPrim = null; + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + if (((SceneObjectGroup)ent).LocalId == parentPrim) + { + parenPrim = (SceneObjectGroup)ent; + break; + } + } + } + + List children = new List(); + if (parenPrim != null) + { + // We do this in reverse to get the link order of the prims correct + for (int i = childPrims.Count - 1; i >= 0; i--) + { + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + if (((SceneObjectGroup)ent).LocalId == childPrims[i]) + { + // Make sure no child prim is set for sale + // So that, on delink, no prims are unwittingly + // left for sale and sold off + ((SceneObjectGroup)ent).RootPart.ObjectSaleType = 0; + ((SceneObjectGroup)ent).RootPart.SalePrice = 10; + children.Add((SceneObjectGroup)ent); + } + } + } + } + } + else + { + return; // parent is null so not in this region + } + + foreach (SceneObjectGroup sceneObj in children) + { + parenPrim.LinkToGroup(sceneObj); + + // this is here so physics gets updated! + // Don't remove! Bad juju! Stay away! or fix physics! + sceneObj.AbsolutePosition = sceneObj.AbsolutePosition; + } + + // We need to explicitly resend the newly link prim's object properties since no other actions + // occur on link to invoke this elsewhere (such as object selection) + parenPrim.RootPart.AddFlag(PrimFlags.CreateSelected); + parenPrim.TriggerScriptChangedEvent(Changed.LINK); + + if (client != null) + { + parenPrim.GetProperties(client); + } + else + { + foreach (ScenePresence p in GetScenePresences()) + { + parenPrim.GetProperties(p.ControllingClient); + } + } + } + + /// + /// Delink a linkset + /// + /// + protected internal void DelinkObjects(List primIds) + { + DelinkObjects(primIds, true); + } + + protected internal void DelinkObjects(List primIds, bool sendEvents) + { + SceneObjectGroup parenPrim = null; + + // Need a list of the SceneObjectGroup local ids + // XXX I'm anticipating that building this dictionary once is more efficient than + // repeated scanning of the Entity.Values for a large number of primIds. However, it might + // be more efficient yet to keep this dictionary permanently on hand. + + Dictionary sceneObjects = new Dictionary(); + + List EntityList = GetEntities(); + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + SceneObjectGroup obj = (SceneObjectGroup)ent; + // Nasty one. Can't unlink anything in the sim + // If a duplicate local ID sneaks in + // So, check it here! + // + if (!sceneObjects.ContainsKey(obj.LocalId)) + sceneObjects.Add(obj.LocalId, obj); + + } + } + + // Find the root prim among the prim ids we've been given + for (int i = 0; i < primIds.Count; i++) + { + + if (sceneObjects.ContainsKey(primIds[i])) + { + parenPrim = sceneObjects[primIds[i]]; + primIds.RemoveAt(i); + break; + } + } + + if (parenPrim != null) + { + foreach (uint childPrimId in primIds) + { + parenPrim.DelinkFromGroup(childPrimId, sendEvents); + } + + if (parenPrim.Children.Count == 1) + { + // The link set has been completely torn down + // This is the case if you select a link set and delink + // + parenPrim.RootPart.LinkNum = 0; + if (sendEvents) + parenPrim.TriggerScriptChangedEvent(Changed.LINK); + } + else + { + // The link set has prims remaining. This path is taken + // when a subset of a link set's prims are selected + // and the root prim is part of that selection + // + List parts = new List(parenPrim.Children.Values); + + List unlink_ids = new List(); + foreach (SceneObjectPart unlink_part in parts) + unlink_ids.Add(unlink_part.LocalId); + + // Tear down the remaining link set + // + if (unlink_ids.Count == 2) + { + DelinkObjects(unlink_ids, true); + return; + } + + DelinkObjects(unlink_ids, false); + + // Send event to root prim, then we're done with it + parenPrim.TriggerScriptChangedEvent(Changed.LINK); + + unlink_ids.Remove(parenPrim.RootPart.LocalId); + + foreach (uint localId in unlink_ids) + { + SceneObjectPart nr = GetSceneObjectPart(localId); + nr.UpdateFlag = 0; + } + + uint newRoot = unlink_ids[0]; + unlink_ids.Remove(newRoot); + + LinkObjects(null, newRoot, unlink_ids); + } + } + else + { + // The selected prims were all child prims. Edit linked parts + // without the root prim selected will get us here + // + List parents = new List(); + + // If the first scan failed, we need to do a /deep/ scan of the linkages. This is /really/ slow + // We know that this is not the root prim now essentially, so we don't have to worry about remapping + // which one is the root prim + bool delinkedSomething = false; + for (int i = 0; i < primIds.Count; i++) + { + foreach (SceneObjectGroup grp in sceneObjects.Values) + { + SceneObjectPart gPart = grp.GetChildPart(primIds[i]); + if (gPart != null) + { + grp.DelinkFromGroup(primIds[i]); + delinkedSomething = true; + if (!parents.Contains(grp)) + parents.Add(grp); + } + + } + } + if (!delinkedSomething) + { + m_log.InfoFormat("[SCENE]: " + + "DelinkObjects(): Could not find a root prim out of {0} as given to a delink request!", + primIds); + } + else + { + foreach (SceneObjectGroup g in parents) + { + g.TriggerScriptChangedEvent(Changed.LINK); + } + } + } + } + + protected internal void MakeObjectSearchable(IClientAPI remoteClient, bool IncludeInSearch, uint localID) + { + UUID user = remoteClient.AgentId; + UUID objid = UUID.Zero; + SceneObjectPart obj = null; + + List EntityList = GetEntities(); + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + foreach (KeyValuePair subent in ((SceneObjectGroup)ent).Children) + { + if (subent.Value.LocalId == localID) + { + objid = subent.Key; + obj = subent.Value; + } + } + } + } + + //Protip: In my day, we didn't call them searchable objects, we called them limited point-to-point joints + //aka ObjectFlags.JointWheel = IncludeInSearch + + //Permissions model: Object can be REMOVED from search IFF: + // * User owns object + //use CanEditObject + + //Object can be ADDED to search IFF: + // * User owns object + // * Asset/DRM permission bit "modify" is enabled + //use CanEditObjectPosition + + // libomv will complain about PrimFlags.JointWheel being + // deprecated, so we + #pragma warning disable 0612 + if (IncludeInSearch && m_parentScene.Permissions.CanEditObject(objid, user)) + { + obj.ParentGroup.RootPart.AddFlag(PrimFlags.JointWheel); + obj.ParentGroup.HasGroupChanged = true; + } + else if (!IncludeInSearch && m_parentScene.Permissions.CanMoveObject(objid,user)) + { + obj.ParentGroup.RootPart.RemFlag(PrimFlags.JointWheel); + obj.ParentGroup.HasGroupChanged = true; + } + #pragma warning restore 0612 + } + + /// + /// Duplicate the given object, Fire and Forget, No rotation, no return wrapper + /// + /// + /// + /// + protected internal void DuplicateObject(uint originalPrim, Vector3 offset, uint flags, UUID AgentID, UUID GroupID) + { + //m_log.DebugFormat("[SCENE]: Duplication of object {0} at offset {1} requested by agent {2}", originalPrim, offset, AgentID); + + // SceneObjectGroup dupe = DuplicateObject(originalPrim, offset, flags, AgentID, GroupID, Quaternion.Zero); + DuplicateObject(originalPrim, offset, flags, AgentID, GroupID, Quaternion.Identity); + } + + /// + /// Duplicate the given object. + /// + /// + /// + /// + protected internal SceneObjectGroup DuplicateObject(uint originalPrim, Vector3 offset, uint flags, UUID AgentID, UUID GroupID, Quaternion rot) + { + //m_log.DebugFormat("[SCENE]: Duplication of object {0} at offset {1} requested by agent {2}", originalPrim, offset, AgentID); + + List EntityList = GetEntities(); + + SceneObjectGroup originPrim = null; + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + if (((SceneObjectGroup)ent).LocalId == originalPrim) + { + originPrim = (SceneObjectGroup)ent; + break; + } + } + } + + if (originPrim != null) + { + if (m_parentScene.Permissions.CanDuplicateObject(originPrim.Children.Count, originPrim.UUID, AgentID, originPrim.AbsolutePosition)) + { + SceneObjectGroup copy = originPrim.Copy(AgentID, GroupID, true); + copy.AbsolutePosition = copy.AbsolutePosition + offset; + + Entities.Add(copy); + + // Since we copy from a source group that is in selected + // state, but the copy is shown deselected in the viewer, + // We need to clear the selection flag here, else that + // prim never gets persisted at all. The client doesn't + // think it's selected, so it will never send a deselect... + copy.IsSelected = false; + + m_numPrim += copy.Children.Count; + + if (rot != Quaternion.Identity) + { + copy.UpdateGroupRotation(rot); + } + + copy.CreateScriptInstances(0, false, m_parentScene.DefaultScriptEngine, 0); + copy.HasGroupChanged = true; + copy.ScheduleGroupForFullUpdate(); + + // required for physics to update it's position + copy.AbsolutePosition = copy.AbsolutePosition; + + if (OnObjectDuplicate != null) + OnObjectDuplicate(originPrim, copy); + + return copy; + } + } + else + { + m_log.WarnFormat("[SCENE]: Attempted to duplicate nonexistant prim id {0}", GroupID); + } + + return null; + } + + /// + /// Calculates the distance between two Vector3s + /// + /// + /// + /// + protected internal float Vector3Distance(Vector3 v1, Vector3 v2) + { + // We don't really need the double floating point precision... + // so casting it to a single + + return + (float) + Math.Sqrt((v1.X - v2.X) * (v1.X - v2.X) + (v1.Y - v2.Y) * (v1.Y - v2.Y) + (v1.Z - v2.Z) * (v1.Z - v2.Z)); + } + + #endregion + + + } +} diff --git a/OpenSim/Region/Framework/Scenes/SceneManager.cs b/OpenSim/Region/Framework/Scenes/SceneManager.cs new file mode 100644 index 0000000..180f8a1 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/SceneManager.cs @@ -0,0 +1,669 @@ +/* + * 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 OpenSim 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.Net; +using System.Reflection; +using OpenMetaverse; +using log4net; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.Framework.Scenes +{ + public delegate void RestartSim(RegionInfo thisregion); + + /// + /// Manager for adding, closing and restarting scenes. + /// + public class SceneManager + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public event RestartSim OnRestartSim; + + private readonly List m_localScenes; + private Scene m_currentScene = null; + + public List Scenes + { + get { return m_localScenes; } + } + + public Scene CurrentScene + { + get { return m_currentScene; } + } + + public Scene CurrentOrFirstScene + { + get + { + if (m_currentScene == null) + { + return m_localScenes[0]; + } + else + { + return m_currentScene; + } + } + } + + public SceneManager() + { + m_localScenes = new List(); + } + + public void Close() + { + // collect known shared modules in sharedModules + Dictionary sharedModules = new Dictionary(); + for (int i = 0; i < m_localScenes.Count; i++) + { + // extract known shared modules from scene + foreach (string k in m_localScenes[i].Modules.Keys) + { + if (m_localScenes[i].Modules[k].IsSharedModule && + !sharedModules.ContainsKey(k)) + sharedModules[k] = m_localScenes[i].Modules[k]; + } + // close scene/region + m_localScenes[i].Close(); + } + + // all regions/scenes are now closed, we can now safely + // close all shared modules + foreach (IRegionModule mod in sharedModules.Values) + { + mod.Close(); + } + } + + public void Close(Scene cscene) + { + if (m_localScenes.Contains(cscene)) + { + for (int i = 0; i < m_localScenes.Count; i++) + { + if (m_localScenes[i].Equals(cscene)) + { + m_localScenes[i].Close(); + } + } + } + } + + public void Add(Scene scene) + { + scene.OnRestart += HandleRestart; + m_localScenes.Add(scene); + } + + public void HandleRestart(RegionInfo rdata) + { + m_log.Error("[SCENEMANAGER]: Got Restart message for region:" + rdata.RegionName + " Sending up to main"); + int RegionSceneElement = -1; + for (int i = 0; i < m_localScenes.Count; i++) + { + if (rdata.RegionName == m_localScenes[i].RegionInfo.RegionName) + { + RegionSceneElement = i; + } + } + + // Now we make sure the region is no longer known about by the SceneManager + // Prevents duplicates. + + if (RegionSceneElement >= 0) + { + m_localScenes.RemoveAt(RegionSceneElement); + } + + // Send signal to main that we're restarting this sim. + OnRestartSim(rdata); + } + + public void SendSimOnlineNotification(ulong regionHandle) + { + RegionInfo Result = null; + + for (int i = 0; i < m_localScenes.Count; i++) + { + if (m_localScenes[i].RegionInfo.RegionHandle == regionHandle) + { + // Inform other regions to tell their avatar about me + Result = m_localScenes[i].RegionInfo; + } + } + if (Result != null) + { + for (int i = 0; i < m_localScenes.Count; i++) + { + if (m_localScenes[i].RegionInfo.RegionHandle != regionHandle) + { + // Inform other regions to tell their avatar about me + //m_localScenes[i].OtherRegionUp(Result); + } + } + } + else + { + m_log.Error("[REGION]: Unable to notify Other regions of this Region coming up"); + } + } + + /// + /// Save the prims in the current scene to an xml file in OpenSimulator's original 'xml' format + /// + /// + public void SaveCurrentSceneToXml(string filename) + { + IRegionSerialiserModule serialiser = CurrentOrFirstScene.RequestModuleInterface(); + if (serialiser != null) + serialiser.SavePrimsToXml(CurrentOrFirstScene, filename); + } + + /// + /// Load an xml file of prims in OpenSimulator's original 'xml' file format to the current scene + /// + /// + /// + /// + public void LoadCurrentSceneFromXml(string filename, bool generateNewIDs, Vector3 loadOffset) + { + IRegionSerialiserModule serialiser = CurrentOrFirstScene.RequestModuleInterface(); + if (serialiser != null) + serialiser.LoadPrimsFromXml(CurrentOrFirstScene, filename, generateNewIDs, loadOffset); + } + + /// + /// Save the prims in the current scene to an xml file in OpenSimulator's current 'xml2' format + /// + /// + public void SaveCurrentSceneToXml2(string filename) + { + IRegionSerialiserModule serialiser = CurrentOrFirstScene.RequestModuleInterface(); + if (serialiser != null) + serialiser.SavePrimsToXml2(CurrentOrFirstScene, filename); + } + + public void SaveNamedPrimsToXml2(string primName, string filename) + { + IRegionSerialiserModule serialiser = CurrentOrFirstScene.RequestModuleInterface(); + if (serialiser != null) + serialiser.SaveNamedPrimsToXml2(CurrentOrFirstScene, primName, filename); + } + + /// + /// Load an xml file of prims in OpenSimulator's current 'xml2' file format to the current scene + /// + public void LoadCurrentSceneFromXml2(string filename) + { + IRegionSerialiserModule serialiser = CurrentOrFirstScene.RequestModuleInterface(); + if (serialiser != null) + serialiser.LoadPrimsFromXml2(CurrentOrFirstScene, filename); + } + + /// + /// Save the current scene to an OpenSimulator archive. This archive will eventually include the prim's assets + /// as well as the details of the prims themselves. + /// + /// + public void SaveCurrentSceneToArchive(string filename) + { + IRegionArchiverModule archiver = CurrentOrFirstScene.RequestModuleInterface(); + if (archiver != null) + archiver.ArchiveRegion(filename); + } + + /// + /// Load an OpenSim archive into the current scene. This will load both the shapes of the prims and upload + /// their assets to the asset service. + /// + /// + public void LoadArchiveToCurrentScene(string filename) + { + IRegionArchiverModule archiver = CurrentOrFirstScene.RequestModuleInterface(); + if (archiver != null) + archiver.DearchiveRegion(filename); + } + + public string SaveCurrentSceneMapToXmlString() + { + return CurrentOrFirstScene.Heightmap.SaveToXmlString(); + } + + public void LoadCurrenSceneMapFromXmlString(string mapData) + { + CurrentOrFirstScene.Heightmap.LoadFromXmlString(mapData); + } + + public void SendCommandToPluginModules(string[] cmdparams) + { + ForEachCurrentScene(delegate(Scene scene) { scene.SendCommandToPlugins(cmdparams); }); + } + + public void SetBypassPermissionsOnCurrentScene(bool bypassPermissions) + { + ForEachCurrentScene(delegate(Scene scene) { scene.Permissions.SetBypassPermissions(bypassPermissions); }); + } + + private void ForEachCurrentScene(Action func) + { + if (m_currentScene == null) + { + m_localScenes.ForEach(func); + } + else + { + func(m_currentScene); + } + } + + public void RestartCurrentScene() + { + ForEachCurrentScene(delegate(Scene scene) { scene.RestartNow(); }); + } + + public void BackupCurrentScene() + { + ForEachCurrentScene(delegate(Scene scene) { scene.Backup(); }); + } + + public void HandleAlertCommandOnCurrentScene(string[] cmdparams) + { + ForEachCurrentScene(delegate(Scene scene) { scene.HandleAlertCommand(cmdparams); }); + } + + public void SendGeneralMessage(string msg) + { + ForEachCurrentScene(delegate(Scene scene) { scene.HandleAlertCommand(new string[] { "general", msg }); }); + } + + public bool TrySetCurrentScene(string regionName) + { + if ((String.Compare(regionName, "root") == 0) + || (String.Compare(regionName, "..") == 0) + || (String.Compare(regionName, "/") == 0)) + { + m_currentScene = null; + return true; + } + else + { + foreach (Scene scene in m_localScenes) + { + if (String.Compare(scene.RegionInfo.RegionName, regionName, true) == 0) + { + m_currentScene = scene; + return true; + } + } + + return false; + } + } + + public bool TrySetCurrentScene(UUID regionID) + { + Console.WriteLine("Searching for Region: '{0}'", regionID.ToString()); + + foreach (Scene scene in m_localScenes) + { + if (scene.RegionInfo.RegionID == regionID) + { + m_currentScene = scene; + return true; + } + } + + return false; + } + + public bool TryGetScene(string regionName, out Scene scene) + { + foreach (Scene mscene in m_localScenes) + { + if (String.Compare(mscene.RegionInfo.RegionName, regionName, true) == 0) + { + scene = mscene; + return true; + } + } + scene = null; + return false; + } + + public bool TryGetScene(UUID regionID, out Scene scene) + { + foreach (Scene mscene in m_localScenes) + { + if (mscene.RegionInfo.RegionID == regionID) + { + scene = mscene; + return true; + } + } + + scene = null; + return false; + } + + public bool TryGetScene(uint locX, uint locY, out Scene scene) + { + foreach (Scene mscene in m_localScenes) + { + if (mscene.RegionInfo.RegionLocX == locX && + mscene.RegionInfo.RegionLocY == locY) + { + scene = mscene; + return true; + } + } + + scene = null; + return false; + } + + public bool TryGetScene(IPEndPoint ipEndPoint, out Scene scene) + { + foreach (Scene mscene in m_localScenes) + { + if ((mscene.RegionInfo.InternalEndPoint.Equals(ipEndPoint.Address)) && + (mscene.RegionInfo.InternalEndPoint.Port == ipEndPoint.Port)) + { + scene = mscene; + return true; + } + } + + scene = null; + return false; + } + + /// + /// Set the debug packet level on the current scene. This level governs which packets are printed out to the + /// console. + /// + /// + public void SetDebugPacketLevelOnCurrentScene(int newDebug) + { + ForEachCurrentScene(delegate(Scene scene) + { + List scenePresences = scene.GetScenePresences(); + + foreach (ScenePresence scenePresence in scenePresences) + { + if (!scenePresence.IsChildAgent) + { + m_log.ErrorFormat("Packet debug for {0} {1} set to {2}", + scenePresence.Firstname, + scenePresence.Lastname, + newDebug); + + scenePresence.ControllingClient.SetDebugPacketLevel(newDebug); + } + } + }); + } + + public List GetCurrentSceneAvatars() + { + List avatars = new List(); + + ForEachCurrentScene(delegate(Scene scene) + { + List scenePresences = scene.GetScenePresences(); + + foreach (ScenePresence scenePresence in scenePresences) + { + if (!scenePresence.IsChildAgent) + { + avatars.Add(scenePresence); + } + } + }); + + return avatars; + } + + public List GetCurrentScenePresences() + { + List presences = new List(); + + ForEachCurrentScene(delegate(Scene scene) + { + List scenePresences = scene.GetScenePresences(); + presences.AddRange(scenePresences); + }); + + return presences; + } + + public RegionInfo GetRegionInfo(ulong regionHandle) + { + foreach (Scene scene in m_localScenes) + { + if (scene.RegionInfo.RegionHandle == regionHandle) + { + return scene.RegionInfo; + } + } + + return null; + } + + public void ForceCurrentSceneClientUpdate() + { + ForEachCurrentScene(delegate(Scene scene) { scene.ForceClientUpdate(); }); + } + + public void HandleEditCommandOnCurrentScene(string[] cmdparams) + { + ForEachCurrentScene(delegate(Scene scene) { scene.HandleEditCommand(cmdparams); }); + } + + public bool TryGetAvatar(UUID avatarId, out ScenePresence avatar) + { + foreach (Scene scene in m_localScenes) + { + if (scene.TryGetAvatar(avatarId, out avatar)) + { + return true; + } + } + + avatar = null; + return false; + } + + public bool TryGetAvatarsScene(UUID avatarId, out Scene scene) + { + ScenePresence avatar = null; + foreach (Scene mScene in m_localScenes) + { + if (mScene.TryGetAvatar(avatarId, out avatar)) + { + scene = mScene; + return true; + } + } + + scene = null; + return false; + } + + public void CloseScene(Scene scene) + { + m_localScenes.Remove(scene); + scene.Close(); + } + + public bool TryGetAvatarByName(string avatarName, out ScenePresence avatar) + { + foreach (Scene scene in m_localScenes) + { + if (scene.TryGetAvatarByName(avatarName, out avatar)) + { + return true; + } + } + + avatar = null; + return false; + } + + public void ForEachScene(Action action) + { + m_localScenes.ForEach(action); + } + + public void CacheJ2kDecode(int threads) + { + if (threads < 1) threads = 1; + + IJ2KDecoder m_decoder = m_localScenes[0].RequestModuleInterface(); + + List assetRequestList = new List(); + + #region AssetGathering! + foreach (Scene scene in m_localScenes) + { + List entitles = scene.GetEntities(); + foreach (EntityBase entity in entitles) + { + if (entity is SceneObjectGroup) + { + SceneObjectGroup sog = (SceneObjectGroup) entity; + foreach (SceneObjectPart part in sog.Children.Values) + { + if (part.Shape != null) + { + if (part.Shape.TextureEntry.Length > 0) + { + OpenMetaverse.Primitive.TextureEntry te = + new Primitive.TextureEntry(part.Shape.TextureEntry, 0, + part.Shape.TextureEntry.Length); + if (te.DefaultTexture != null) // this has been null for some reason... + { + if (te.DefaultTexture.TextureID != UUID.Zero) + assetRequestList.Add(te.DefaultTexture.TextureID); + } + for (int i=0; i arrvalus = new List(); + + //split into separate arrays + for (int j = 0; j < threads; j++) + { + List val = new List(); + + for (int k = j * entries_per_thread; k < ((j + 1) * entries_per_thread); k++) + { + if (k < arrAssetRequestList.Length) + { + val.Add(arrAssetRequestList[k]); + } + + } + arrvalus.Add(val.ToArray()); + } + + for (int l = 0; l < arrvalus.Count; l++) + { + DecodeThreadContents threadworkItem = new DecodeThreadContents(); + threadworkItem.sn = m_localScenes[0]; + threadworkItem.j2kdecode = m_decoder; + threadworkItem.arrassets = arrvalus[l]; + + System.Threading.Thread decodethread = + new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(threadworkItem.run)); + + threadworkItem.SetThread(decodethread); + + decodethread.Priority = System.Threading.ThreadPriority.Lowest; + decodethread.Name = "J2kCacheDecodeThread_" + l + 1; + ThreadTracker.Add(decodethread); + decodethread.Start(); + + } + } + } + + public class DecodeThreadContents + { + public Scene sn; + public UUID[] arrassets; + public IJ2KDecoder j2kdecode; + private System.Threading.Thread thisthread; + + public void run( object o) + { + for (int i=0;i + /// Force all task inventories of prims in the scene object to persist + /// + public void ForceInventoryPersistence() + { + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + part.Inventory.ForceInventoryPersistence(); + } + } + } + + /// + /// Start the scripts contained in all the prims in this group. + /// + public void CreateScriptInstances(int startParam, bool postOnRez, + string engine, int stateSource) + { + // Don't start scripts if they're turned off in the region! + if (!m_scene.RegionInfo.RegionSettings.DisableScripts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + part.Inventory.CreateScriptInstances(startParam, postOnRez, engine, stateSource); + } + } + } + + /// + /// Stop the scripts contained in all the prims in this group + /// + public void RemoveScriptInstances() + { + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + part.Inventory.RemoveScriptInstances(); + } + } + } + + /// + /// + /// + /// + /// + public bool GetPartInventoryFileName(IClientAPI remoteClient, uint localID) + { + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + return part.Inventory.GetInventoryFileName(remoteClient, localID); + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Couldn't find part {0} in object group {1}, {2} to retreive prim inventory", + localID, Name, UUID); + } + return false; + } + + /// + /// Return serialized inventory metadata for the given constituent prim + /// + /// + /// + public void RequestInventoryFile(IClientAPI client, uint localID, IXfer xferManager) + { + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + part.Inventory.RequestInventoryFile(client, xferManager); + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Couldn't find part {0} in object group {1}, {2} to request inventory data", + localID, Name, UUID); + } + } + + /// + /// Add an inventory item to a prim in this group. + /// + /// + /// + /// + /// The item UUID that should be used by the new item. + /// + public bool AddInventoryItem(IClientAPI remoteClient, uint localID, + InventoryItemBase item, UUID copyItemID) + { + UUID newItemId = (copyItemID != UUID.Zero) ? copyItemID : item.ID; + + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + TaskInventoryItem taskItem = new TaskInventoryItem(); + + taskItem.ItemID = newItemId; + taskItem.AssetID = item.AssetID; + taskItem.Name = item.Name; + taskItem.Description = item.Description; + taskItem.OwnerID = part.OwnerID; // Transfer ownership + taskItem.CreatorID = item.Creator; + taskItem.Type = item.AssetType; + taskItem.InvType = item.InvType; + + if (remoteClient != null && + remoteClient.AgentId != part.OwnerID && + m_scene.Permissions.PropagatePermissions()) + { + taskItem.BasePermissions = item.BasePermissions & + item.NextPermissions; + taskItem.CurrentPermissions = item.CurrentPermissions & + item.NextPermissions; + taskItem.EveryonePermissions = item.EveryOnePermissions & + item.NextPermissions; + taskItem.GroupPermissions = item.GroupPermissions & + item.NextPermissions; + taskItem.NextPermissions = item.NextPermissions; + taskItem.CurrentPermissions |= 8; + } else { + taskItem.BasePermissions = item.BasePermissions; + taskItem.CurrentPermissions = item.CurrentPermissions; + taskItem.CurrentPermissions |= 8; + taskItem.EveryonePermissions = item.EveryOnePermissions; + taskItem.GroupPermissions = item.GroupPermissions; + taskItem.NextPermissions = item.NextPermissions; + } + + taskItem.Flags = item.Flags; + // TODO: These are pending addition of those fields to TaskInventoryItem +// taskItem.SalePrice = item.SalePrice; +// taskItem.SaleType = item.SaleType; + taskItem.CreationDate = (uint)item.CreationDate; + + bool addFromAllowedDrop = false; + if (remoteClient!=null) + { + addFromAllowedDrop = remoteClient.AgentId != part.OwnerID; + } + + part.Inventory.AddInventoryItem(taskItem, addFromAllowedDrop); + + return true; + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Couldn't find prim local ID {0} in group {1}, {2} to add inventory item ID {3}", + localID, Name, UUID, newItemId); + } + + return false; + } + + /// + /// Returns an existing inventory item. Returns the original, so any changes will be live. + /// + /// + /// + /// null if the item does not exist + public TaskInventoryItem GetInventoryItem(uint primID, UUID itemID) + { + SceneObjectPart part = GetChildPart(primID); + if (part != null) + { + return part.Inventory.GetInventoryItem(itemID); + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Couldn't find prim local ID {0} in prim {1}, {2} to get inventory item ID {3}", + primID, part.Name, part.UUID, itemID); + } + + return null; + } + + /// + /// Update an existing inventory item. + /// + /// The updated item. An item with the same id must already exist + /// in this prim's inventory + /// false if the item did not exist, true if the update occurred succesfully + public bool UpdateInventoryItem(TaskInventoryItem item) + { + SceneObjectPart part = GetChildPart(item.ParentPartID); + if (part != null) + { + part.Inventory.UpdateInventoryItem(item); + + return true; + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Couldn't find prim ID {0} to update item {1}, {2}", + item.ParentPartID, item.Name, item.ItemID); + } + + return false; + } + + public int RemoveInventoryItem(uint localID, UUID itemID) + { + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + int type = part.Inventory.RemoveInventoryItem(itemID); + + return type; + } + + return -1; + } + + public uint GetEffectivePermissions() + { + uint perms=(uint)(PermissionMask.Modify | + PermissionMask.Copy | + PermissionMask.Move | + PermissionMask.Transfer) | 7; + + uint ownerMask = 0x7ffffff; + foreach (SceneObjectPart part in m_parts.Values) + { + ownerMask &= part.OwnerMask; + perms &= part.Inventory.MaskEffectivePermissions(); + } + + if ((ownerMask & (uint)PermissionMask.Modify) == 0) + perms &= ~(uint)PermissionMask.Modify; + if ((ownerMask & (uint)PermissionMask.Copy) == 0) + perms &= ~(uint)PermissionMask.Copy; + if ((ownerMask & (uint)PermissionMask.Transfer) == 0) + perms &= ~(uint)PermissionMask.Transfer; + + if ((ownerMask & RootPart.NextOwnerMask & (uint)PermissionMask.Modify) == 0) + perms &= ~((uint)PermissionMask.Modify >> 13); + if ((ownerMask & RootPart.NextOwnerMask & (uint)PermissionMask.Copy) == 0) + perms &= ~((uint)PermissionMask.Copy >> 13); + if ((ownerMask & RootPart.NextOwnerMask & (uint)PermissionMask.Transfer) == 0) + perms &= ~((uint)PermissionMask.Transfer >> 13); + + return perms; + } + + public void ApplyNextOwnerPermissions() + { + foreach (SceneObjectPart part in m_parts.Values) + { + part.ApplyNextOwnerPermissions(); + } + } + + public string GetStateSnapshot() + { + List assemblies = new List(); + Dictionary states = new Dictionary(); + + foreach (SceneObjectPart part in m_parts.Values) + { + foreach (string a in part.Inventory.GetScriptAssemblies()) + { + if (a != "" && !assemblies.Contains(a)) + assemblies.Add(a); + } + + foreach (KeyValuePair s in part.Inventory.GetScriptStates()) + { + states[s.Key] = s.Value; + } + } + + if (states.Count < 1 || assemblies.Count < 1) + return ""; + + XmlDocument xmldoc = new XmlDocument(); + + XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + xmldoc.AppendChild(xmlnode); + XmlElement rootElement = xmldoc.CreateElement("", "ScriptData", + ""); + + xmldoc.AppendChild(rootElement); + + XmlElement wrapper = xmldoc.CreateElement("", "Assemblies", + ""); + + rootElement.AppendChild(wrapper); + + foreach (string assembly in assemblies) + { + string fn = Path.GetFileName(assembly); + if (fn == String.Empty) + continue; + + FileInfo fi = new FileInfo(assembly); + + if (fi == null) + continue; + + Byte[] data = new Byte[fi.Length]; + + FileStream fs = File.Open(assembly, FileMode.Open, FileAccess.Read); + fs.Read(data, 0, data.Length); + fs.Close(); + + XmlElement assemblyData = xmldoc.CreateElement("", "Assembly", ""); + XmlAttribute assemblyName = xmldoc.CreateAttribute("", "Filename", ""); + assemblyName.Value = fn; + assemblyData.Attributes.Append(assemblyName); + + assemblyData.InnerText = System.Convert.ToBase64String(data); + + wrapper.AppendChild(assemblyData); + } + + wrapper = xmldoc.CreateElement("", "ScriptStates", + ""); + + rootElement.AppendChild(wrapper); + + foreach (KeyValuePair state in states) + { + XmlElement stateData = xmldoc.CreateElement("", "State", ""); + + XmlAttribute stateID = xmldoc.CreateAttribute("", "UUID", ""); + stateID.Value = state.Key.ToString(); + stateData.Attributes.Append(stateID); + + XmlDocument sdoc = new XmlDocument(); + sdoc.LoadXml(state.Value); + XmlNodeList rootL = sdoc.GetElementsByTagName("ScriptState"); + XmlNode rootNode = rootL[0]; + + XmlNode newNode = xmldoc.ImportNode(rootNode, true); + stateData.AppendChild(newNode); + wrapper.AppendChild(stateData); + } + + return xmldoc.InnerXml; + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs new file mode 100644 index 0000000..57d9ce4 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -0,0 +1,3012 @@ +/* + * 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 OpenSim 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.Drawing; +using System.IO; +using System.Xml; +using System.Xml.Serialization; +using OpenMetaverse; +using OpenMetaverse.Packets; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Physics.Manager; + +namespace OpenSim.Region.Framework.Scenes +{ + [Flags] + public enum scriptEvents + { + None = 0, + attach = 1, + collision = 16, + collision_end = 32, + collision_start = 64, + control = 128, + dataserver = 256, + email = 512, + http_response = 1024, + land_collision = 2048, + land_collision_end = 4096, + land_collision_start = 8192, + at_target = 16384, + listen = 32768, + money = 65536, + moving_end = 131072, + moving_start = 262144, + not_at_rot_target = 524288, + not_at_target = 1048576, + remote_data = 8388608, + run_time_permissions = 268435456, + state_entry = 1073741824, + state_exit = 2, + timer = 4, + touch = 8, + touch_end = 536870912, + touch_start = 2097152, + object_rez = 4194304 + } + + struct scriptPosTarget + { + public Vector3 targetPos; + public float tolerance; + } + + public delegate void PrimCountTaintedDelegate(); + + /// + /// A scene object group is conceptually an object in the scene. The object is constituted of SceneObjectParts + /// (often known as prims), one of which is considered the root part. + /// + public partial class SceneObjectGroup : EntityBase + { + // private PrimCountTaintedDelegate handlerPrimCountTainted = null; + + /// + /// Signal whether the non-inventory attributes of any prims in the group have changed + /// since the group's last persistent backup + /// + private bool m_hasGroupChanged = false; + private long timeFirstChanged; + private long timeLastChanged; + + public bool HasGroupChanged + { + set + { + if (value) + { + timeLastChanged = DateTime.Now.Ticks; + if (!m_hasGroupChanged) + timeFirstChanged = DateTime.Now.Ticks; + } + m_hasGroupChanged = value; + } + + get { return m_hasGroupChanged; } + } + + private bool isTimeToPersist() + { + if (IsSelected || IsDeleted || IsAttachment) + return false; + if (!m_hasGroupChanged) + return false; + if (m_scene.ShuttingDown) + return true; + long currentTime = DateTime.Now.Ticks; + if (currentTime - timeLastChanged > m_scene.m_dontPersistBefore || currentTime - timeFirstChanged > m_scene.m_persistAfter) + return true; + return false; + } + + /// + /// Is this scene object acting as an attachment? + /// + /// We return false if the group has already been deleted. + /// + /// TODO: At the moment set must be done on the part itself. There may be a case for doing it here since I + /// presume either all or no parts in a linkset can be part of an attachment (in which + /// case the value would get proprogated down into all the descendent parts). + /// + public bool IsAttachment + { + get + { + if (!IsDeleted) + return m_rootPart.IsAttachment; + + return false; + } + } + + public float scriptScore = 0f; + + private Vector3 lastPhysGroupPos; + private Quaternion lastPhysGroupRot; + + private bool m_isBackedUp = false; + + /// + /// The constituent parts of this group + /// + protected Dictionary m_parts = new Dictionary(); + + protected ulong m_regionHandle; + protected SceneObjectPart m_rootPart; + // private Dictionary m_scriptEvents = new Dictionary(); + + private Dictionary m_targets = new Dictionary(); + + private bool m_scriptListens_atTarget = false; + private bool m_scriptListens_notAtTarget = false; + + #region Properties + + /// + /// The name of an object grouping is always the same as its root part + /// + public override string Name + { + get { + if (RootPart == null) + return ""; + return RootPart.Name; + } + set { RootPart.Name = value; } + } + + /// + /// Added because the Parcel code seems to use it + /// but not sure a object should have this + /// as what does it tell us? that some avatar has selected it (but not what Avatar/user) + /// think really there should be a list (or whatever) in each scenepresence + /// saying what prim(s) that user has selected. + /// + protected bool m_isSelected = false; + + /// + /// Number of prims in this group + /// + public int PrimCount + { + get { return m_parts.Count; } + } + + public Quaternion GroupRotation + { + get { return m_rootPart.RotationOffset; } + } + + public UUID GroupID + { + get { return m_rootPart.GroupID; } + set { m_rootPart.GroupID = value; } + } + + public Dictionary Children + { + get { return m_parts; } + set { m_parts = value; } + } + + /// + /// The root part of this scene object + /// + public SceneObjectPart RootPart + { + get { return m_rootPart; } + } + + public ulong RegionHandle + { + get { return m_regionHandle; } + set + { + m_regionHandle = value; + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + part.RegionHandle = m_regionHandle; + } + } + } + } + + /// + /// The absolute position of this scene object in the scene + /// + public override Vector3 AbsolutePosition + { + get + { + if (m_rootPart == null) + { + throw new NullReferenceException( + string.Format("[SCENE OBJECT GROUP]: Object {0} has no root part.", m_uuid)); + } + + return m_rootPart.GroupPosition; + } + set + { + Vector3 val = value; + + if ((val.X > 257f || val.X < -1f || val.Y > 257f || val.Y < -1f) && !IsAttachment) + { + m_scene.CrossPrimGroupIntoNewRegion(val, this, true); + } + + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + part.GroupPosition = val; + } + } + + //if (m_rootPart.PhysActor != null) + //{ + //m_rootPart.PhysActor.Position = + //new PhysicsVector(m_rootPart.GroupPosition.X, m_rootPart.GroupPosition.Y, + //m_rootPart.GroupPosition.Z); + //m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor); + //} + } + } + + public override uint LocalId + { + get + { + if (m_rootPart == null) + { + m_log.Error("[SCENE OBJECT GROUP]: Unable to find the rootpart for a LocalId Request!"); + return 0; + } + + return m_rootPart.LocalId; + } + set { m_rootPart.LocalId = value; } + } + + public override UUID UUID + { + get { + if (m_rootPart == null) + { + m_log.Error("Got a null rootpart while requesting UUID. Called from: ", new Exception()); + return UUID.Zero; + } + else return m_rootPart.UUID; + } + set { m_rootPart.UUID = value; } + } + + public UUID OwnerID + { + get + { + if (m_rootPart == null) + return UUID.Zero; + + return m_rootPart.OwnerID; + } + set { m_rootPart.OwnerID = value; } + } + + public Color Color + { + get { return m_rootPart.Color; } + set { m_rootPart.Color = value; } + } + + public string Text + { + get { + string returnstr = m_rootPart.Text; + if (returnstr.Length > 255) + { + returnstr = returnstr.Substring(0, 255); + } + return returnstr; + } + set { m_rootPart.Text = value; } + } + + protected virtual bool InSceneBackup + { + get { return true; } + } + + public bool IsSelected + { + get { return m_isSelected; } + set + { + m_isSelected = value; + // Tell physics engine that group is selected + if (m_rootPart != null && m_rootPart.PhysActor != null) + { + m_rootPart.PhysActor.Selected = value; + // Pass it on to the children. + foreach (SceneObjectPart child in Children.Values) + { + if (child.PhysActor != null) + { + child.PhysActor.Selected = value; + } + } + } + } + } + + // The UUID for the Region this Object is in. + public UUID RegionUUID + { + get + { + if (m_scene != null) + { + return m_scene.RegionInfo.RegionID; + } + return UUID.Zero; + } + } + + #endregion + + #region Constructors + + /// + /// Constructor + /// + public SceneObjectGroup() + { + } + + /// + /// This constructor creates a SceneObjectGroup using a pre-existing SceneObjectPart. + /// The original SceneObjectPart will be used rather than a copy, preserving + /// its existing localID and UUID. + /// + public SceneObjectGroup(SceneObjectPart part) + { + SetRootPart(part); + } + + public SceneObjectGroup(string xmlData, bool isOriginalXmlFormat) + : this(UUID.Zero, xmlData, isOriginalXmlFormat) + { + } + + /// + /// Create an object using serialized data in OpenSim's original xml format. + /// + /// + /// If applicable, the user inventory item id from which this object was rezzed. If not applicable then this + /// should be UUID.Zero + /// + /// + /// + /// This parameter only exists to separate the two different xml constructors. In the future, versions should + /// be specified within the xml itself. + /// + public SceneObjectGroup(UUID fromUserInventoryItemID, string xmlData, bool isOriginalXmlFormat) + { + if (!isOriginalXmlFormat) + throw new Exception("This constructor must specify the xml is in OpenSim's original format"); + + //m_log.DebugFormat("[SOG]: Starting deserialization of SOG"); + int time = System.Environment.TickCount; + + // libomv.types changes UUID to Guid + xmlData = xmlData.Replace("", ""); + xmlData = xmlData.Replace("", ""); + + // Handle Nested property + xmlData = xmlData.Replace("", ""); + xmlData = xmlData.Replace("", ""); + StringReader sr = new StringReader(xmlData); + XmlTextReader reader = new XmlTextReader(sr); + + try + { + reader.Read(); + reader.ReadStartElement("SceneObjectGroup"); + reader.ReadStartElement("RootPart"); + SetRootPart(SceneObjectPart.FromXml(fromUserInventoryItemID, reader)); + + reader.ReadEndElement(); + + while (reader.Read()) + { + switch (reader.NodeType) + { + case XmlNodeType.Element: + if (reader.Name == "Part") + { + reader.Read(); + SceneObjectPart part = SceneObjectPart.FromXml(reader); + + // We reset the link number in order to make sure that the persisted linkset order is + int linkNum = part.LinkNum; + AddPart(part); + part.LinkNum = linkNum; + + part.TrimPermissions(); + part.StoreUndoState(); + } + break; + + case XmlNodeType.EndElement: + break; + } + } + } + catch (XmlException e) + { + m_log.ErrorFormat("[SCENE]: Deserialization of xml failed with {0}. xml was {1}", e, xmlData); + } + + reader.Close(); + sr.Close(); + m_log.DebugFormat("[SOG]: Finished deserialization of SOG {0}, {1}ms", Name, System.Environment.TickCount - time); + } + + /// + /// Create an object using serialized data in OpenSim's xml2 format. + /// + public SceneObjectGroup(string xmlData) + { + SetFromXml(xmlData); + } + + protected void SetFromXml(string xmlData) + { + //m_log.DebugFormat("[SOG]: Starting deserialization of SOG"); + //int time = System.Environment.TickCount; + + // libomv.types changes UUID to Guid + xmlData = xmlData.Replace("", ""); + xmlData = xmlData.Replace("", ""); + + // Handle Nested property + xmlData = xmlData.Replace("", ""); + xmlData = xmlData.Replace("", ""); + + StringReader sr = new StringReader(xmlData); + XmlTextReader reader = new XmlTextReader(sr); + reader.Read(); + + reader.ReadStartElement("SceneObjectGroup"); + SetRootPart(CreatePartFromXml(reader)); + + reader.Read(); + bool more = true; + + while (more) + { + switch (reader.NodeType) + { + case XmlNodeType.Element: + if (reader.Name == "SceneObjectPart") + { + SceneObjectPart part = CreatePartFromXml(reader); + AddPart(part); + part.StoreUndoState(); + } + else + { + Console.WriteLine("found unexpected element: " + reader.Name); + reader.Read(); + } + break; + case XmlNodeType.EndElement: + reader.Read(); + break; + } + more = !reader.EOF; + } + + reader.Close(); + sr.Close(); + + //m_log.DebugFormat("[SOG]: Finished deserialization of SOG {0}, {1}ms", Name, System.Environment.TickCount - time); + } + + protected virtual SceneObjectPart CreatePartFromXml(XmlTextReader reader) + { + SceneObjectPart part = SceneObjectPart.FromXml(reader); + return part; + } + + /// + /// Constructor. This object is added to the scene later via AttachToScene() + /// + public SceneObjectGroup(UUID ownerID, Vector3 pos, Quaternion rot, PrimitiveBaseShape shape) + { + Vector3 rootOffset = new Vector3(0, 0, 0); + SetRootPart(new SceneObjectPart(ownerID, shape, pos, rot, rootOffset)); + } + + /// + /// Constructor. + /// + public SceneObjectGroup(UUID ownerID, Vector3 pos, PrimitiveBaseShape shape) + : this(ownerID, pos, Quaternion.Identity, shape) + { + } + + public void SetFromAssetID(UUID AssetId) + { + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + part.FromAssetID = AssetId; + } + } + } + + public UUID GetFromAssetID() + { + if (m_rootPart != null) + { + return m_rootPart.FromAssetID; + } + return UUID.Zero; + } + + /// + /// Hooks this object up to the backup event so that it is persisted to the database when the update thread executes. + /// + public void AttachToBackup() + { + if (InSceneBackup) + { + //m_log.DebugFormat( + // "[SCENE OBJECT GROUP]: Attaching object {0} {1} to scene presistence sweep", Name, UUID); + + if (!m_isBackedUp) + m_scene.EventManager.OnBackup += ProcessBackup; + + m_isBackedUp = true; + } + } + + /// + /// Attach this object to a scene. It will also now appear to agents. + /// + /// + public void AttachToScene(Scene scene) + { + m_scene = scene; + RegionHandle = m_scene.RegionInfo.RegionHandle; + + if (m_rootPart.Shape.PCode != 9 || m_rootPart.Shape.State == 0) + m_rootPart.ParentID = 0; + if (m_rootPart.LocalId==0) + m_rootPart.LocalId = m_scene.AllocateLocalId(); + + // No need to lock here since the object isn't yet in a scene + foreach (SceneObjectPart part in m_parts.Values) + { + if (Object.ReferenceEquals(part, m_rootPart)) + continue; + if (part.LocalId==0) + part.LocalId = m_scene.AllocateLocalId(); + part.ParentID = m_rootPart.LocalId; + //m_log.DebugFormat("[SCENE]: Given local id {0} to part {1}, linknum {2}, parent {3} {4}", part.LocalId, part.UUID, part.LinkNum, part.ParentID, part.ParentUUID); + } + + ApplyPhysics(m_scene.m_physicalPrim); + + ScheduleGroupForFullUpdate(); + } + + public Vector3 GroupScale() + { + Vector3 minScale = new Vector3(Constants.RegionSize,Constants.RegionSize,Constants.RegionSize); + Vector3 maxScale = new Vector3(0f,0f,0f); + Vector3 finalScale = new Vector3(0.5f, 0.5f, 0.5f); + + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + Vector3 partscale = part.Scale; + Vector3 partoffset = part.OffsetPosition; + + minScale.X = (partscale.X + partoffset.X < minScale.X) ? partscale.X + partoffset.X : minScale.X; + minScale.Y = (partscale.Y + partoffset.Y < minScale.Y) ? partscale.X + partoffset.Y : minScale.Y; + minScale.Z = (partscale.Z + partoffset.Z < minScale.Z) ? partscale.X + partoffset.Z : minScale.Z; + + maxScale.X = (partscale.X + partoffset.X > maxScale.X) ? partscale.X + partoffset.X : maxScale.X; + maxScale.Y = (partscale.Y + partoffset.Y > maxScale.Y) ? partscale.Y + partoffset.Y : maxScale.Y; + maxScale.Z = (partscale.Z + partoffset.Z > maxScale.Z) ? partscale.Z + partoffset.Z : maxScale.Z; + } + } + finalScale.X = (minScale.X > maxScale.X) ? minScale.X : maxScale.X; + finalScale.Y = (minScale.Y > maxScale.Y) ? minScale.Y : maxScale.Y; + finalScale.Z = (minScale.Z > maxScale.Z) ? minScale.Z : maxScale.Z; + return finalScale; + + } + public EntityIntersection TestIntersection(Ray hRay, bool frontFacesOnly, bool faceCenters) + { + // We got a request from the inner_scene to raytrace along the Ray hRay + // We're going to check all of the prim in this group for intersection with the ray + // If we get a result, we're going to find the closest result to the origin of the ray + // and send back the intersection information back to the innerscene. + + EntityIntersection returnresult = new EntityIntersection(); + + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + // Temporary commented to stop compiler warning + //Vector3 partPosition = + // new Vector3(part.AbsolutePosition.X, part.AbsolutePosition.Y, part.AbsolutePosition.Z); + Quaternion parentrotation = GroupRotation; + + // Telling the prim to raytrace. + //EntityIntersection inter = part.TestIntersection(hRay, parentrotation); + + EntityIntersection inter = part.TestIntersectionOBB(hRay, parentrotation,frontFacesOnly, faceCenters); + + // This may need to be updated to the maximum draw distance possible.. + // We might (and probably will) be checking for prim creation from other sims + // when the camera crosses the border. + float idist = Constants.RegionSize; + + + if (inter.HitTF) + { + // We need to find the closest prim to return to the testcaller along the ray + if (inter.distance < idist) + { + returnresult.HitTF = true; + returnresult.ipoint = inter.ipoint; + returnresult.obj = part; + returnresult.normal = inter.normal; + returnresult.distance = inter.distance; + } + } + } + } + return returnresult; + } + + #endregion + + + public string ToXmlString() + { + using (StringWriter sw = new StringWriter()) + { + using (XmlTextWriter writer = new XmlTextWriter(sw)) + { + ToXml(writer); + } + + return sw.ToString(); + } + } + + public void ToXml(XmlTextWriter writer) + { + m_log.DebugFormat("[SOG]: Starting serialization of {0}", Name); + int time = System.Environment.TickCount; + + writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty); + writer.WriteStartElement(String.Empty, "RootPart", String.Empty); + m_rootPart.ToXml(writer); + writer.WriteEndElement(); + writer.WriteStartElement(String.Empty, "OtherParts", String.Empty); + + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + if (part.UUID != m_rootPart.UUID) + { + writer.WriteStartElement(String.Empty, "Part", String.Empty); + part.ToXml(writer); + writer.WriteEndElement(); + } + } + } + + writer.WriteEndElement(); + writer.WriteEndElement(); + + m_log.DebugFormat("[SOG]: Finished serialization of SOG {0}, {1}ms", Name, System.Environment.TickCount - time); + + } + + public string ToXmlString2() + { + using (StringWriter sw = new StringWriter()) + { + using (XmlTextWriter writer = new XmlTextWriter(sw)) + { + ToXml2(writer); + } + + return sw.ToString(); + } + } + + public void ToXml2(XmlTextWriter writer) + { + m_log.DebugFormat("[SOG]: Starting serialization of SOG {0} to XML2", Name); + int time = System.Environment.TickCount; + + writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty); + m_rootPart.ToXml(writer); + writer.WriteStartElement(String.Empty, "OtherParts", String.Empty); + + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + if (part.UUID != m_rootPart.UUID) + { + part.ToXml(writer); + } + } + } + + writer.WriteEndElement(); + writer.WriteEndElement(); + m_log.DebugFormat("[SOG]: Finished serialization of SOG {0} to XML2, {1}ms", Name, System.Environment.TickCount - time); + } + + /// + /// Attach this scene object to the given avatar. + /// + /// + /// + /// + public void AttachToAgent(UUID agentID, uint attachmentpoint, Vector3 AttachOffset, bool silent) + { + ScenePresence avatar = m_scene.GetScenePresence(agentID); + if (avatar != null) + { + // don't attach attachments to child agents + if (avatar.IsChildAgent) return; + + DetachFromBackup(); + + // Remove from database and parcel prim count + // + m_scene.DeleteFromStorage(UUID); + m_scene.EventManager.TriggerParcelPrimCountTainted(); + + m_rootPart.AttachedAvatar = agentID; + + if (m_rootPart.PhysActor != null) + { + m_scene.PhysicsScene.RemovePrim(m_rootPart.PhysActor); + m_rootPart.PhysActor = null; + } + + AbsolutePosition = AttachOffset; + m_rootPart.AttachedPos = AttachOffset; + m_rootPart.IsAttachment = true; + + m_rootPart.SetParentLocalId(avatar.LocalId); + SetAttachmentPoint(Convert.ToByte(attachmentpoint)); + + avatar.AddAttachment(this); + + if (!silent) + { + // Killing it here will cause the client to deselect it + // It then reappears on the avatar, deselected + // through the full update below + // + if (IsSelected) + { + m_scene.SendKillObject(m_rootPart.LocalId); + } + + IsSelected = false; // fudge.... + ScheduleGroupForFullUpdate(); + } + } + } + + public byte GetAttachmentPoint() + { + if (m_rootPart != null) + { + return m_rootPart.Shape.State; + } + return (byte)0; + } + + public void ClearPartAttachmentData() + { + SetAttachmentPoint((Byte)0); + } + + public void DetachToGround() + { + ScenePresence avatar = m_scene.GetScenePresence(m_rootPart.AttachedAvatar); + if (avatar == null) + return; + + avatar.RemoveAttachment(this); + + Vector3 detachedpos = new Vector3(127f,127f,127f); + if (avatar == null) + return; + + detachedpos = avatar.AbsolutePosition; + + AbsolutePosition = detachedpos; + m_rootPart.AttachedAvatar = UUID.Zero; + m_rootPart.SetParentLocalId(0); + SetAttachmentPoint((byte)0); + m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_rootPart.VolumeDetectActive, m_scene.m_physicalPrim); + HasGroupChanged = true; + RootPart.Rezzed = DateTime.Now; + RootPart.RemFlag(PrimFlags.TemporaryOnRez); + AttachToBackup(); + m_scene.EventManager.TriggerParcelPrimCountTainted(); + m_rootPart.ScheduleFullUpdate(); + m_rootPart.ClearUndoState(); + } + + public void DetachToInventoryPrep() + { + ScenePresence avatar = m_scene.GetScenePresence(m_rootPart.AttachedAvatar); + //Vector3 detachedpos = new Vector3(127f, 127f, 127f); + if (avatar != null) + { + //detachedpos = avatar.AbsolutePosition; + avatar.RemoveAttachment(this); + } + + m_rootPart.AttachedAvatar = UUID.Zero; + m_rootPart.SetParentLocalId(0); + //m_rootPart.SetAttachmentPoint((byte)0); + m_rootPart.IsAttachment = false; + AbsolutePosition = m_rootPart.AttachedPos; + //m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_scene.m_physicalPrim); + //AttachToBackup(); + //m_rootPart.ScheduleFullUpdate(); + + } + /// + /// + /// + /// + private void SetPartAsNonRoot(SceneObjectPart part) + { + part.ParentID = m_rootPart.LocalId; + part.ClearUndoState(); + } + + public override void UpdateMovement() + { + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + part.UpdateMovement(); + } + } + } + + public float GetTimeDilation() + { + return m_scene.TimeDilation; + } + + /// + /// Added as a way for the storage provider to reset the scene, + /// most likely a better way to do this sort of thing but for now... + /// + /// + public void SetScene(Scene scene) + { + m_scene = scene; + } + + /// + /// Set a part to act as the root part for this scene object + /// + /// + public void SetRootPart(SceneObjectPart part) + { + part.SetParent(this); + m_rootPart = part; + if (!IsAttachment) + part.ParentID = 0; + part.LinkNum = 0; + + // No locking required since the SOG should not be in the scene yet - one can't change root parts after + // the scene object has been attached to the scene + m_parts.Add(m_rootPart.UUID, m_rootPart); + } + + /// + /// Add a new part to this scene object. The part must already be correctly configured. + /// + /// + public void AddPart(SceneObjectPart part) + { + lock (m_parts) + { + part.SetParent(this); + m_parts.Add(part.UUID, part); + + part.LinkNum = m_parts.Count; + + if (part.LinkNum == 2 && RootPart != null) + RootPart.LinkNum = 1; + } + } + + /// + /// Make sure that every non root part has the proper parent root part local id + /// + private void UpdateParentIDs() + { + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + if (part.UUID != m_rootPart.UUID) + { + part.ParentID = m_rootPart.LocalId; + } + } + } + } + + public void RegenerateFullIDs() + { + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + part.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. + // dahlia: I had to uncomment it, without it meshing was failing on some prims and objects + // at region startup + public void ResetChildPrimPhysicsPositions() + { + AbsolutePosition = AbsolutePosition; // could someone in the know please explain how this works? + } + + public UUID GetPartsFullID(uint localID) + { + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + return part.UUID; + } + return UUID.Zero; + } + + public void ObjectGrabHandler(uint localId, Vector3 offsetPos, IClientAPI remoteClient) + { + if (m_rootPart.LocalId == localId) + { + OnGrabGroup(offsetPos, remoteClient); + } + else + { + SceneObjectPart part = GetChildPart(localId); + OnGrabPart(part, offsetPos, remoteClient); + + } + } + + public virtual void OnGrabPart(SceneObjectPart part, Vector3 offsetPos, IClientAPI remoteClient) + { + part.StoreUndoState(); + part.OnGrab(offsetPos, remoteClient); + } + + public virtual void OnGrabGroup(Vector3 offsetPos, IClientAPI remoteClient) + { + m_scene.EventManager.TriggerGroupGrab(UUID, offsetPos, remoteClient.AgentId); + } + + /// + /// Delete this group from its scene and tell all the scene presences about that deletion. + /// + /// Broadcast deletions to all clients. + public void DeleteGroup(bool silent) + { + // We need to keep track of this state in case this group is still queued for backup. + m_isDeleted = true; + + DetachFromBackup(); + + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { +// part.Inventory.RemoveScriptInstances(); + + List avatars = Scene.GetScenePresences(); + for (int i = 0; i < avatars.Count; i++) + { + if (avatars[i].ParentID == LocalId) + { + avatars[i].StandUp(); + } + + if (!silent) + { + if (m_rootPart != null && part == m_rootPart) + avatars[i].ControllingClient.SendKillObject(m_regionHandle, part.LocalId); + } + } + } + } + } + + public void AddScriptLPS(int count) + { + if (scriptScore + count >= float.MaxValue - count) + scriptScore = 0; + + scriptScore += (float)count; + SceneGraph d = m_scene.m_sceneGraph; + d.AddToScriptLPS(count); + } + + public void AddActiveScriptCount(int count) + { + SceneGraph d = m_scene.m_sceneGraph; + d.AddActiveScripts(count); + } + + public void aggregateScriptEvents() + { + uint objectflagupdate=(uint)RootPart.GetEffectiveObjectFlags(); + + scriptEvents aggregateScriptEvents=0; + + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + if (part == null) + continue; + if (part != RootPart) + part.ObjectFlags = objectflagupdate; + aggregateScriptEvents |= part.AggregateScriptEvents; + } + } + + if ((aggregateScriptEvents & scriptEvents.at_target) != 0) + { + m_scriptListens_atTarget = true; + } + else + { + m_scriptListens_atTarget = false; + } + + if ((aggregateScriptEvents & scriptEvents.not_at_target) != 0) + { + m_scriptListens_notAtTarget = true; + } + else + { + m_scriptListens_notAtTarget = false; + } + + if (m_scriptListens_atTarget || m_scriptListens_notAtTarget) + { + } + else + { + lock (m_targets) + m_targets.Clear(); + } + + ScheduleGroupForFullUpdate(); + } + + public override void SetText(string text, Vector3 color, double alpha) + { + Color = Color.FromArgb(0xff - (int) (alpha * 0xff), + (int) (color.X * 0xff), + (int) (color.Y * 0xff), + (int) (color.Z * 0xff)); + Text = text; + + HasGroupChanged = true; + m_rootPart.ScheduleFullUpdate(); + } + + /// + /// Apply physics to this group + /// + /// + public void ApplyPhysics(bool m_physicalPrim) + { + lock (m_parts) + { + if (m_parts.Count > 1) + { + m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_rootPart.VolumeDetectActive, m_physicalPrim); + foreach (SceneObjectPart part in m_parts.Values) + { + if (part.LocalId != m_rootPart.LocalId) + { + part.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), part.VolumeDetectActive, m_physicalPrim); + } + } + + // Hack to get the physics scene geometries in the right spot + ResetChildPrimPhysicsPositions(); + } + else + { + m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_rootPart.VolumeDetectActive, m_physicalPrim); + } + } + } + + public void SetOwnerId(UUID userId) + { + ForEachPart(delegate(SceneObjectPart part) { part.OwnerID = userId; }); + } + + public void ForEachPart(Action whatToDo) + { + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + whatToDo(part); + } + } + } + + #region Events + + /// + /// Processes backup. + /// + /// + public void ProcessBackup(IRegionDataStore datastore, bool forcedBackup) + { + if (!m_isBackedUp) + return; + + // Since this is the top of the section of call stack for backing up a particular scene object, don't let + // any exception propogate upwards. + + if (IsDeleted || UUID == UUID.Zero) + return; + + try + { + if (!m_scene.ShuttingDown) // if shutting down then there will be nothing to handle the return so leave till next restart + { + ILandObject parcel = m_scene.LandChannel.GetLandObject( + m_rootPart.GroupPosition.X, m_rootPart.GroupPosition.Y); + + if (parcel != null && parcel.landData != null && + parcel.landData.OtherCleanTime != 0) + { + if (parcel.landData.OwnerID != OwnerID && + (parcel.landData.GroupID != GroupID || + parcel.landData.GroupID == UUID.Zero)) + { + if ((DateTime.Now - RootPart.Rezzed).TotalMinutes > + parcel.landData.OtherCleanTime) + { + DetachFromBackup(); + m_log.InfoFormat("[SCENE]: Returning object {0} due to parcel auto return", RootPart.UUID.ToString()); + m_scene.AddReturn(OwnerID, Name, AbsolutePosition, "parcel auto return"); + m_scene.DeRezObject(null, RootPart.LocalId, + RootPart.GroupID, DeRezAction.Return, UUID.Zero); + + return; + } + } + } + } + + if (HasGroupChanged) + { + // don't backup while it's selected or you're asking for changes mid stream. + if ((isTimeToPersist()) || (forcedBackup)) + { + m_log.DebugFormat( + "[SCENE]: Storing {0}, {1} in {2}", + Name, UUID, m_scene.RegionInfo.RegionName); + + SceneObjectGroup backup_group = Copy(OwnerID, GroupID, false); + backup_group.RootPart.Velocity = RootPart.Velocity; + backup_group.RootPart.Acceleration = RootPart.Acceleration; + backup_group.RootPart.AngularVelocity = RootPart.AngularVelocity; + backup_group.RootPart.ParticleSystem = RootPart.ParticleSystem; + HasGroupChanged = false; + + datastore.StoreObject(backup_group, m_scene.RegionInfo.RegionID); + + backup_group.ForEachPart(delegate(SceneObjectPart part) + { + part.Inventory.ProcessInventoryBackup(datastore); + }); + + backup_group = null; + } + // else + // { + // m_log.DebugFormat( + // "[SCENE]: Did not update persistence of object {0} {1}, selected = {2}", + // Name, UUID, IsSelected); + // } + } + } + catch (Exception e) + { + m_log.ErrorFormat( + "[SCENE]: Storing of {0}, {1} in {2} failed with exception {3}\n\t{4}", + Name, UUID, m_scene.RegionInfo.RegionName, e, e.StackTrace); + } + } + + #endregion + + #region Client Updating + + public void SendFullUpdateToClient(IClientAPI remoteClient) + { + SendPartFullUpdate(remoteClient, RootPart, m_scene.Permissions.GenerateClientFlags(remoteClient.AgentId, RootPart.UUID)); + + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + if (part != RootPart) + SendPartFullUpdate(remoteClient, part, m_scene.Permissions.GenerateClientFlags(remoteClient.AgentId, part.UUID)); + } + } + } + + /// + /// Send a full update to the client for the given part + /// + /// + /// + internal void SendPartFullUpdate(IClientAPI remoteClient, SceneObjectPart part, uint clientFlags) + { + if (m_rootPart != null && m_rootPart.UUID == part.UUID) + { + if (IsAttachment) + { + part.SendFullUpdateToClient(remoteClient, m_rootPart.AttachedPos, clientFlags); + } + else + { + part.SendFullUpdateToClient(remoteClient, AbsolutePosition, clientFlags); + } + } + else + { + part.SendFullUpdateToClient(remoteClient, clientFlags); + } + } + + #endregion + + #region Copying + + /// + /// Duplicates this object, including operations such as physics set up and attaching to the backup event. + /// + /// + public SceneObjectGroup Copy(UUID cAgentID, UUID cGroupID, bool userExposed) + { + SceneObjectGroup dupe = (SceneObjectGroup)MemberwiseClone(); + dupe.m_isBackedUp = false; + dupe.m_parts = new Dictionary(); + dupe.AbsolutePosition = new Vector3(AbsolutePosition.X, AbsolutePosition.Y, AbsolutePosition.Z); + + dupe.CopyRootPart(m_rootPart, OwnerID, GroupID, userExposed); + dupe.m_rootPart.LinkNum = m_rootPart.LinkNum; + + if (userExposed) + dupe.m_rootPart.TrimPermissions(); + + /// may need to create a new Physics actor. + if (dupe.RootPart.PhysActor != null && userExposed) + { + PrimitiveBaseShape pbs = dupe.RootPart.Shape; + + dupe.RootPart.PhysActor = m_scene.PhysicsScene.AddPrimShape( + dupe.RootPart.Name, + pbs, + new PhysicsVector(dupe.RootPart.AbsolutePosition.X, dupe.RootPart.AbsolutePosition.Y, dupe.RootPart.AbsolutePosition.Z), + new PhysicsVector(dupe.RootPart.Scale.X, dupe.RootPart.Scale.Y, dupe.RootPart.Scale.Z), + dupe.RootPart.RotationOffset, + dupe.RootPart.PhysActor.IsPhysical); + + dupe.RootPart.PhysActor.LocalID = dupe.RootPart.LocalId; + dupe.RootPart.DoPhysicsPropertyUpdate(dupe.RootPart.PhysActor.IsPhysical, true); + } + + // Now we've made a copy that replaces this one, we need to + // switch the owner to the person who did the copying + // Second Life copies an object and duplicates the first one in it's place + // So, we have to make a copy of this one, set it in it's place then set the owner on this one + if (userExposed) + { + SetRootPartOwner(m_rootPart, cAgentID, cGroupID); + m_rootPart.ScheduleFullUpdate(); + } + + List partList; + + lock (m_parts) + { + partList = new List(m_parts.Values); + } + + partList.Sort(delegate(SceneObjectPart p1, SceneObjectPart p2) + { + return p1.LinkNum.CompareTo(p2.LinkNum); + } + ); + + foreach (SceneObjectPart part in partList) + { + if (part.UUID != m_rootPart.UUID) + { + SceneObjectPart newPart = dupe.CopyPart(part, OwnerID, GroupID, userExposed); + + newPart.LinkNum = part.LinkNum; + + if (userExposed) + { + SetPartOwner(newPart, cAgentID, cGroupID); + newPart.ScheduleFullUpdate(); + } + } + } + + if (userExposed) + { + dupe.UpdateParentIDs(); + dupe.HasGroupChanged = true; + dupe.AttachToBackup(); + + ScheduleGroupForFullUpdate(); + } + + return dupe; + } + + /// + /// + /// + /// + /// + /// + public void CopyRootPart(SceneObjectPart part, UUID cAgentID, UUID cGroupID, bool userExposed) + { + SetRootPart(part.Copy(m_scene.AllocateLocalId(), OwnerID, GroupID, m_parts.Count, userExposed)); + } + + public void ScriptSetPhysicsStatus(bool UsePhysics) + { + bool IsTemporary = ((RootPart.Flags & PrimFlags.TemporaryOnRez) != 0); + bool IsPhantom = ((RootPart.Flags & PrimFlags.Phantom) != 0); + bool IsVolumeDetect = RootPart.VolumeDetectActive; + UpdatePrimFlags(RootPart.LocalId, UsePhysics, IsTemporary, IsPhantom, IsVolumeDetect); + } + + public void ScriptSetTemporaryStatus(bool TemporaryStatus) + { + bool UsePhysics = ((RootPart.Flags & PrimFlags.Physics) != 0); + bool IsPhantom = ((RootPart.Flags & PrimFlags.Phantom) != 0); + bool IsVolumeDetect = RootPart.VolumeDetectActive; + UpdatePrimFlags(RootPart.LocalId, UsePhysics, TemporaryStatus, IsPhantom, IsVolumeDetect); + } + + public void ScriptSetPhantomStatus(bool PhantomStatus) + { + bool UsePhysics = ((RootPart.Flags & PrimFlags.Physics) != 0); + bool IsTemporary = ((RootPart.Flags & PrimFlags.TemporaryOnRez) != 0); + bool IsVolumeDetect = RootPart.VolumeDetectActive; + UpdatePrimFlags(RootPart.LocalId, UsePhysics, IsTemporary, PhantomStatus, IsVolumeDetect); + } + + public void ScriptSetVolumeDetect(bool VDStatus) + { + bool UsePhysics = ((RootPart.Flags & PrimFlags.Physics) != 0); + bool IsTemporary = ((RootPart.Flags & PrimFlags.TemporaryOnRez) != 0); + bool IsPhantom = ((RootPart.Flags & PrimFlags.Phantom) != 0); + UpdatePrimFlags(RootPart.LocalId, UsePhysics, IsTemporary, IsPhantom, VDStatus); + + /* + ScriptSetPhantomStatus(false); // What ever it was before, now it's not phantom anymore + + if (PhysActor != null) // Should always be the case now + { + PhysActor.SetVolumeDetect(param); + } + if (param != 0) + AddFlag(PrimFlags.Phantom); + + ScheduleFullUpdate(); + */ + } + + public void applyImpulse(PhysicsVector impulse) + { + // We check if rootpart is null here because scripts don't delete if you delete the host. + // This means that unfortunately, we can pass a null physics actor to Simulate! + // Make sure we don't do that! + SceneObjectPart rootpart = m_rootPart; + if (rootpart != null) + { + if (rootpart.PhysActor != null) + { + if (IsAttachment) + { + ScenePresence avatar = m_scene.GetScenePresence(rootpart.AttachedAvatar); + if (avatar != null) + { + avatar.PushForce(impulse); + } + } + else + { + rootpart.PhysActor.AddForce(impulse,true); + m_scene.PhysicsScene.AddPhysicsActorTaint(rootpart.PhysActor); + } + } + } + } + + public void applyAngularImpulse(PhysicsVector impulse) + { + // We check if rootpart is null here because scripts don't delete if you delete the host. + // This means that unfortunately, we can pass a null physics actor to Simulate! + // Make sure we don't do that! + SceneObjectPart rootpart = m_rootPart; + if (rootpart != null) + { + if (rootpart.PhysActor != null) + { + if (!IsAttachment) + { + rootpart.PhysActor.AddAngularForce(impulse, true); + m_scene.PhysicsScene.AddPhysicsActorTaint(rootpart.PhysActor); + } + } + } + } + + public void setAngularImpulse(PhysicsVector impulse) + { + // We check if rootpart is null here because scripts don't delete if you delete the host. + // This means that unfortunately, we can pass a null physics actor to Simulate! + // Make sure we don't do that! + SceneObjectPart rootpart = m_rootPart; + if (rootpart != null) + { + if (rootpart.PhysActor != null) + { + if (!IsAttachment) + { + rootpart.PhysActor.Torque = impulse; + m_scene.PhysicsScene.AddPhysicsActorTaint(rootpart.PhysActor); + } + } + } + } + + public Vector3 GetTorque() + { + // We check if rootpart is null here because scripts don't delete if you delete the host. + // This means that unfortunately, we can pass a null physics actor to Simulate! + // Make sure we don't do that! + SceneObjectPart rootpart = m_rootPart; + if (rootpart != null) + { + if (rootpart.PhysActor != null) + { + if (!IsAttachment) + { + PhysicsVector torque = rootpart.PhysActor.Torque; + return new Vector3(torque.X, torque.Y, torque.Z); + } + } + } + return Vector3.Zero; + } + + public void moveToTarget(Vector3 target, float tau) + { + SceneObjectPart rootpart = m_rootPart; + if (rootpart != null) + { + if (rootpart.PhysActor != null) + { + rootpart.PhysActor.PIDTarget = new PhysicsVector(target.X, target.Y, target.Z); + rootpart.PhysActor.PIDTau = tau; + rootpart.PhysActor.PIDActive = true; + } + } + } + + public void stopMoveToTarget() + { + SceneObjectPart rootpart = m_rootPart; + if (rootpart != null) + { + if (rootpart.PhysActor != null) + { + rootpart.PhysActor.PIDActive = false; + } + } + } + + /// + /// Set the owner of the root part. + /// + /// + /// + /// + public void SetRootPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID) + { + part.LastOwnerID = part.OwnerID; + part.OwnerID = cAgentID; + part.GroupID = cGroupID; + + if (part.OwnerID != cAgentID) + { + // Apply Next Owner Permissions if we're not bypassing permissions + if (!m_scene.Permissions.BypassPermissions()) + ApplyNextOwnerPermissions(); + } + + part.ScheduleFullUpdate(); + } + + /// + /// Make a copy of the given part. + /// + /// + /// + /// + public SceneObjectPart CopyPart(SceneObjectPart part, UUID cAgentID, UUID cGroupID, bool userExposed) + { + SceneObjectPart newPart = part.Copy(m_scene.AllocateLocalId(), OwnerID, GroupID, m_parts.Count, userExposed); + newPart.SetParent(this); + + lock (m_parts) + { + m_parts.Add(newPart.UUID, newPart); + } + + SetPartAsNonRoot(newPart); + + return newPart; + } + + /// + /// Reset the UUIDs for all the prims that make up this group. + /// + /// This is called by methods which want to add a new group to an existing scene, in order + /// to ensure that there are no clashes with groups already present. + /// + public void ResetIDs() + { + // As this is only ever called for prims which are not currently part of the scene (and hence + // not accessible by clients), there should be no need to lock + List partsList = new List(m_parts.Values); + m_parts.Clear(); + foreach (SceneObjectPart part in partsList) + { + part.ResetIDs(part.LinkNum); // Don't change link nums + m_parts.Add(part.UUID, part); + } + } + + /// + /// + /// + /// + public void ServiceObjectPropertiesFamilyRequest(IClientAPI remoteClient, UUID AgentID, uint RequestFlags) + { + + remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.ObjectOwner, RootPart.GroupID, RootPart.BaseMask, + RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask, + RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category, + RootPart.CreatorID, RootPart.Name, RootPart.Description); + } + + public void SetPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID) + { + part.OwnerID = cAgentID; + part.GroupID = cGroupID; + } + + #endregion + + #region Scheduling + + public override void Update() + { + // Check that the group was not deleted before the scheduled update + // FIXME: This is merely a temporary measure to reduce the incidence of failure when + // an object has been deleted from a scene before update was processed. + // A more fundamental overhaul of the update mechanism is required to eliminate all + // the race conditions. + if (m_isDeleted) + return; + + // This is what happens when an orphanced link set child prim's + // group was queued when it was linked + // + if (m_rootPart == null) + return; + + lock (m_parts) + { + bool UsePhysics = ((RootPart.Flags & PrimFlags.Physics) != 0); + + //if (IsAttachment) + //{ + //foreach (SceneObjectPart part in m_parts.Values) + //{ + //part.SendScheduledUpdates(); + //} + //return; + //} + + if ((Util.GetDistanceTo(lastPhysGroupPos, AbsolutePosition) > 0.02) && UsePhysics) + { + m_rootPart.UpdateFlag = 1; + lastPhysGroupPos = AbsolutePosition; + } + //foreach (SceneObjectPart part in m_parts.Values) + //{ + //if (part.UpdateFlag == 0) part.UpdateFlag = 1; + //} + + checkAtTargets(); + + if (((Math.Abs(lastPhysGroupRot.W - GroupRotation.W) > 0.1) + || (Math.Abs(lastPhysGroupRot.X - GroupRotation.X) > 0.1) + || (Math.Abs(lastPhysGroupRot.Y - GroupRotation.Y) > 0.1) + || (Math.Abs(lastPhysGroupRot.Z - GroupRotation.Z) > 0.1)) + && UsePhysics) + { + m_rootPart.UpdateFlag = 1; + + lastPhysGroupRot = GroupRotation; + } + + foreach (SceneObjectPart part in m_parts.Values) + { + part.SendScheduledUpdates(); + } + } + } + + public void ScheduleFullUpdateToAvatar(ScenePresence presence) + { + RootPart.AddFullUpdateToAvatar(presence); + + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + if (part != RootPart) + part.AddFullUpdateToAvatar(presence); + } + } + } + + public void ScheduleTerseUpdateToAvatar(ScenePresence presence) + { + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + part.AddTerseUpdateToAvatar(presence); + } + } + } + + /// + /// Schedule a full update for this scene object + /// + public void ScheduleGroupForFullUpdate() + { + checkAtTargets(); + RootPart.ScheduleFullUpdate(); + + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + if (part != RootPart) + part.ScheduleFullUpdate(); + } + } + } + + /// + /// Schedule a terse update for this scene object + /// + public void ScheduleGroupForTerseUpdate() + { + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + part.ScheduleTerseUpdate(); + } + } + } + + /// + /// Immediately send a full update for this scene object. + /// + public void SendGroupFullUpdate() + { + if (IsDeleted) + return; + + RootPart.SendFullUpdateToAllClients(); + + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + if (part != RootPart) + part.SendFullUpdateToAllClients(); + } + } + } + + /// + /// Immediately send an update for this scene object's root prim only. + /// This is for updates regarding the object as a whole, and none of its parts in particular. + /// Note: this may not be cused by opensim (it probably should) but it's used by + /// external modules. + /// + public void SendGroupRootUpdate() + { + if (IsDeleted) + return; + + RootPart.SendFullUpdateToAllClients(); + } + + public void QueueForUpdateCheck() + { + if (m_scene == null) // Need to check here as it's null during object creation + return; + m_scene.m_sceneGraph.AddToUpdateList(this); + } + + /// + /// Immediately send a terse update for this scene object. + /// + public void SendGroupTerseUpdate() + { + if (IsDeleted) + return; + + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + part.SendTerseUpdateToAllClients(); + } + } + } + + #endregion + + #region SceneGroupPart Methods + + /// + /// Get the child part by LinkNum + /// + /// + /// null if no child part with that linknum or child part + public SceneObjectPart GetLinkNumPart(int linknum) + { + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + if (part.LinkNum == linknum) + { + return part; + } + } + } + + return null; + } + + /// + /// Get a child part with a given UUID + /// + /// + /// null if a child part with the primID was not found + public SceneObjectPart GetChildPart(UUID primID) + { + SceneObjectPart childPart = null; + if (m_parts.ContainsKey(primID)) + { + childPart = m_parts[primID]; + } + return childPart; + } + + /// + /// Get a child part with a given local ID + /// + /// + /// null if a child part with the local ID was not found + public SceneObjectPart GetChildPart(uint localID) + { + //m_log.DebugFormat("Entered looking for {0}", localID); + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + //m_log.DebugFormat("Found {0}", part.LocalId); + if (part.LocalId == localID) + { + return part; + } + } + } + + return null; + } + + /// + /// Does this group contain the child prim + /// should be able to remove these methods once we have a entity index in scene + /// + /// + /// + public bool HasChildPrim(UUID primID) + { + if (m_parts.ContainsKey(primID)) + { + return true; + } + + return false; + } + + /// + /// Does this group contain the child prim + /// should be able to remove these methods once we have a entity index in scene + /// + /// + /// + public bool HasChildPrim(uint localID) + { + //m_log.DebugFormat("Entered HasChildPrim looking for {0}", localID); + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + //m_log.DebugFormat("Found {0}", part.LocalId); + if (part.LocalId == localID) + { + return true; + } + } + } + + return false; + } + + #endregion + + #region Packet Handlers + + /// + /// Link the prims in a given group to this group + /// + /// The group of prims which should be linked to this group + public void LinkToGroup(SceneObjectGroup objectGroup) + { + if (objectGroup.RootPart.UpdateFlag > 0) + { + // I've never actually seen this happen, though I think it's theoretically possible + m_log.WarnFormat( + "[SCENE OBJECT GROUP]: Aborted linking {0}, {1} to {2}, {3} as it has yet to finish delinking", + objectGroup.RootPart.Name, objectGroup.RootPart.UUID, RootPart.Name, RootPart.UUID); + + return; + } + +// m_log.DebugFormat( +// "[SCENE OBJECT GROUP]: Linking group with root part {0}, {1} to group with root part {2}, {3}", +// objectGroup.RootPart.Name, objectGroup.RootPart.UUID, RootPart.Name, RootPart.UUID); + + SceneObjectPart linkPart = objectGroup.m_rootPart; + + Vector3 oldGroupPosition = linkPart.GroupPosition; + Quaternion oldRootRotation = linkPart.RotationOffset; + + linkPart.OffsetPosition = linkPart.GroupPosition - AbsolutePosition; + linkPart.GroupPosition = AbsolutePosition; + Vector3 axPos = linkPart.OffsetPosition; + + Quaternion parentRot = m_rootPart.RotationOffset; + axPos *= Quaternion.Inverse(parentRot); + + linkPart.OffsetPosition = axPos; + Quaternion oldRot = linkPart.RotationOffset; + Quaternion newRot = Quaternion.Inverse(parentRot) * oldRot; + linkPart.RotationOffset = newRot; + + linkPart.ParentID = m_rootPart.LocalId; + if (m_rootPart.LinkNum == 0) + m_rootPart.LinkNum = 1; + + lock (m_parts) + { + m_parts.Add(linkPart.UUID, linkPart); + + // Insert in terms of link numbers, the new links + // before the current ones (with the exception of + // the root prim. Shuffle the old ones up + foreach (KeyValuePair kvp in m_parts) + { + if (kvp.Value.LinkNum != 1) + { + // Don't update root prim link number + kvp.Value.LinkNum += objectGroup.PrimCount; + } + } + + linkPart.LinkNum = 2; + + linkPart.SetParent(this); + linkPart.AddFlag(PrimFlags.CreateSelected); + + //if (linkPart.PhysActor != null) + //{ + // m_scene.PhysicsScene.RemovePrim(linkPart.PhysActor); + + //linkPart.PhysActor = null; + //} + + //TODO: rest of parts + int linkNum = 3; + foreach (SceneObjectPart part in objectGroup.Children.Values) + { + if (part.UUID != objectGroup.m_rootPart.UUID) + { + LinkNonRootPart(part, oldGroupPosition, oldRootRotation, linkNum++); + } + part.ClearUndoState(); + } + } + + m_scene.UnlinkSceneObject(objectGroup.UUID, true); + objectGroup.m_isDeleted = true; + + lock (objectGroup.m_parts) + { + objectGroup.m_parts.Clear(); + } + + // Can't do this yet since backup still makes use of the root part without any synchronization +// objectGroup.m_rootPart = null; + + AttachToBackup(); + HasGroupChanged = true; + ScheduleGroupForFullUpdate(); + } + + /// + /// Delink the given prim from this group. The delinked prim is established as + /// an independent SceneObjectGroup. + /// + /// + public void DelinkFromGroup(uint partID) + { + DelinkFromGroup(partID, true); + } + + /// + /// Delink the given prim from this group. The delinked prim is established as + /// an independent SceneObjectGroup. + /// + /// + /// + public void DelinkFromGroup(uint partID, bool sendEvents) + { + SceneObjectPart linkPart = GetChildPart(partID); + + if (null != linkPart) + { + linkPart.ClearUndoState(); +// m_log.DebugFormat( +// "[SCENE OBJECT GROUP]: Delinking part {0}, {1} from group with root part {2}, {3}", +// linkPart.Name, linkPart.UUID, RootPart.Name, RootPart.UUID); + + Quaternion worldRot = linkPart.GetWorldRotation(); + + // Remove the part from this object + lock (m_parts) + { + m_parts.Remove(linkPart.UUID); + } + + if (m_parts.Count == 1 && RootPart != null) //Single prim is left + RootPart.LinkNum = 0; + else + { + foreach (SceneObjectPart p in m_parts.Values) + { + if (p.LinkNum > linkPart.LinkNum) + p.LinkNum--; + } + } + + linkPart.ParentID = 0; + linkPart.LinkNum = 0; + + if (linkPart.PhysActor != null) + { + m_scene.PhysicsScene.RemovePrim(linkPart.PhysActor); + } + + // We need to reset the child part's position + // ready for life as a separate object after being a part of another object + Quaternion parentRot = m_rootPart.RotationOffset; + + Vector3 axPos = linkPart.OffsetPosition; + + axPos *= parentRot; + linkPart.OffsetPosition = new Vector3(axPos.X, axPos.Y, axPos.Z); + linkPart.GroupPosition = AbsolutePosition + linkPart.OffsetPosition; + linkPart.OffsetPosition = new Vector3(0, 0, 0); + + linkPart.RotationOffset = worldRot; + + SceneObjectGroup objectGroup = new SceneObjectGroup(linkPart); + + m_scene.AddNewSceneObject(objectGroup, true); + + if (sendEvents) + linkPart.TriggerScriptChangedEvent(Changed.LINK); + + linkPart.Rezzed = RootPart.Rezzed; + + HasGroupChanged = true; + ScheduleGroupForFullUpdate(); + } + else + { + m_log.InfoFormat("[SCENE OBJECT GROUP]: " + + "DelinkFromGroup(): Child prim {0} not found in object {1}, {2}", + partID, LocalId, UUID); + } + } + + /// + /// Stop this object from being persisted over server restarts. + /// + /// + public void DetachFromBackup() + { + if (m_isBackedUp) + m_scene.EventManager.OnBackup -= ProcessBackup; + + m_isBackedUp = false; + } + + private void LinkNonRootPart(SceneObjectPart part, Vector3 oldGroupPosition, Quaternion oldGroupRotation, int linkNum) + { + + Quaternion parentRot = oldGroupRotation; + Quaternion oldRot = part.RotationOffset; + Quaternion worldRot = parentRot * oldRot; + + parentRot = oldGroupRotation; + + Vector3 axPos = part.OffsetPosition; + + axPos *= parentRot; + part.OffsetPosition = axPos; + part.GroupPosition = oldGroupPosition + part.OffsetPosition; + part.OffsetPosition = Vector3.Zero; + part.RotationOffset = worldRot; + + part.SetParent(this); + part.ParentID = m_rootPart.LocalId; + + // Caller locks m_parts for us + m_parts.Add(part.UUID, part); + + part.LinkNum = linkNum; + + + part.OffsetPosition = part.GroupPosition - AbsolutePosition; + + Quaternion rootRotation = m_rootPart.RotationOffset; + + Vector3 pos = part.OffsetPosition; + pos *= Quaternion.Inverse(rootRotation); + part.OffsetPosition = pos; + + parentRot = m_rootPart.RotationOffset; + oldRot = part.RotationOffset; + Quaternion newRot = Quaternion.Inverse(parentRot) * oldRot; + part.RotationOffset = newRot; + } + + /// + /// If object is physical, apply force to move it around + /// If object is not physical, just put it at the resulting location + /// + /// Always seems to be 0,0,0, so ignoring + /// New position. We do the math here to turn it into a force + /// + public void GrabMovement(Vector3 offset, Vector3 pos, IClientAPI remoteClient) + { + if (m_scene.EventManager.TriggerGroupMove(UUID, pos)) + { + if (m_rootPart.PhysActor != null) + { + if (m_rootPart.PhysActor.IsPhysical) + { + Vector3 llmoveforce = pos - AbsolutePosition; + PhysicsVector grabforce = new PhysicsVector(llmoveforce.X, llmoveforce.Y, llmoveforce.Z); + grabforce = (grabforce / 10) * m_rootPart.PhysActor.Mass; + m_rootPart.PhysActor.AddForce(grabforce,true); + m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor); + } + else + { + //NonPhysicalGrabMovement(pos); + } + } + else + { + //NonPhysicalGrabMovement(pos); + } + } + } + + public void NonPhysicalGrabMovement(Vector3 pos) + { + AbsolutePosition = pos; + m_rootPart.SendTerseUpdateToAllClients(); + } + + /// + /// Return metadata about a prim (name, description, sale price, etc.) + /// + /// + public void GetProperties(IClientAPI client) + { + m_rootPart.GetProperties(client); + } + + /// + /// Set the name of a prim + /// + /// + /// + public void SetPartName(string name, uint localID) + { + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + part.Name = name; + } + } + + public void SetPartDescription(string des, uint localID) + { + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + part.Description = des; + } + } + + public void SetPartText(string text, uint localID) + { + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + part.SetText(text); + } + } + + public void SetPartText(string text, UUID partID) + { + SceneObjectPart part = GetChildPart(partID); + if (part != null) + { + part.SetText(text); + } + } + + public string GetPartName(uint localID) + { + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + return part.Name; + } + return String.Empty; + } + + public string GetPartDescription(uint localID) + { + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + return part.Description; + } + return String.Empty; + } + + /// + /// Update prim flags for this group. + /// + /// + /// + /// + /// + public void UpdatePrimFlags(uint localID, bool UsePhysics, bool IsTemporary, bool IsPhantom, bool IsVolumeDetect) + { + SceneObjectPart selectionPart = GetChildPart(localID); + + if (IsTemporary) + { + DetachFromBackup(); + // Remove from database and parcel prim count + // + m_scene.DeleteFromStorage(UUID); + m_scene.EventManager.TriggerParcelPrimCountTainted(); + } + + if (selectionPart != null) + { + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + if (part.Scale.X > 10.0 || part.Scale.Y > 10.0 || part.Scale.Z > 10.0) + { + UsePhysics = false; // Reset physics + break; + } + } + + foreach (SceneObjectPart part in m_parts.Values) + { + part.UpdatePrimFlags(UsePhysics, IsTemporary, IsPhantom, IsVolumeDetect); + } + } + } + } + + public void UpdateExtraParam(uint localID, ushort type, bool inUse, byte[] data) + { + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + part.UpdateExtraParam(type, inUse, data); + } + } + + /// + /// Get the parts of this scene object + /// + /// + public SceneObjectPart[] GetParts() + { + int numParts = Children.Count; + SceneObjectPart[] partArray = new SceneObjectPart[numParts]; + Children.Values.CopyTo(partArray, 0); + return partArray; + } + + /// + /// Update the texture entry for this part + /// + /// + /// + public void UpdateTextureEntry(uint localID, byte[] textureEntry) + { + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + part.UpdateTextureEntry(textureEntry); + } + } + + public void UpdatePermissions(UUID AgentID, byte field, uint localID, + uint mask, byte addRemTF) + { + foreach (SceneObjectPart part in m_parts.Values) + part.UpdatePermissions(AgentID, field, localID, mask, + addRemTF); + + HasGroupChanged = true; + } + + #endregion + + #region Shape + + /// + /// + /// + /// + public void UpdateShape(ObjectShapePacket.ObjectDataBlock shapeBlock, uint localID) + { + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + part.UpdateShape(shapeBlock); + + if (part.PhysActor != null) + m_scene.PhysicsScene.AddPhysicsActorTaint(part.PhysActor); + } + } + + #endregion + + #region Resize + + /// + /// Resize the given part + /// + /// + /// + public void Resize(Vector3 scale, uint localID) + { + if (scale.X > m_scene.m_maxNonphys) + scale.X = m_scene.m_maxNonphys; + if (scale.Y > m_scene.m_maxNonphys) + scale.Y = m_scene.m_maxNonphys; + if (scale.Z > m_scene.m_maxNonphys) + scale.Z = m_scene.m_maxNonphys; + + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + part.Resize(scale); + if (part.PhysActor != null) + { + if (part.PhysActor.IsPhysical) + { + if (scale.X > m_scene.m_maxPhys) + scale.X = m_scene.m_maxPhys; + if (scale.Y > m_scene.m_maxPhys) + scale.Y = m_scene.m_maxPhys; + if (scale.Z > m_scene.m_maxPhys) + scale.Z = m_scene.m_maxPhys; + } + part.PhysActor.Size = + new PhysicsVector(scale.X, scale.Y, scale.Z); + m_scene.PhysicsScene.AddPhysicsActorTaint(part.PhysActor); + } + //if (part.UUID != m_rootPart.UUID) + + HasGroupChanged = true; + ScheduleGroupForFullUpdate(); + + //if (part.UUID == m_rootPart.UUID) + //{ + //if (m_rootPart.PhysActor != null) + //{ + //m_rootPart.PhysActor.Size = + //new PhysicsVector(m_rootPart.Scale.X, m_rootPart.Scale.Y, m_rootPart.Scale.Z); + //m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor); + //} + //} + } + } + + public void GroupResize(Vector3 scale, uint localID) + { + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + if (scale.X > m_scene.m_maxNonphys) + scale.X = m_scene.m_maxNonphys; + if (scale.Y > m_scene.m_maxNonphys) + scale.Y = m_scene.m_maxNonphys; + if (scale.Z > m_scene.m_maxNonphys) + scale.Z = m_scene.m_maxNonphys; + if (part.PhysActor != null && part.PhysActor.IsPhysical) + { + if (scale.X > m_scene.m_maxPhys) + scale.X = m_scene.m_maxPhys; + if (scale.Y > m_scene.m_maxPhys) + scale.Y = m_scene.m_maxPhys; + if (scale.Z > m_scene.m_maxPhys) + scale.Z = m_scene.m_maxPhys; + } + float x = (scale.X / part.Scale.X); + float y = (scale.Y / part.Scale.Y); + float z = (scale.Z / part.Scale.Z); + + lock (m_parts) + { + if (x > 1.0f || y > 1.0f || z > 1.0f) + { + foreach (SceneObjectPart obPart in m_parts.Values) + { + if (obPart.UUID != m_rootPart.UUID) + { + Vector3 oldSize = new Vector3(obPart.Scale); + + float f = 1.0f; + float a = 1.0f; + + if (part.PhysActor != null && part.PhysActor.IsPhysical) + { + if (oldSize.X*x > m_scene.m_maxPhys) + { + f = m_scene.m_maxPhys / oldSize.X; + a = f / x; + x *= a; + y *= a; + z *= a; + } + if (oldSize.Y*y > m_scene.m_maxPhys) + { + f = m_scene.m_maxPhys / oldSize.Y; + a = f / y; + x *= a; + y *= a; + z *= a; + } + if (oldSize.Z*z > m_scene.m_maxPhys) + { + f = m_scene.m_maxPhys / oldSize.Z; + a = f / z; + x *= a; + y *= a; + z *= a; + } + } + else + { + if (oldSize.X*x > m_scene.m_maxNonphys) + { + f = m_scene.m_maxNonphys / oldSize.X; + a = f / x; + x *= a; + y *= a; + z *= a; + } + if (oldSize.Y*y > m_scene.m_maxNonphys) + { + f = m_scene.m_maxNonphys / oldSize.Y; + a = f / y; + x *= a; + y *= a; + z *= a; + } + if (oldSize.Z*z > m_scene.m_maxNonphys) + { + f = m_scene.m_maxNonphys / oldSize.Z; + a = f / z; + x *= a; + y *= a; + z *= a; + } + } + } + } + } + } + + Vector3 prevScale = part.Scale; + prevScale.X *= x; + prevScale.Y *= y; + prevScale.Z *= z; + part.Resize(prevScale); + + lock (m_parts) + { + foreach (SceneObjectPart obPart in m_parts.Values) + { + if (obPart.UUID != m_rootPart.UUID) + { + Vector3 currentpos = new Vector3(obPart.OffsetPosition); + currentpos.X *= x; + currentpos.Y *= y; + currentpos.Z *= z; + Vector3 newSize = new Vector3(obPart.Scale); + newSize.X *= x; + newSize.Y *= y; + newSize.Z *= z; + obPart.Resize(newSize); + obPart.UpdateOffSet(currentpos); + } + } + } + + if (part.PhysActor != null) + { + part.PhysActor.Size = + new PhysicsVector(prevScale.X, prevScale.Y, prevScale.Z); + m_scene.PhysicsScene.AddPhysicsActorTaint(part.PhysActor); + } + + HasGroupChanged = true; + ScheduleGroupForTerseUpdate(); + } + } + + #endregion + + #region Position + + /// + /// Move this scene object + /// + /// + public void UpdateGroupPosition(Vector3 pos) + { + if (m_scene.EventManager.TriggerGroupMove(UUID, pos)) + { + if (IsAttachment) + { + m_rootPart.AttachedPos = pos; + } + + AbsolutePosition = pos; + + HasGroupChanged = true; + } + + //we need to do a terse update even if the move wasn't allowed + // so that the position is reset in the client (the object snaps back) + ScheduleGroupForTerseUpdate(); + } + + /// + /// Update the position of a single part of this scene object + /// + /// + /// + public void UpdateSinglePosition(Vector3 pos, uint localID) + { + SceneObjectPart part = GetChildPart(localID); + + if (part != null) + { + if (part.UUID == m_rootPart.UUID) + { + UpdateRootPosition(pos); + } + else + { + part.UpdateOffSet(pos); + } + + HasGroupChanged = true; + } + } + + /// + /// + /// + /// + private void UpdateRootPosition(Vector3 pos) + { + Vector3 newPos = new Vector3(pos.X, pos.Y, pos.Z); + Vector3 oldPos = + new Vector3(AbsolutePosition.X + m_rootPart.OffsetPosition.X, + AbsolutePosition.Y + m_rootPart.OffsetPosition.Y, + AbsolutePosition.Z + m_rootPart.OffsetPosition.Z); + Vector3 diff = oldPos - newPos; + Vector3 axDiff = new Vector3(diff.X, diff.Y, diff.Z); + Quaternion partRotation = m_rootPart.RotationOffset; + axDiff *= Quaternion.Inverse(partRotation); + diff = axDiff; + + lock (m_parts) + { + foreach (SceneObjectPart obPart in m_parts.Values) + { + if (obPart.UUID != m_rootPart.UUID) + { + obPart.OffsetPosition = obPart.OffsetPosition + diff; + } + } + } + + AbsolutePosition = newPos; + + HasGroupChanged = true; + ScheduleGroupForTerseUpdate(); + } + + public void OffsetForNewRegion(Vector3 offset) + { + m_rootPart.GroupPosition = offset; + } + + #endregion + + #region Rotation + + /// + /// + /// + /// + public void UpdateGroupRotation(Quaternion rot) + { + m_rootPart.UpdateRotation(rot); + if (m_rootPart.PhysActor != null) + { + m_rootPart.PhysActor.Orientation = m_rootPart.RotationOffset; + m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor); + } + + HasGroupChanged = true; + ScheduleGroupForTerseUpdate(); + } + + /// + /// + /// + /// + /// + public void UpdateGroupRotation(Vector3 pos, Quaternion rot) + { + m_rootPart.UpdateRotation(rot); + if (m_rootPart.PhysActor != null) + { + m_rootPart.PhysActor.Orientation = m_rootPart.RotationOffset; + m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor); + } + AbsolutePosition = pos; + + HasGroupChanged = true; + ScheduleGroupForTerseUpdate(); + } + + /// + /// + /// + /// + /// + public void UpdateSingleRotation(Quaternion rot, uint localID) + { + SceneObjectPart part = GetChildPart(localID); + if (part != null) + { + if (part.UUID == m_rootPart.UUID) + { + UpdateRootRotation(rot); + } + else + { + part.UpdateRotation(rot); + } + } + } + + /// + /// + /// + /// + private void UpdateRootRotation(Quaternion rot) + { + Quaternion axRot = rot; + Quaternion oldParentRot = m_rootPart.RotationOffset; + + m_rootPart.UpdateRotation(rot); + if (m_rootPart.PhysActor != null) + { + m_rootPart.PhysActor.Orientation = m_rootPart.RotationOffset; + m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor); + } + + lock (m_parts) + { + foreach (SceneObjectPart prim in m_parts.Values) + { + if (prim.UUID != m_rootPart.UUID) + { + Vector3 axPos = prim.OffsetPosition; + axPos *= oldParentRot; + axPos *= Quaternion.Inverse(axRot); + prim.OffsetPosition = axPos; + Quaternion primsRot = prim.RotationOffset; + Quaternion newRot = primsRot * oldParentRot; + newRot *= Quaternion.Inverse(axRot); + prim.RotationOffset = newRot; + prim.ScheduleTerseUpdate(); + } + } + } + + m_rootPart.ScheduleTerseUpdate(); + } + + #endregion + + internal void SetAxisRotation(int axis, int rotate10) + { + bool setX = false; + bool setY = false; + bool setZ = false; + + int xaxis = 2; + int yaxis = 4; + int zaxis = 8; + + if (m_rootPart != null) + { + setX = ((axis & xaxis) != 0) ? true : false; + setY = ((axis & yaxis) != 0) ? true : false; + setZ = ((axis & zaxis) != 0) ? true : false; + + float setval = (rotate10 > 0) ? 1f : 0f; + + if (setX) + m_rootPart.RotationAxis.X = setval; + if (setY) + m_rootPart.RotationAxis.Y = setval; + if (setZ) + m_rootPart.RotationAxis.Z = setval; + + if (setX || setY || setZ) + { + m_rootPart.SetPhysicsAxisRotation(); + } + + } + } + + public int registerTargetWaypoint(Vector3 target, float tolerance) + { + scriptPosTarget waypoint = new scriptPosTarget(); + waypoint.targetPos = target; + waypoint.tolerance = tolerance; + uint handle = m_scene.AllocateLocalId(); + lock (m_targets) + { + m_targets.Add(handle, waypoint); + } + return (int)handle; + } + + public void unregisterTargetWaypoint(int handle) + { + lock (m_targets) + { + if (m_targets.ContainsKey((uint)handle)) + m_targets.Remove((uint)handle); + } + } + + private void checkAtTargets() + { + if (m_scriptListens_atTarget || m_scriptListens_notAtTarget) + { + if (m_targets.Count > 0) + { + bool at_target = false; + //Vector3 targetPos; + //uint targetHandle; + Dictionary atTargets = new Dictionary(); + lock (m_targets) + { + foreach (uint idx in m_targets.Keys) + { + scriptPosTarget target = m_targets[idx]; + if (Util.GetDistanceTo(target.targetPos, m_rootPart.GroupPosition) <= target.tolerance) + { + // trigger at_target + if (m_scriptListens_atTarget) + { + // Reusing att.tolerance to hold the index of the target in the targets dictionary + // to avoid deadlocking the sim. + at_target = true; + scriptPosTarget att = new scriptPosTarget(); + att.targetPos = target.targetPos; + att.tolerance = (float)idx; + atTargets.Add(idx, att); + } + } + } + } + if (atTargets.Count > 0) + { + uint[] localids = new uint[0]; + lock (m_parts) + { + localids = new uint[m_parts.Count]; + int cntr = 0; + foreach (SceneObjectPart part in m_parts.Values) + { + localids[cntr] = part.LocalId; + cntr++; + } + } + for (int ctr = 0; ctr < localids.Length; ctr++) + { + foreach (uint target in atTargets.Keys) + { + scriptPosTarget att = atTargets[target]; + // Reusing att.tolerance to hold the index of the target in the targets dictionary + // to avoid deadlocking the sim. + m_scene.TriggerAtTargetEvent(localids[ctr], (uint)att.tolerance, att.targetPos, m_rootPart.GroupPosition); + + + } + } + return; + } + if (m_scriptListens_notAtTarget && !at_target) + { + //trigger not_at_target + uint[] localids = new uint[0]; + lock (m_parts) + { + localids = new uint[m_parts.Count]; + int cntr = 0; + foreach (SceneObjectPart part in m_parts.Values) + { + localids[cntr] = part.LocalId; + cntr++; + } + } + for (int ctr = 0; ctr < localids.Length; ctr++) + { + m_scene.TriggerNotAtTargetEvent(localids[ctr]); + } + } + } + } + } + + public float GetMass() + { + float retmass = 0f; + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + retmass += part.GetMass(); + } + } + return retmass; + } + + public void CheckSculptAndLoad() + { + lock (m_parts) + { + if (!IsDeleted) + { + if ((RootPart.GetEffectiveObjectFlags() & (uint)PrimFlags.Phantom) == 0) + { + foreach (SceneObjectPart part in m_parts.Values) + { + if (part.Shape.SculptEntry && part.Shape.SculptTexture != UUID.Zero) + { + m_scene.AssetCache.GetAsset(part.Shape.SculptTexture, part.SculptTextureCallback, true); + } + } + } + } + } + } + + /// + /// Set the user group to which this scene object belongs. + /// + /// + /// + public void SetGroup(UUID GroupID, IClientAPI client) + { + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + { + part.SetGroup(GroupID, client); + part.Inventory.ChangeInventoryGroup(GroupID); + } + + HasGroupChanged = true; + } + + ScheduleGroupForFullUpdate(); + } + + public void TriggerScriptChangedEvent(Changed val) + { + foreach (SceneObjectPart part in Children.Values) + { + part.TriggerScriptChangedEvent(val); + } + } + + public override string ToString() + { + return String.Format("{0} {1} ({2})", Name, UUID, AbsolutePosition); + } + + public void SetAttachmentPoint(byte point) + { + lock (m_parts) + { + foreach (SceneObjectPart part in m_parts.Values) + part.SetAttachmentPoint(point); + } + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs new file mode 100644 index 0000000..dfa9318 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -0,0 +1,3826 @@ +/* + * 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 OpenSim 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.Drawing; +using System.Reflection; +using System.Runtime.Serialization; +using System.Security.Permissions; +using System.Xml; +using System.Xml.Serialization; +using log4net; +using OpenMetaverse; +using OpenMetaverse.Packets; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes.Scripting; +using OpenSim.Region.Physics.Manager; + +namespace OpenSim.Region.Framework.Scenes +{ + #region Enumerations + + [Flags] + public enum Changed : uint + { + INVENTORY = 1, + COLOR = 2, + SHAPE = 4, + SCALE = 8, + TEXTURE = 16, + LINK = 32, + ALLOWED_DROP = 64, + OWNER = 128, + REGION_RESTART = 256, + REGION = 512, + TELEPORT = 1024 + } + + // I don't really know where to put this except here. + // Can't access the OpenSim.Region.ScriptEngine.Common.LSL_BaseClass.Changed constants + [Flags] + public enum ExtraParamType + { + Something1 = 1, + Something2 = 2, + Something3 = 4, + Something4 = 8, + Flexible = 16, + Light = 32, + Sculpt = 48, + Something5 = 64, + Something6 = 128 + } + + [Flags] + public enum TextureAnimFlags : byte + { + NONE = 0x00, + ANIM_ON = 0x01, + LOOP = 0x02, + REVERSE = 0x04, + PING_PONG = 0x08, + SMOOTH = 0x10, + ROTATE = 0x20, + SCALE = 0x40 + } + + #endregion Enumerations + + [Serializable] + public class SceneObjectPart : IScriptHost, ISerializable + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // use only one serializer to give the runtime a chance to optimize it (it won't do that if you + // use a new instance every time) + private static XmlSerializer serializer = new XmlSerializer(typeof (SceneObjectPart)); + + #region Fields + + [XmlIgnore] + public bool AllowedDrop = false; + + [XmlIgnore] + public bool DIE_AT_EDGE = false; + + // TODO: This needs to be persisted in next XML version update! + [XmlIgnore] + public int[] PayPrice = {-2,-2,-2,-2,-2}; + [XmlIgnore] + public PhysicsActor PhysActor = null; + + //Xantor 20080528 Sound stuff: + // Note: This isn't persisted in the database right now, as the fields for that aren't just there yet. + // Not a big problem as long as the script that sets it remains in the prim on startup. + // for SL compatibility it should be persisted though (set sound / displaytext / particlesystem, kill script) + [XmlIgnore] + public UUID Sound; + + [XmlIgnore] + public byte SoundFlags; + + [XmlIgnore] + public double SoundGain; + + [XmlIgnore] + public double SoundRadius; + + [XmlIgnore] + public uint TimeStampFull = 0; + + [XmlIgnore] + public uint TimeStampLastActivity = 0; // Will be used for AutoReturn + + [XmlIgnore] + public uint TimeStampTerse = 0; + + [XmlIgnore] + public UUID FromAssetID = UUID.Zero; + + /// + /// The UUID of the user inventory item from which this object was rezzed if this is a root part. + /// If UUID.Zero then either this is not a root part or there is no connection with a user inventory item. + /// + private UUID m_fromUserInventoryItemID = UUID.Zero; + + [XmlIgnore] + public UUID FromUserInventoryItemID + { + get { return m_fromUserInventoryItemID; } + } + + [XmlIgnore] + public bool IsAttachment = false; + + [XmlIgnore] + public scriptEvents AggregateScriptEvents = 0; + + [XmlIgnore] + public UUID AttachedAvatar = UUID.Zero; + + [XmlIgnore] + public Vector3 AttachedPos = Vector3.Zero; + + [XmlIgnore] + public uint AttachmentPoint = (byte)0; + + [XmlIgnore] + public PhysicsVector RotationAxis = new PhysicsVector(1f,1f,1f); + + [XmlIgnore] + public bool VolumeDetectActive = false; // XmlIgnore set to avoid problems with persistance until I come to care for this + // Certainly this must be a persistant setting finally + + /// + /// This part's inventory + /// + [XmlIgnore] + public IEntityInventory Inventory + { + get { return m_inventory; } + } + protected SceneObjectPartInventory m_inventory; + + [XmlIgnore] + public bool Undoing = false; + + [XmlIgnore] + private PrimFlags LocalFlags = 0; + private byte[] m_TextureAnimation; + private byte m_clickAction = 0; + private Color m_color = Color.Black; + private string m_description = String.Empty; + private readonly List m_lastColliders = new List(); + // private PhysicsVector m_lastRotationalVelocity = PhysicsVector.Zero; + private int m_linkNum = 0; + [XmlIgnore] + private int m_scriptAccessPin = 0; + [XmlIgnore] + private readonly Dictionary m_scriptEvents = new Dictionary(); + private string m_sitName = String.Empty; + private Quaternion m_sitTargetOrientation = Quaternion.Identity; + private Vector3 m_sitTargetPosition = Vector3.Zero; + private string m_sitAnimation = "SIT"; + private string m_text = String.Empty; + private string m_touchName = String.Empty; + private readonly UndoStack m_undo = new UndoStack(5); + private UUID _creatorID; + + /// + /// Only used internally to schedule client updates. + /// 0 - no update is scheduled + /// 1 - terse update scheduled + /// 2 - full update scheduled + /// + /// TODO - This should be an enumeration + /// + private byte m_updateFlag; + + protected Vector3 m_acceleration; + protected Vector3 m_angularVelocity; + + //unkown if this will be kept, added as a way of removing the group position from the group class + protected Vector3 m_groupPosition; + protected uint m_localId; + protected Material m_material = (Material)3; // Wood + protected string m_name; + protected Vector3 m_offsetPosition; + + // FIXME, TODO, ERROR: 'ParentGroup' can't be in here, move it out. + protected SceneObjectGroup m_parentGroup; + protected byte[] m_particleSystem = new byte[0]; + protected ulong m_regionHandle; + protected Quaternion m_rotationOffset; + protected PrimitiveBaseShape m_shape = null; + protected UUID m_uuid; + protected Vector3 m_velocity; + + // TODO: Those have to be changed into persistent properties at some later point, + // or sit-camera on vehicles will break on sim-crossing. + private Vector3 m_cameraEyeOffset = new Vector3(0.0f, 0.0f, 0.0f); + private Vector3 m_cameraAtOffset = new Vector3(0.0f, 0.0f, 0.0f); + private bool m_forceMouselook = false; + + // TODO: Collision sound should have default. + private UUID m_collisionSound = UUID.Zero; + private float m_collisionSoundVolume = 0.0f; + + #endregion Fields + + #region Constructors + + /// + /// No arg constructor called by region restore db code + /// + public SceneObjectPart() + { + // It's not necessary to persist this + m_TextureAnimation = new byte[0]; + m_particleSystem = new byte[0]; + Rezzed = DateTime.Now; + + m_inventory = new SceneObjectPartInventory(this); + } + + /// + /// Create a completely new SceneObjectPart (prim). This will need to be added separately to a SceneObjectGroup + /// + /// + /// + /// + /// + /// + public SceneObjectPart( + UUID ownerID, PrimitiveBaseShape shape, Vector3 groupPosition, + Quaternion rotationOffset, Vector3 offsetPosition) + { + m_name = "Primitive"; + + Rezzed = DateTime.Now; + _creationDate = (Int32) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; + _ownerID = ownerID; + _creatorID = _ownerID; + _lastOwnerID = UUID.Zero; + UUID = UUID.Random(); + Shape = shape; + // Todo: Add More Object Parameter from above! + _ownershipCost = 0; + _objectSaleType = (byte) 0; + _salePrice = 0; + _category = (uint) 0; + _lastOwnerID = _creatorID; + // End Todo: /// + GroupPosition = groupPosition; + OffsetPosition = offsetPosition; + RotationOffset = rotationOffset; + Velocity = new Vector3(0, 0, 0); + AngularVelocity = new Vector3(0, 0, 0); + Acceleration = new Vector3(0, 0, 0); + m_TextureAnimation = new byte[0]; + m_particleSystem = new byte[0]; + + // 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 + // the prim into an agent inventory (Linden client reports that the "Object not found for drop" in its log + + _flags = 0; + _flags |= PrimFlags.CreateSelected; + + TrimPermissions(); + //m_undo = new UndoStack(ParentGroup.GetSceneMaxUndo()); + + m_inventory = new SceneObjectPartInventory(this); + } + + protected SceneObjectPart(SerializationInfo info, StreamingContext context) + { + //System.Console.WriteLine("SceneObjectPart Deserialize BGN"); + m_TextureAnimation = new byte[0]; + m_particleSystem = new byte[0]; + if (info == null) + { + throw new ArgumentNullException("info"); + } + + /* + m_queue = (Queue)info.GetValue("m_queue", typeof(Queue)); + m_ids = (List)info.GetValue("m_ids", typeof(List)); + */ + + //System.Console.WriteLine("SceneObjectPart Deserialize END"); + Rezzed = DateTime.Now; + + m_inventory = new SceneObjectPartInventory(this); + } + + #endregion Constructors + + #region XML Schema + + private UUID _lastOwnerID; + private UUID _ownerID; + private UUID _groupID; + private int _ownershipCost; + private byte _objectSaleType; + private int _salePrice; + private uint _category; + private Int32 _creationDate; + private uint _parentID = 0; + private UUID m_sitTargetAvatar = UUID.Zero; + private uint _baseMask = (uint)PermissionMask.All; + private uint _ownerMask = (uint)PermissionMask.All; + private uint _groupMask = (uint)PermissionMask.None; + private uint _everyoneMask = (uint)PermissionMask.None; + private uint _nextOwnerMask = (uint)PermissionMask.All; + private PrimFlags _flags = 0; + private DateTime m_expires; + private DateTime m_rezzed; + + public UUID CreatorID + { + get + { + return _creatorID; + } + set + { + _creatorID = value; + } + } + + /// + /// Exposing this is not particularly good, but it's one of the least evils at the moment to see + /// folder id from prim inventory item data, since it's not (yet) actually stored with the prim. + /// + public UUID FolderID + { + get { return UUID; } + set { } // Don't allow assignment, or legacy prims wil b0rk + } + + /// + /// Access should be via Inventory directly - this property temporarily remains for xml serialization purposes + /// + public uint InventorySerial + { + get { return m_inventory.Serial; } + set { m_inventory.Serial = value; } + } + + /// + /// Access should be via Inventory directly - this property temporarily remains for xml serialization purposes + /// + public TaskInventoryDictionary TaskInventory + { + get { return m_inventory.Items; } + set { m_inventory.Items = value; } + } + + public uint ObjectFlags + { + get { return (uint)_flags; } + set { _flags = (PrimFlags)value; } + } + + public UUID UUID + { + get { return m_uuid; } + set { m_uuid = value; } + } + + public uint LocalId + { + get { return m_localId; } + set { m_localId = value; } + } + + public virtual string Name + { + get { return m_name; } + set + { + m_name = value; + if (PhysActor != null) + { + PhysActor.SOPName = value; + } + } + } + + public byte Material + { + get { return (byte) m_material; } + set { m_material = (Material)value; } + } + + public ulong RegionHandle + { + get { return m_regionHandle; } + set { m_regionHandle = value; } + } + + public int ScriptAccessPin + { + get { return m_scriptAccessPin; } + set { m_scriptAccessPin = (int)value; } + } + + [XmlIgnore] + public Byte[] TextureAnimation + { + get { return m_TextureAnimation; } + set { m_TextureAnimation = value; } + } + + [XmlIgnore] + public Byte[] ParticleSystem + { + get { return m_particleSystem; } + set { m_particleSystem = value; } + } + + [XmlIgnore] + public DateTime Expires + { + get { return m_expires; } + set { m_expires = value; } + } + + [XmlIgnore] + public DateTime Rezzed + { + get { return m_rezzed; } + set { m_rezzed = value; } + } + + /// + /// The position of the entire group that this prim belongs to. + /// + public Vector3 GroupPosition + { + get + { + // If this is a linkset, we don't want the physics engine mucking up our group position here. + if (PhysActor != null && _parentID == 0) + { + m_groupPosition.X = PhysActor.Position.X; + m_groupPosition.Y = PhysActor.Position.Y; + m_groupPosition.Z = PhysActor.Position.Z; + } + + if (IsAttachment) + { + ScenePresence sp = m_parentGroup.Scene.GetScenePresence(AttachedAvatar); + if (sp != null) + { + return sp.AbsolutePosition; + } + } + + return m_groupPosition; + } + set + { + StoreUndoState(); + + m_groupPosition = value; + + if (PhysActor != null) + { + try + { + // Root prim actually goes at Position + if (_parentID == 0) + { + PhysActor.Position = new PhysicsVector(value.X, value.Y, value.Z); + } + else + { + // To move the child prim in respect to the group position and rotation we have to calculate + Vector3 resultingposition = GetWorldPosition(); + PhysActor.Position = new PhysicsVector(resultingposition.X, resultingposition.Y, resultingposition.Z); + Quaternion resultingrot = GetWorldRotation(); + PhysActor.Orientation = resultingrot; + } + + // Tell the physics engines that this prim changed. + m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor); + } + catch (Exception e) + { + Console.WriteLine("[SCENEOBJECTPART]: GROUP POSITION. " + e.Message); + } + } + + // TODO if we decide to do sitting in a more SL compatible way (multiple avatars per prim), this has to be fixed, too + if (m_sitTargetAvatar != UUID.Zero) + { + if (m_parentGroup != null) // TODO can there be a SOP without a SOG? + { + ScenePresence avatar; + if (m_parentGroup.Scene.TryGetAvatar(m_sitTargetAvatar, out avatar)) + { + avatar.ParentPosition = GetWorldPosition(); + } + } + } + } + } + + public Vector3 OffsetPosition + { + get { return m_offsetPosition; } + set + { + StoreUndoState(); + m_offsetPosition = value; + + if (ParentGroup != null && !ParentGroup.IsDeleted) + { + if (_parentID != 0 && PhysActor != null) + { + Vector3 resultingposition = GetWorldPosition(); + PhysActor.Position = new PhysicsVector(resultingposition.X, resultingposition.Y, resultingposition.Z); + Quaternion resultingrot = GetWorldRotation(); + PhysActor.Orientation = resultingrot; + + // Tell the physics engines that this prim changed. + m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor); + } + } + } + } + + public Quaternion RotationOffset + { + get + { + // We don't want the physics engine mucking up the rotations in a linkset + if ((_parentID == 0) && (Shape.PCode != 9 || Shape.State == 0) && (PhysActor != null)) + { + if (PhysActor.Orientation.X != 0 || PhysActor.Orientation.Y != 0 + || PhysActor.Orientation.Z != 0 || PhysActor.Orientation.W != 0) + { + m_rotationOffset = PhysActor.Orientation; + } + } + return m_rotationOffset; + } + set + { + StoreUndoState(); + m_rotationOffset = value; + + if (PhysActor != null) + { + try + { + // Root prim gets value directly + if (_parentID == 0) + { + PhysActor.Orientation = value; + //m_log.Info("[PART]: RO1:" + PhysActor.Orientation.ToString()); + } + else + { + // Child prim we have to calculate it's world rotationwel + Quaternion resultingrotation = GetWorldRotation(); + PhysActor.Orientation = resultingrotation; + //m_log.Info("[PART]: RO2:" + PhysActor.Orientation.ToString()); + } + m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor); + //} + } + catch (Exception ex) + { + Console.WriteLine("[SCENEOBJECTPART]: ROTATIONOFFSET" + ex.Message); + } + } + + } + } + + /// + public Vector3 Velocity + { + get + { + //if (PhysActor.Velocity.X != 0 || PhysActor.Velocity.Y != 0 + //|| PhysActor.Velocity.Z != 0) + //{ + if (PhysActor != null) + { + if (PhysActor.IsPhysical) + { + m_velocity.X = PhysActor.Velocity.X; + m_velocity.Y = PhysActor.Velocity.Y; + m_velocity.Z = PhysActor.Velocity.Z; + } + } + + return m_velocity; + } + + set + { + m_velocity = value; + if (PhysActor != null) + { + if (PhysActor.IsPhysical) + { + PhysActor.Velocity = new PhysicsVector(value.X, value.Y, value.Z); + m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor); + } + } + } + } + + public Vector3 RotationalVelocity + { + get { return AngularVelocity; } + set { AngularVelocity = value; } + } + + /// + public Vector3 AngularVelocity + { + get + { + if ((PhysActor != null) && PhysActor.IsPhysical) + { + m_angularVelocity.FromBytes(PhysActor.RotationalVelocity.GetBytes(), 0); + } + return m_angularVelocity; + } + set { m_angularVelocity = value; } + } + + /// + public Vector3 Acceleration + { + get { return m_acceleration; } + set { m_acceleration = value; } + } + + public string Description + { + get { return m_description; } + set + { + m_description = value; + if (PhysActor != null) + { + PhysActor.SOPDescription = value; + } + } + } + + public Color Color + { + get { return m_color; } + set + { + m_color = value; + TriggerScriptChangedEvent(Changed.COLOR); + + /* ScheduleFullUpdate() need not be called b/c after + * setting the color, the text will be set, so then + * ScheduleFullUpdate() will be called. */ + //ScheduleFullUpdate(); + } + } + + public string Text + { + get + { + string returnstr = m_text; + if (returnstr.Length > 255) + { + returnstr = returnstr.Substring(0, 254); + } + return returnstr; + } + set + { + m_text = value; + } + } + + + public string SitName + { + get { return m_sitName; } + set { m_sitName = value; } + } + + public string TouchName + { + get { return m_touchName; } + set { m_touchName = value; } + } + + public int LinkNum + { + get { return m_linkNum; } + set { m_linkNum = value; } + } + + public byte ClickAction + { + get { return m_clickAction; } + set + { + m_clickAction = value; + } + } + + public PrimitiveBaseShape Shape + { + get { return m_shape; } + set + { + bool shape_changed = false; + // TODO: this should really be restricted to the right + // set of attributes on shape change. For instance, + // changing the lighting on a shape shouldn't cause + // this. + if (m_shape != null) + shape_changed = true; + + m_shape = value; + + if (shape_changed) + TriggerScriptChangedEvent(Changed.SHAPE); + } + } + + public Vector3 Scale + { + get { return m_shape.Scale; } + set + { + StoreUndoState(); +if (m_shape != null) { + m_shape.Scale = value; + + if (PhysActor != null && m_parentGroup != null) + { + if (m_parentGroup.Scene != null) + { + if (m_parentGroup.Scene.PhysicsScene != null) + { + PhysActor.Size = new PhysicsVector(m_shape.Scale.X, m_shape.Scale.Y, m_shape.Scale.Z); + m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor); + } + } + } +} + TriggerScriptChangedEvent(Changed.SCALE); + } + } + public byte UpdateFlag + { + get { return m_updateFlag; } + set { m_updateFlag = value; } + } + + #endregion + +//--------------- +#region Public Properties with only Get + + public Vector3 AbsolutePosition + { + get { + if (IsAttachment) + return GroupPosition; + + return m_offsetPosition + m_groupPosition; } + } + + public UUID ObjectCreator + { + get { return _creatorID; } + } + + public UUID ObjectOwner + { + get { return _ownerID; } + } + + public SceneObjectGroup ParentGroup + { + get { return m_parentGroup; } + } + + public scriptEvents ScriptEvents + { + get { return AggregateScriptEvents; } + } + + + public Quaternion SitTargetOrientation + { + get { return m_sitTargetOrientation; } + set { m_sitTargetOrientation = value; } + } + + + public Vector3 SitTargetPosition + { + get { return m_sitTargetPosition; } + set { m_sitTargetPosition = value; } + } + + // This sort of sucks, but I'm adding these in to make some of + // the mappings more consistant. + public Vector3 SitTargetPositionLL + { + get { return new Vector3(m_sitTargetPosition.X, m_sitTargetPosition.Y,m_sitTargetPosition.Z); } + set { m_sitTargetPosition = value; } + } + + public Quaternion SitTargetOrientationLL + { + get + { + return new Quaternion( + m_sitTargetOrientation.X, + m_sitTargetOrientation.Y, + m_sitTargetOrientation.Z, + m_sitTargetOrientation.W + ); + } + + set { m_sitTargetOrientation = new Quaternion(value.X, value.Y, value.Z, value.W); } + } + + public bool Stopped + { + get { + double threshold = 0.02; + return (Math.Abs(Velocity.X) < threshold && + Math.Abs(Velocity.Y) < threshold && + Math.Abs(Velocity.Z) < threshold && + Math.Abs(AngularVelocity.X) < threshold && + Math.Abs(AngularVelocity.Y) < threshold && + Math.Abs(AngularVelocity.Z) < threshold); + } + } + + public uint ParentID + { + get { return _parentID; } + set { _parentID = value; } + } + + public int CreationDate + { + get { return _creationDate; } + set { _creationDate = value; } + } + + public uint Category + { + get { return _category; } + set { _category = value; } + } + + public int SalePrice + { + get { return _salePrice; } + set { _salePrice = value; } + } + + public byte ObjectSaleType + { + get { return _objectSaleType; } + set { _objectSaleType = value; } + } + + public int OwnershipCost + { + get { return _ownershipCost; } + set { _ownershipCost = value; } + } + + public UUID GroupID + { + get { return _groupID; } + set { _groupID = value; } + } + + public UUID OwnerID + { + get { return _ownerID; } + set { _ownerID = value; } + } + + public UUID LastOwnerID + { + get { return _lastOwnerID; } + set { _lastOwnerID = value; } + } + + public uint BaseMask + { + get { return _baseMask; } + set { _baseMask = value; } + } + + public uint OwnerMask + { + get { return _ownerMask; } + set { _ownerMask = value; } + } + + public uint GroupMask + { + get { return _groupMask; } + set { _groupMask = value; } + } + + public uint EveryoneMask + { + get { return _everyoneMask; } + set { _everyoneMask = value; } + } + + public uint NextOwnerMask + { + get { return _nextOwnerMask; } + set { _nextOwnerMask = value; } + } + + public PrimFlags Flags + { + get { return _flags; } + set { _flags = value; } + } + + [XmlIgnore] + public UUID SitTargetAvatar + { + get { return m_sitTargetAvatar; } + set { m_sitTargetAvatar = value; } + } + + [XmlIgnore] + public virtual UUID RegionID + { + get + { + if (ParentGroup != null && ParentGroup.Scene != null) + return ParentGroup.Scene.RegionInfo.RegionID; + else + return UUID.Zero; + } + set {} // read only + } + + private UUID _parentUUID = UUID.Zero; + [XmlIgnore] + public UUID ParentUUID + { + get + { + if (ParentGroup != null) + { + _parentUUID = ParentGroup.UUID; + } + return _parentUUID; + } + set { _parentUUID = value; } + } + + [XmlIgnore] + public string SitAnimation + { + get { return m_sitAnimation; } + set { m_sitAnimation = value; } + } + + public UUID CollisionSound + { + get { return m_collisionSound; } + set + { + m_collisionSound = value; + aggregateScriptEvents(); + } + } + + public float CollisionSoundVolume + { + get { return m_collisionSoundVolume; } + set { m_collisionSoundVolume = value; } + } + + #endregion Public Properties with only Get + + + + #region Private Methods + + private uint ApplyMask(uint val, bool set, uint mask) + { + if (set) + { + return val |= mask; + } + else + { + return val &= ~mask; + } + } + + /// + /// Clear all pending updates of parts to clients + /// + private void ClearUpdateSchedule() + { + m_updateFlag = 0; + } + + private void SendObjectPropertiesToClient(UUID AgentID) + { + List avatars = m_parentGroup.Scene.GetScenePresences(); + for (int i = 0; i < avatars.Count; i++) + { + // Ugly reference :( + if (avatars[i].UUID == AgentID) + { + m_parentGroup.GetProperties(avatars[i].ControllingClient); + } + } + } + + private void handleTimerAccounting(uint localID, double interval) + { + if (localID == LocalId) + { + + float sec = (float)interval; + if (m_parentGroup != null) + { + if (sec == 0) + { + if (m_parentGroup.scriptScore + 0.001f >= float.MaxValue - 0.001) + m_parentGroup.scriptScore = 0; + + m_parentGroup.scriptScore += 0.001f; + return; + } + + if (m_parentGroup.scriptScore + (0.001f / sec) >= float.MaxValue - (0.001f / sec)) + m_parentGroup.scriptScore = 0; + m_parentGroup.scriptScore += (0.001f / sec); + } + + } + } + + #endregion Private Methods + + #region Public Methods + + public void ResetExpire() + { + Expires = DateTime.Now + new TimeSpan(600000000); + } + + public void AddFlag(PrimFlags flag) + { + // PrimFlags prevflag = Flags; + if ((ObjectFlags & (uint) flag) == 0) + { + //Console.WriteLine("Adding flag: " + ((PrimFlags) flag).ToString()); + _flags |= flag; + + if (flag == PrimFlags.TemporaryOnRez) + ResetExpire(); + } + // System.Console.WriteLine("Aprev: " + prevflag.ToString() + " curr: " + Flags.ToString()); + } + + /// + /// Tell all scene presences that they should send updates for this part to their clients + /// + public void AddFullUpdateToAllAvatars() + { + List avatars = m_parentGroup.Scene.GetScenePresences(); + for (int i = 0; i < avatars.Count; i++) + { + avatars[i].QueuePartForUpdate(this); + } + } + + public void AddFullUpdateToAvatar(ScenePresence presence) + { + presence.QueuePartForUpdate(this); + } + + public void AddNewParticleSystem(Primitive.ParticleSystem pSystem) + { + m_particleSystem = pSystem.GetBytes(); + } + + public void RemoveParticleSystem() + { + m_particleSystem = new byte[0]; + } + + /// Terse updates + public void AddTerseUpdateToAllAvatars() + { + List avatars = m_parentGroup.Scene.GetScenePresences(); + for (int i = 0; i < avatars.Count; i++) + { + avatars[i].QueuePartForUpdate(this); + } + } + + public void AddTerseUpdateToAvatar(ScenePresence presence) + { + presence.QueuePartForUpdate(this); + } + + public void AddTextureAnimation(Primitive.TextureAnimation pTexAnim) + { + byte[] data = new byte[16]; + int pos = 0; + + // The flags don't like conversion from uint to byte, so we have to do + // it the crappy way. See the above function :( + + data[pos] = ConvertScriptUintToByte((uint)pTexAnim.Flags); pos++; + data[pos] = (byte)pTexAnim.Face; pos++; + data[pos] = (byte)pTexAnim.SizeX; pos++; + data[pos] = (byte)pTexAnim.SizeY; pos++; + + Utils.FloatToBytes(pTexAnim.Start).CopyTo(data, pos); + Utils.FloatToBytes(pTexAnim.Length).CopyTo(data, pos + 4); + Utils.FloatToBytes(pTexAnim.Rate).CopyTo(data, pos + 8); + + m_TextureAnimation = data; + } + + public void AdjustSoundGain(double volume) + { + if (volume > 1) + volume = 1; + if (volume < 0) + volume = 0; + + List avatarts = m_parentGroup.Scene.GetAvatars(); + foreach (ScenePresence p in avatarts) + { + p.ControllingClient.SendAttachedSoundGainChange(UUID, (float)volume); + } + } + + /// + /// hook to the physics scene to apply impulse + /// This is sent up to the group, which then finds the root prim + /// and applies the force on the root prim of the group + /// + /// Vector force + /// true for the local frame, false for the global frame + public void ApplyImpulse(Vector3 impulsei, bool localGlobalTF) + { + PhysicsVector impulse = new PhysicsVector(impulsei.X, impulsei.Y, impulsei.Z); + + if (localGlobalTF) + { + Quaternion grot = GetWorldRotation(); + Quaternion AXgrot = grot; + Vector3 AXimpulsei = impulsei; + Vector3 newimpulse = AXimpulsei * AXgrot; + impulse = new PhysicsVector(newimpulse.X, newimpulse.Y, newimpulse.Z); + } + + if (m_parentGroup != null) + { + m_parentGroup.applyImpulse(impulse); + } + } + + + /// + /// hook to the physics scene to apply angular impulse + /// This is sent up to the group, which then finds the root prim + /// and applies the force on the root prim of the group + /// + /// Vector force + /// true for the local frame, false for the global frame + public void ApplyAngularImpulse(Vector3 impulsei, bool localGlobalTF) + { + PhysicsVector impulse = new PhysicsVector(impulsei.X, impulsei.Y, impulsei.Z); + + if (localGlobalTF) + { + Quaternion grot = GetWorldRotation(); + Quaternion AXgrot = grot; + Vector3 AXimpulsei = impulsei; + Vector3 newimpulse = AXimpulsei * AXgrot; + impulse = new PhysicsVector(newimpulse.X, newimpulse.Y, newimpulse.Z); + } + + if (m_parentGroup != null) + { + m_parentGroup.applyAngularImpulse(impulse); + } + } + + /// + /// hook to the physics scene to apply angular impulse + /// This is sent up to the group, which then finds the root prim + /// and applies the force on the root prim of the group + /// + /// Vector force + /// true for the local frame, false for the global frame + public void SetAngularImpulse(Vector3 impulsei, bool localGlobalTF) + { + PhysicsVector impulse = new PhysicsVector(impulsei.X, impulsei.Y, impulsei.Z); + + if (localGlobalTF) + { + Quaternion grot = GetWorldRotation(); + Quaternion AXgrot = grot; + Vector3 AXimpulsei = impulsei; + Vector3 newimpulse = AXimpulsei * AXgrot; + impulse = new PhysicsVector(newimpulse.X, newimpulse.Y, newimpulse.Z); + } + + if (m_parentGroup != null) + { + m_parentGroup.setAngularImpulse(impulse); + } + } + + public Vector3 GetTorque() + { + if (m_parentGroup != null) + { + m_parentGroup.GetTorque(); + } + return Vector3.Zero; + } + + /// + /// Apply physics to this part. + /// + /// + /// + public void ApplyPhysics(uint rootObjectFlags, bool VolumeDetectActive, bool m_physicalPrim) + { + bool isPhysical = (((rootObjectFlags & (uint) PrimFlags.Physics) != 0) && m_physicalPrim); + bool isPhantom = ((rootObjectFlags & (uint) PrimFlags.Phantom) != 0); + + if (IsJoint()) + { + DoPhysicsPropertyUpdate(isPhysical, true); + } + else + { + // Special case for VolumeDetection: If VolumeDetection is set, the phantom flag is locally ignored + if (VolumeDetectActive) + isPhantom = false; + + // Added clarification.. since A rigid body is an object that you can kick around, etc. + bool RigidBody = isPhysical && !isPhantom; + + // The only time the physics scene shouldn't know about the prim is if it's phantom or an attachment, which is phantom by definition + if (!isPhantom && !IsAttachment) + { + PhysActor = m_parentGroup.Scene.PhysicsScene.AddPrimShape( + Name, + Shape, + new PhysicsVector(AbsolutePosition.X, AbsolutePosition.Y, AbsolutePosition.Z), + new PhysicsVector(Scale.X, Scale.Y, Scale.Z), + RotationOffset, + RigidBody); + + // Basic Physics returns null.. joy joy joy. + if (PhysActor != null) + { + PhysActor.SOPName = this.Name; // save object name and desc into the PhysActor so ODE internals know the joint/body info + PhysActor.SOPDescription = this.Description; + PhysActor.LocalID = LocalId; + DoPhysicsPropertyUpdate(RigidBody, true); + PhysActor.SetVolumeDetect(VolumeDetectActive ? 1 : 0); + } + } + } + } + + public void ClearUndoState() + { + lock (m_undo) + { + m_undo.Clear(); + } + StoreUndoState(); + } + + public byte ConvertScriptUintToByte(uint indata) + { + byte outdata = (byte)TextureAnimFlags.NONE; + if ((indata & 1) != 0) outdata |= (byte)TextureAnimFlags.ANIM_ON; + if ((indata & 2) != 0) outdata |= (byte)TextureAnimFlags.LOOP; + if ((indata & 4) != 0) outdata |= (byte)TextureAnimFlags.REVERSE; + if ((indata & 8) != 0) outdata |= (byte)TextureAnimFlags.PING_PONG; + if ((indata & 16) != 0) outdata |= (byte)TextureAnimFlags.SMOOTH; + if ((indata & 32) != 0) outdata |= (byte)TextureAnimFlags.ROTATE; + if ((indata & 64) != 0) outdata |= (byte)TextureAnimFlags.SCALE; + return outdata; + } + + /// + /// Duplicates this part. + /// + /// + public SceneObjectPart Copy(uint localID, UUID AgentID, UUID GroupID, int linkNum, bool userExposed) + { + SceneObjectPart dupe = (SceneObjectPart) MemberwiseClone(); + dupe.m_shape = m_shape.Copy(); + dupe.m_regionHandle = m_regionHandle; + if (userExposed) + dupe.UUID = UUID.Random(); + + //memberwiseclone means it also clones the physics actor reference + // This will make physical prim 'bounce' if not set to null. + if (!userExposed) + dupe.PhysActor = null; + + dupe._ownerID = AgentID; + dupe._groupID = GroupID; + dupe.GroupPosition = GroupPosition; + dupe.OffsetPosition = OffsetPosition; + dupe.RotationOffset = RotationOffset; + dupe.Velocity = new Vector3(0, 0, 0); + dupe.Acceleration = new Vector3(0, 0, 0); + dupe.AngularVelocity = new Vector3(0, 0, 0); + dupe.ObjectFlags = ObjectFlags; + + dupe._ownershipCost = _ownershipCost; + dupe._objectSaleType = _objectSaleType; + dupe._salePrice = _salePrice; + dupe._category = _category; + dupe.m_rezzed = m_rezzed; + + dupe.m_inventory = new SceneObjectPartInventory(dupe); + dupe.m_inventory.Items = (TaskInventoryDictionary)m_inventory.Items.Clone(); + + if (userExposed) + { + dupe.ResetIDs(linkNum); + dupe.m_inventory.HasInventoryChanged = true; + } + else + { + dupe.m_inventory.HasInventoryChanged = m_inventory.HasInventoryChanged; + } + + // Move afterwards ResetIDs as it clears the localID + dupe.LocalId = localID; + // This may be wrong... it might have to be applied in SceneObjectGroup to the object that's being duplicated. + dupe._lastOwnerID = ObjectOwner; + + byte[] extraP = new byte[Shape.ExtraParams.Length]; + Array.Copy(Shape.ExtraParams, extraP, extraP.Length); + dupe.Shape.ExtraParams = extraP; + + if (userExposed) + { + if (dupe.m_shape.SculptEntry && dupe.m_shape.SculptTexture != UUID.Zero) + { + m_parentGroup.Scene.AssetCache.GetAsset(dupe.m_shape.SculptTexture, dupe.SculptTextureCallback, true); + } + bool UsePhysics = ((dupe.ObjectFlags & (uint)PrimFlags.Physics) != 0); + dupe.DoPhysicsPropertyUpdate(UsePhysics, true); + } + + return dupe; + } + + public static SceneObjectPart Create() + { + SceneObjectPart part = new SceneObjectPart(); + part.UUID = UUID.Random(); + + PrimitiveBaseShape shape = PrimitiveBaseShape.Create(); + part.Shape = shape; + + part.Name = "Primitive"; + part._ownerID = UUID.Random(); + + return part; + } + + public void DoPhysicsPropertyUpdate(bool UsePhysics, bool isNew) + { + if (IsJoint()) + { + if (UsePhysics) + { + // by turning a joint proxy object physical, we cause creation of a joint in the ODE scene. + // note that, as a special case, joints have no bodies or geoms in the physics scene, even though they are physical. + + PhysicsJointType jointType; + if (IsHingeJoint()) + { + jointType = PhysicsJointType.Hinge; + } + else if (IsBallJoint()) + { + jointType = PhysicsJointType.Ball; + } + else + { + jointType = PhysicsJointType.Ball; + } + + List bodyNames = new List(); + string RawParams = Description; + string[] jointParams = RawParams.Split(" ".ToCharArray(), System.StringSplitOptions.RemoveEmptyEntries); + string trackedBodyName = null; + if (jointParams.Length >= 2) + { + for (int iBodyName = 0; iBodyName < 2; iBodyName++) + { + string bodyName = jointParams[iBodyName]; + bodyNames.Add(bodyName); + if (bodyName != "NULL") + { + if (trackedBodyName == null) + { + trackedBodyName = bodyName; + } + } + } + } + + SceneObjectPart trackedBody = m_parentGroup.Scene.GetSceneObjectPart(trackedBodyName); // FIXME: causes a sequential lookup + Quaternion localRotation = Quaternion.Identity; + if (trackedBody != null) + { + localRotation = Quaternion.Inverse(trackedBody.RotationOffset) * this.RotationOffset; + } + else + { + // error, output it below + } + + PhysicsJoint joint; + + joint = m_parentGroup.Scene.PhysicsScene.RequestJointCreation(Name, jointType, + new PhysicsVector(AbsolutePosition.X, AbsolutePosition.Y, AbsolutePosition.Z), + this.RotationOffset, + Description, + bodyNames, + trackedBodyName, + localRotation); + + if (trackedBody == null) + { + ParentGroup.Scene.jointErrorMessage(joint, "warning: tracked body name not found! joint location will not be updated properly. joint: " + Name); + } + + } + else + { + if (isNew) + { + // if the joint proxy is new, and it is not physical, do nothing. There is no joint in ODE to + // delete, and if we try to delete it, due to asynchronous processing, the deletion request + // will get processed later at an indeterminate time, which could cancel a later-arriving + // joint creation request. + } + else + { + // here we turn off the joint object, so remove the joint from the physics scene + m_parentGroup.Scene.PhysicsScene.RequestJointDeletion(Name); // FIXME: what if the name changed? + + // make sure client isn't interpolating the joint proxy object + Velocity = new Vector3(0, 0, 0); + RotationalVelocity = new Vector3(0, 0, 0); + Acceleration = new Vector3(0, 0, 0); + } + } + } + else + { + if (PhysActor != null) + { + if (UsePhysics != PhysActor.IsPhysical || isNew) + { + if (PhysActor.IsPhysical) // implies UsePhysics==false for this block + { + if (!isNew) + ParentGroup.Scene.RemovePhysicalPrim(1); + + PhysActor.OnRequestTerseUpdate -= PhysicsRequestingTerseUpdate; + PhysActor.OnOutOfBounds -= PhysicsOutOfBounds; + PhysActor.delink(); + + if (ParentGroup.Scene.PhysicsScene.SupportsNINJAJoints && (!isNew)) + { + // destroy all joints connected to this now deactivated body + m_parentGroup.Scene.PhysicsScene.RemoveAllJointsConnectedToActorThreadLocked(PhysActor); + } + + // stop client-side interpolation of all joint proxy objects that have just been deleted + // this is done because RemoveAllJointsConnectedToActor invokes the OnJointDeactivated callback, + // which stops client-side interpolation of deactivated joint proxy objects. + } + + if (!UsePhysics && !isNew) + { + // reset velocity to 0 on physics switch-off. Without that, the client thinks the + // prim still has velocity and continues to interpolate its position along the old + // velocity-vector. + Velocity = new Vector3(0, 0, 0); + Acceleration = new Vector3(0, 0, 0); + AngularVelocity = new Vector3(0, 0, 0); + //RotationalVelocity = new Vector3(0, 0, 0); + } + + PhysActor.IsPhysical = UsePhysics; + + + // If we're not what we're supposed to be in the physics scene, recreate ourselves. + //m_parentGroup.Scene.PhysicsScene.RemovePrim(PhysActor); + /// that's not wholesome. Had to make Scene public + //PhysActor = null; + + if ((ObjectFlags & (uint)PrimFlags.Phantom) == 0) + { + if (UsePhysics) + { + ParentGroup.Scene.AddPhysicalPrim(1); + + PhysActor.OnRequestTerseUpdate += PhysicsRequestingTerseUpdate; + PhysActor.OnOutOfBounds += PhysicsOutOfBounds; + if (_parentID != 0 && _parentID != LocalId) + { + if (ParentGroup.RootPart.PhysActor != null) + { + PhysActor.link(ParentGroup.RootPart.PhysActor); + } + } + } + } + } + m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor); + } + } + } + + /// + /// Restore this part from the serialized xml representation. + /// + /// + /// + public static SceneObjectPart FromXml(XmlReader xmlReader) + { + return FromXml(UUID.Zero, xmlReader); + } + + /// + /// Restore this part from the serialized xml representation. + /// + /// The inventory id from which this part came, if applicable + /// + /// + public static SceneObjectPart FromXml(UUID fromUserInventoryItemId, XmlReader xmlReader) + { + SceneObjectPart part = (SceneObjectPart)serializer.Deserialize(xmlReader); + part.m_fromUserInventoryItemID = fromUserInventoryItemId; + + // for tempOnRez objects, we have to fix the Expire date. + if ((part.Flags & PrimFlags.TemporaryOnRez) != 0) part.ResetExpire(); + + return part; + } + + public UUID GetAvatarOnSitTarget() + { + return m_sitTargetAvatar; + } + + public bool GetDieAtEdge() + { + if (m_parentGroup == null) + return false; + if (m_parentGroup.IsDeleted) + return false; + + return m_parentGroup.RootPart.DIE_AT_EDGE; + } + + public double GetDistanceTo(Vector3 a, Vector3 b) + { + float dx = a.X - b.X; + float dy = a.Y - b.Y; + float dz = a.Z - b.Z; + return Math.Sqrt(dx * dx + dy * dy + dz * dz); + } + + public uint GetEffectiveObjectFlags() + { + PrimFlags f = _flags; + if (m_parentGroup == null || m_parentGroup.RootPart == this) + f &= ~(PrimFlags.Touch | PrimFlags.Money); + + return (uint)_flags | (uint)LocalFlags; + } + + public Vector3 GetGeometricCenter() + { + if (PhysActor != null) + { + return new Vector3(PhysActor.CenterOfMass.X, PhysActor.CenterOfMass.Y, PhysActor.CenterOfMass.Z); + } + else + { + return new Vector3(0, 0, 0); + } + } + + public float GetMass() + { + if (PhysActor != null) + { + return PhysActor.Mass; + } + else + { + return 0; + } + } + + public PhysicsVector GetForce() + { + if (PhysActor != null) + return PhysActor.Force; + else + return new PhysicsVector(); + } + + [SecurityPermission(SecurityAction.LinkDemand, + Flags = SecurityPermissionFlag.SerializationFormatter)] + public virtual void GetObjectData( + SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException("info"); + } + + info.AddValue("m_inventoryFileName", Inventory.GetInventoryFileName()); + info.AddValue("m_folderID", UUID); + info.AddValue("PhysActor", PhysActor); + + Dictionary TaskInventory_work = new Dictionary(); + + foreach (UUID id in TaskInventory.Keys) + { + TaskInventory_work.Add(id.Guid, TaskInventory[id]); + } + + info.AddValue("TaskInventory", TaskInventory_work); + + info.AddValue("LastOwnerID", _lastOwnerID.Guid); + info.AddValue("OwnerID", _ownerID.Guid); + info.AddValue("GroupID", _groupID.Guid); + + info.AddValue("OwnershipCost", _ownershipCost); + info.AddValue("ObjectSaleType", _objectSaleType); + info.AddValue("SalePrice", _salePrice); + info.AddValue("Category", _category); + + info.AddValue("CreationDate", _creationDate); + info.AddValue("ParentID", _parentID); + + info.AddValue("OwnerMask", _ownerMask); + info.AddValue("NextOwnerMask", _nextOwnerMask); + info.AddValue("GroupMask", _groupMask); + info.AddValue("EveryoneMask", _everyoneMask); + info.AddValue("BaseMask", _baseMask); + + info.AddValue("m_particleSystem", m_particleSystem); + + info.AddValue("TimeStampFull", TimeStampFull); + info.AddValue("TimeStampTerse", TimeStampTerse); + info.AddValue("TimeStampLastActivity", TimeStampLastActivity); + + info.AddValue("m_updateFlag", m_updateFlag); + info.AddValue("CreatorID", _creatorID.Guid); + + info.AddValue("m_inventorySerial", m_inventory.Serial); + info.AddValue("m_uuid", m_uuid.Guid); + info.AddValue("m_localID", m_localId); + info.AddValue("m_name", m_name); + info.AddValue("m_flags", _flags); + info.AddValue("m_material", m_material); + info.AddValue("m_regionHandle", m_regionHandle); + + info.AddValue("m_groupPosition.X", m_groupPosition.X); + info.AddValue("m_groupPosition.Y", m_groupPosition.Y); + info.AddValue("m_groupPosition.Z", m_groupPosition.Z); + + info.AddValue("m_offsetPosition.X", m_offsetPosition.X); + info.AddValue("m_offsetPosition.Y", m_offsetPosition.Y); + info.AddValue("m_offsetPosition.Z", m_offsetPosition.Z); + + info.AddValue("m_rotationOffset.W", m_rotationOffset.W); + info.AddValue("m_rotationOffset.X", m_rotationOffset.X); + info.AddValue("m_rotationOffset.Y", m_rotationOffset.Y); + info.AddValue("m_rotationOffset.Z", m_rotationOffset.Z); + + info.AddValue("m_velocity.X", m_velocity.X); + info.AddValue("m_velocity.Y", m_velocity.Y); + info.AddValue("m_velocity.Z", m_velocity.Z); + + info.AddValue("m_rotationalvelocity.X", RotationalVelocity.X); + info.AddValue("m_rotationalvelocity.Y", RotationalVelocity.Y); + info.AddValue("m_rotationalvelocity.Z", RotationalVelocity.Z); + + info.AddValue("m_angularVelocity.X", m_angularVelocity.X); + info.AddValue("m_angularVelocity.Y", m_angularVelocity.Y); + info.AddValue("m_angularVelocity.Z", m_angularVelocity.Z); + + info.AddValue("m_acceleration.X", m_acceleration.X); + info.AddValue("m_acceleration.Y", m_acceleration.Y); + info.AddValue("m_acceleration.Z", m_acceleration.Z); + + info.AddValue("m_description", m_description); + info.AddValue("m_color", m_color); + info.AddValue("m_text", m_text); + info.AddValue("m_sitName", m_sitName); + info.AddValue("m_touchName", m_touchName); + info.AddValue("m_clickAction", m_clickAction); + info.AddValue("m_shape", m_shape); + info.AddValue("m_parentGroup", m_parentGroup); + info.AddValue("PayPrice", PayPrice); + } + + public void GetProperties(IClientAPI client) + { + client.SendObjectPropertiesReply( + m_fromUserInventoryItemID, (ulong)_creationDate, _creatorID, UUID.Zero, UUID.Zero, + _groupID, (short)InventorySerial, _lastOwnerID, UUID, _ownerID, + ParentGroup.RootPart.TouchName, new byte[0], ParentGroup.RootPart.SitName, Name, Description, + ParentGroup.RootPart._ownerMask, ParentGroup.RootPart._nextOwnerMask, ParentGroup.RootPart._groupMask, ParentGroup.RootPart._everyoneMask, + ParentGroup.RootPart._baseMask, + ParentGroup.RootPart.ObjectSaleType, + ParentGroup.RootPart.SalePrice); + } + + public UUID GetRootPartUUID() + { + if (m_parentGroup != null) + { + return m_parentGroup.UUID; + } + return UUID.Zero; + } + + /// + /// Method for a prim to get it's world position from the group. + /// Remember, the Group Position simply gives the position of the group itself + /// + /// A Linked Child Prim objects position in world + public Vector3 GetWorldPosition() + { + Quaternion parentRot = ParentGroup.RootPart.RotationOffset; + + Vector3 axPos = OffsetPosition; + + axPos *= parentRot; + Vector3 translationOffsetPosition = axPos; + return GroupPosition + translationOffsetPosition; + } + + /// + /// Gets the rotation of this prim offset by the group rotation + /// + /// + public Quaternion GetWorldRotation() + { + Quaternion newRot; + + if (this.LinkNum == 0) + { + newRot = RotationOffset; + } + else + { + Quaternion parentRot = ParentGroup.RootPart.RotationOffset; + Quaternion oldRot = RotationOffset; + newRot = parentRot * oldRot; + } + + return newRot; + } + + public void MoveToTarget(Vector3 target, float tau) + { + if (tau > 0) + { + m_parentGroup.moveToTarget(target, tau); + } + else + { + StopMoveToTarget(); + } + } + + public virtual void OnGrab(Vector3 offsetPos, IClientAPI remoteClient) + { + } + + public void PhysicsCollision(EventArgs e) + { + // single threaded here + if (e == null) + { + return; + } + + CollisionEventUpdate a = (CollisionEventUpdate)e; + Dictionary collissionswith = a.m_objCollisionList; + List thisHitColliders = new List(); + List endedColliders = new List(); + List startedColliders = new List(); + + // calculate things that started colliding this time + // and build up list of colliders this time + foreach (uint localid in collissionswith.Keys) + { + if (localid != 0) + { + thisHitColliders.Add(localid); + if (!m_lastColliders.Contains(localid)) + { + startedColliders.Add(localid); + } + + //m_log.Debug("[OBJECT]: Collided with:" + localid.ToString() + " at depth of: " + collissionswith[localid].ToString()); + } + } + + // calculate things that ended colliding + foreach (uint localID in m_lastColliders) + { + if (!thisHitColliders.Contains(localID)) + { + endedColliders.Add(localID); + } + } + + //add the items that started colliding this time to the last colliders list. + foreach (uint localID in startedColliders) + { + m_lastColliders.Add(localID); + } + // remove things that ended colliding from the last colliders list + foreach (uint localID in endedColliders) + { + m_lastColliders.Remove(localID); + } + if (m_parentGroup == null) + return; + if (m_parentGroup.IsDeleted) + return; + + // play the sound. + if (startedColliders.Count > 0 && CollisionSound != UUID.Zero && CollisionSoundVolume > 0.0f) + { + SendSound(CollisionSound.ToString(), CollisionSoundVolume, true, (byte)0); + } + + if ((m_parentGroup.RootPart.ScriptEvents & scriptEvents.collision_start) != 0) + { + // do event notification + if (startedColliders.Count > 0) + { + ColliderArgs StartCollidingMessage = new ColliderArgs(); + List colliding = new List(); + foreach (uint localId in startedColliders) + { + // always running this check because if the user deletes the object it would return a null reference. + if (m_parentGroup == null) + return; + if (m_parentGroup.Scene == null) + return; + SceneObjectPart obj = m_parentGroup.Scene.GetSceneObjectPart(localId); + if (obj != null) + { + DetectedObject detobj = new DetectedObject(); + detobj.keyUUID = obj.UUID; + detobj.nameStr = obj.Name; + detobj.ownerUUID = obj._ownerID; + detobj.posVector = obj.AbsolutePosition; + detobj.rotQuat = obj.GetWorldRotation(); + detobj.velVector = obj.Velocity; + detobj.colliderType = 0; + detobj.groupUUID = obj._groupID; + colliding.Add(detobj); + } + else + { + List avlist = m_parentGroup.Scene.GetScenePresences(); + if (avlist != null) + { + foreach (ScenePresence av in avlist) + { + if (av.LocalId == localId) + { + DetectedObject detobj = new DetectedObject(); + detobj.keyUUID = av.UUID; + detobj.nameStr = av.ControllingClient.Name; + detobj.ownerUUID = av.UUID; + detobj.posVector = av.AbsolutePosition; + detobj.rotQuat = av.Rotation; + detobj.velVector = av.Velocity; + detobj.colliderType = 0; + detobj.groupUUID = av.ControllingClient.ActiveGroupId; + colliding.Add(detobj); + } + } + } + } + } + if (colliding.Count > 0) + { + StartCollidingMessage.Colliders = colliding; + // always running this check because if the user deletes the object it would return a null reference. + if (m_parentGroup == null) + return; + if (m_parentGroup.Scene == null) + return; + m_parentGroup.Scene.EventManager.TriggerScriptCollidingStart(LocalId, StartCollidingMessage); + } + } + } + if ((m_parentGroup.RootPart.ScriptEvents & scriptEvents.collision) != 0) + { + if (m_lastColliders.Count > 0) + { + ColliderArgs CollidingMessage = new ColliderArgs(); + List colliding = new List(); + foreach (uint localId in m_lastColliders) + { + // always running this check because if the user deletes the object it would return a null reference. + if (localId == 0) + continue; + + if (m_parentGroup == null) + return; + if (m_parentGroup.Scene == null) + return; + SceneObjectPart obj = m_parentGroup.Scene.GetSceneObjectPart(localId); + if (obj != null) + { + DetectedObject detobj = new DetectedObject(); + detobj.keyUUID = obj.UUID; + detobj.nameStr = obj.Name; + detobj.ownerUUID = obj._ownerID; + detobj.posVector = obj.AbsolutePosition; + detobj.rotQuat = obj.GetWorldRotation(); + detobj.velVector = obj.Velocity; + detobj.colliderType = 0; + detobj.groupUUID = obj._groupID; + colliding.Add(detobj); + } + else + { + List avlist = m_parentGroup.Scene.GetScenePresences(); + if (avlist != null) + { + foreach (ScenePresence av in avlist) + { + if (av.LocalId == localId) + { + DetectedObject detobj = new DetectedObject(); + detobj.keyUUID = av.UUID; + detobj.nameStr = av.Name; + detobj.ownerUUID = av.UUID; + detobj.posVector = av.AbsolutePosition; + detobj.rotQuat = av.Rotation; + detobj.velVector = av.Velocity; + detobj.colliderType = 0; + detobj.groupUUID = av.ControllingClient.ActiveGroupId; + colliding.Add(detobj); + } + } + + } + } + } + if (colliding.Count > 0) + { + CollidingMessage.Colliders = colliding; + // always running this check because if the user deletes the object it would return a null reference. + if (m_parentGroup == null) + return; + if (m_parentGroup.Scene == null) + return; + m_parentGroup.Scene.EventManager.TriggerScriptColliding(LocalId, CollidingMessage); + } + + } + } + if ((m_parentGroup.RootPart.ScriptEvents & scriptEvents.collision_end) != 0) + { + if (endedColliders.Count > 0) + { + ColliderArgs EndCollidingMessage = new ColliderArgs(); + List colliding = new List(); + foreach (uint localId in endedColliders) + { + if (localId == 0) + continue; + + // always running this check because if the user deletes the object it would return a null reference. + if (m_parentGroup == null) + return; + if (m_parentGroup.Scene == null) + return; + SceneObjectPart obj = m_parentGroup.Scene.GetSceneObjectPart(localId); + if (obj != null) + { + DetectedObject detobj = new DetectedObject(); + detobj.keyUUID = obj.UUID; + detobj.nameStr = obj.Name; + detobj.ownerUUID = obj._ownerID; + detobj.posVector = obj.AbsolutePosition; + detobj.rotQuat = obj.GetWorldRotation(); + detobj.velVector = obj.Velocity; + detobj.colliderType = 0; + detobj.groupUUID = obj._groupID; + colliding.Add(detobj); + } + else + { + List avlist = m_parentGroup.Scene.GetScenePresences(); + if (avlist != null) + { + foreach (ScenePresence av in avlist) + { + if (av.LocalId == localId) + { + DetectedObject detobj = new DetectedObject(); + detobj.keyUUID = av.UUID; + detobj.nameStr = av.Name; + detobj.ownerUUID = av.UUID; + detobj.posVector = av.AbsolutePosition; + detobj.rotQuat = av.Rotation; + detobj.velVector = av.Velocity; + detobj.colliderType = 0; + detobj.groupUUID = av.ControllingClient.ActiveGroupId; + colliding.Add(detobj); + } + } + + } + } + } + if (colliding.Count > 0) + { + EndCollidingMessage.Colliders = colliding; + // always running this check because if the user deletes the object it would return a null reference. + if (m_parentGroup == null) + return; + if (m_parentGroup.Scene == null) + return; + m_parentGroup.Scene.EventManager.TriggerScriptCollidingEnd(LocalId, EndCollidingMessage); + } + + } + } + } + + public void PhysicsOutOfBounds(PhysicsVector pos) + { + m_log.Info("[PHYSICS]: Physical Object went out of bounds."); + RemFlag(PrimFlags.Physics); + DoPhysicsPropertyUpdate(false, true); + //m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor); + } + + public void PhysicsRequestingTerseUpdate() + { + if (PhysActor != null) + { + Vector3 newpos = new Vector3(PhysActor.Position.GetBytes(), 0); + if (newpos.X > 257f || newpos.X < -1f || newpos.Y > 257f || newpos.Y < -1f) + { + m_parentGroup.AbsolutePosition = newpos; + return; + } + } + ScheduleTerseUpdate(); + + //SendTerseUpdateToAllClients(); + } + + public void PreloadSound(string sound) + { + // UUID ownerID = OwnerID; + UUID objectID = UUID; + UUID soundID = UUID.Zero; + + if (!UUID.TryParse(sound, out soundID)) + { + //Trys to fetch sound id from prim's inventory. + //Prim's inventory doesn't support non script items yet + SceneObjectPart op = this; + foreach (KeyValuePair item in op.TaskInventory) + { + if (item.Value.Name == sound) + { + soundID = item.Value.ItemID; + break; + } + } + } + + List avatarts = m_parentGroup.Scene.GetAvatars(); + foreach (ScenePresence p in avatarts) + { + // TODO: some filtering by distance of avatar + + p.ControllingClient.SendPreLoadSound(objectID, objectID, soundID); + } + } + + public void RemFlag(PrimFlags flag) + { + // PrimFlags prevflag = Flags; + if ((ObjectFlags & (uint) flag) != 0) + { + //Console.WriteLine("Removing flag: " + ((PrimFlags)flag).ToString()); + _flags &= ~flag; + } + //System.Console.WriteLine("prev: " + prevflag.ToString() + " curr: " + Flags.ToString()); + //ScheduleFullUpdate(); + } + + public void RemoveScriptEvents(UUID scriptid) + { + lock (m_scriptEvents) + { + if (m_scriptEvents.ContainsKey(scriptid)) + { + scriptEvents oldparts = scriptEvents.None; + oldparts = (scriptEvents) m_scriptEvents[scriptid]; + + // remove values from aggregated script events + AggregateScriptEvents &= ~oldparts; + m_scriptEvents.Remove(scriptid); + aggregateScriptEvents(); + } + } + } + + /// + /// Reset UUIDs for this part. This involves generate this part's own UUID and + /// generating new UUIDs for all the items in the inventory. + /// + /// Link number for the part + public void ResetIDs(int linkNum) + { + UUID = UUID.Random(); + LinkNum = linkNum; + LocalId = 0; + Inventory.ResetInventoryIDs(); + } + + /// + /// Resize this part. + /// + /// + public void Resize(Vector3 scale) + { + StoreUndoState(); + m_shape.Scale = scale; + + ParentGroup.HasGroupChanged = true; + ScheduleFullUpdate(); + } + + /// + /// Schedules this prim for a full update + /// + public void ScheduleFullUpdate() + { + if (m_parentGroup != null) + { + m_parentGroup.QueueForUpdateCheck(); + } + + int timeNow = Util.UnixTimeSinceEpoch(); + + // If multiple updates are scheduled on the same second, we still need to perform all of them + // So we'll force the issue by bumping up the timestamp so that later processing sees these need + // to be performed. + if (timeNow <= TimeStampFull) + { + TimeStampFull += 1; + } + else + { + TimeStampFull = (uint)timeNow; + } + + m_updateFlag = 2; + + // m_log.DebugFormat( + // "[SCENE OBJECT PART]: Scheduling full update for {0}, {1} at {2}", + // UUID, Name, TimeStampFull); + } + + /// + /// Schedule a terse update for this prim. Terse updates only send position, + /// rotation, velocity, rotational velocity and shape information. + /// + public void ScheduleTerseUpdate() + { + if (m_updateFlag < 1) + { + if (m_parentGroup != null) + { + m_parentGroup.HasGroupChanged = true; + m_parentGroup.QueueForUpdateCheck(); + } + TimeStampTerse = (uint) Util.UnixTimeSinceEpoch(); + m_updateFlag = 1; + + // m_log.DebugFormat( + // "[SCENE OBJECT PART]: Scheduling terse update for {0}, {1} at {2}", + // UUID, Name, TimeStampTerse); + } + } + + public void ScriptSetPhantomStatus(bool Phantom) + { + if (m_parentGroup != null) + { + m_parentGroup.ScriptSetPhantomStatus(Phantom); + } + } + + public void ScriptSetTemporaryStatus(bool Temporary) + { + if (m_parentGroup != null) + { + m_parentGroup.ScriptSetTemporaryStatus(Temporary); + } + } + + public void ScriptSetPhysicsStatus(bool UsePhysics) + { + if (m_parentGroup == null) + DoPhysicsPropertyUpdate(UsePhysics, false); + else + m_parentGroup.ScriptSetPhysicsStatus(UsePhysics); + } + + public void ScriptSetVolumeDetect(bool SetVD) + { + + if (m_parentGroup != null) + { + m_parentGroup.ScriptSetVolumeDetect(SetVD); + } + } + + + public void SculptTextureCallback(UUID textureID, AssetBase texture) + { + if (m_shape.SculptEntry) + { + if (texture != null) + { + m_shape.SculptData = texture.Data; + if (PhysActor != null) + { + // Tricks physics engine into thinking we've changed the part shape. + PrimitiveBaseShape m_newshape = m_shape.Copy(); + PhysActor.Shape = m_newshape; + m_shape = m_newshape; + + m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor); + } + } + } + } + + /// + /// + /// + /// + public void SendFullUpdate(IClientAPI remoteClient, uint clientFlags) + { + m_parentGroup.SendPartFullUpdate(remoteClient, this, clientFlags); + } + + /// + /// + /// + public void SendFullUpdateToAllClients() + { + List avatars = m_parentGroup.Scene.GetScenePresences(); + for (int i = 0; i < avatars.Count; i++) + { + // Ugly reference :( + m_parentGroup.SendPartFullUpdate(avatars[i].ControllingClient, this, + avatars[i].GenerateClientFlags(UUID)); + } + } + + public void SendFullUpdateToAllClientsExcept(UUID agentID) + { + List avatars = m_parentGroup.Scene.GetScenePresences(); + for (int i = 0; i < avatars.Count; i++) + { + // Ugly reference :( + if (avatars[i].UUID != agentID) + { + m_parentGroup.SendPartFullUpdate(avatars[i].ControllingClient, this, + avatars[i].GenerateClientFlags(UUID)); + } + } + } + + /// + /// Sends a full update to the client + /// + /// + /// + public void SendFullUpdateToClient(IClientAPI remoteClient, uint clientflags) + { + Vector3 lPos; + lPos = OffsetPosition; + SendFullUpdateToClient(remoteClient, lPos, clientflags); + } + + /// + /// Sends a full update to the client + /// + /// + /// + /// + public void SendFullUpdateToClient(IClientAPI remoteClient, Vector3 lPos, uint clientFlags) + { + // Suppress full updates during attachment editing + // + if (ParentGroup.IsSelected && IsAttachment) + return; + + if (ParentGroup.IsDeleted) + return; + + clientFlags &= ~(uint) PrimFlags.CreateSelected; + + if (remoteClient.AgentId == _ownerID) + { + if ((uint) (_flags & PrimFlags.CreateSelected) != 0) + { + clientFlags |= (uint) PrimFlags.CreateSelected; + _flags &= ~PrimFlags.CreateSelected; + } + } + //bool isattachment = IsAttachment; + //if (LocalId != ParentGroup.RootPart.LocalId) + //isattachment = ParentGroup.RootPart.IsAttachment; + + byte[] color = new byte[] {m_color.R, m_color.G, m_color.B, m_color.A}; + remoteClient.SendPrimitiveToClient(m_regionHandle, (ushort)(m_parentGroup.GetTimeDilation() * (float)ushort.MaxValue), LocalId, m_shape, + lPos, Velocity, Acceleration, RotationOffset, RotationalVelocity, clientFlags, m_uuid, _ownerID, + m_text, color, _parentID, m_particleSystem, m_clickAction, (byte)m_material, m_TextureAnimation, IsAttachment, + AttachmentPoint,FromAssetID, Sound, SoundGain, SoundFlags, SoundRadius); + } + + /// + /// Tell all the prims which have had updates scheduled + /// + public void SendScheduledUpdates() + { + if (m_updateFlag == 1) //some change has been made so update the clients + { + AddTerseUpdateToAllAvatars(); + ClearUpdateSchedule(); + + // This causes the Scene to 'poll' physical objects every couple of frames + // bad, so it's been replaced by an event driven method. + //if ((ObjectFlags & (uint)PrimFlags.Physics) != 0) + //{ + // Only send the constant terse updates on physical objects! + //ScheduleTerseUpdate(); + //} + } + else + { + if (m_updateFlag == 2) // is a new prim, just created/reloaded or has major changes + { + AddFullUpdateToAllAvatars(); + ClearUpdateSchedule(); + } + } + } + + /// + /// Trigger or play an attached sound in this part's inventory. + /// + /// + /// + /// + /// + public void SendSound(string sound, double volume, bool triggered, byte flags) + { + if (volume > 1) + volume = 1; + if (volume < 0) + volume = 0; + + UUID ownerID = _ownerID; + UUID objectID = UUID; + UUID parentID = GetRootPartUUID(); + UUID soundID = UUID.Zero; + Vector3 position = AbsolutePosition; // region local + ulong regionHandle = m_parentGroup.Scene.RegionInfo.RegionHandle; + + if (!UUID.TryParse(sound, out soundID)) + { + // search sound file from inventory + SceneObjectPart op = this; + foreach (KeyValuePair item in op.TaskInventory) + { + if (item.Value.Name == sound && item.Value.Type == (int)AssetType.Sound) + { + soundID = item.Value.ItemID; + break; + } + } + } + + if (soundID == UUID.Zero) + return; + + ISoundModule soundModule = m_parentGroup.Scene.RequestModuleInterface(); + if (soundModule != null) + { + if (triggered) + soundModule.TriggerSound(soundID, ownerID, objectID, parentID, volume, position, regionHandle); + else + soundModule.PlayAttachedSound(soundID, ownerID, objectID, volume, position, flags); + } + } + + /// + /// Send a terse update to all clients + /// + public void SendTerseUpdateToAllClients() + { + List avatars = m_parentGroup.Scene.GetScenePresences(); + for (int i = 0; i < avatars.Count; i++) + { + SendTerseUpdateToClient(avatars[i].ControllingClient); + } + } + + public void SetAttachmentPoint(uint AttachmentPoint) + { + this.AttachmentPoint = AttachmentPoint; + + if (AttachmentPoint != 0) + { + IsAttachment = true; + } + else + { + IsAttachment = false; + } + + // save the attachment point. + //if (AttachmentPoint != 0) + //{ + m_shape.State = (byte)AttachmentPoint; + //} + } + + public void SetAvatarOnSitTarget(UUID avatarID) + { + m_sitTargetAvatar = avatarID; + if (ParentGroup != null) + ParentGroup.TriggerScriptChangedEvent(Changed.LINK); + } + + public void SetAxisRotation(int axis, int rotate) + { + if (m_parentGroup != null) + { + m_parentGroup.SetAxisRotation(axis, rotate); + } + } + + public void SetBuoyancy(float fvalue) + { + if (PhysActor != null) + { + PhysActor.Buoyancy = fvalue; + } + } + + public void SetDieAtEdge(bool p) + { + if (m_parentGroup == null) + return; + if (m_parentGroup.IsDeleted) + return; + + m_parentGroup.RootPart.DIE_AT_EDGE = p; + } + + public void SetFloatOnWater(int floatYN) + { + if (PhysActor != null) + { + if (floatYN == 1) + { + PhysActor.FloatOnWater = true; + } + else + { + PhysActor.FloatOnWater = false; + } + } + } + + public void SetForce(PhysicsVector force) + { + if (PhysActor != null) + { + PhysActor.Force = force; + } + } + + public void SetVehicleType(int type) + { + if (PhysActor != null) + { + PhysActor.VehicleType = type; + } + } + + public void SetVehicleFloatParam(int param, float value) + { + if (PhysActor != null) + { + PhysActor.VehicleFloatParam(param, value); + } + } + + public void SetVehicleVectorParam(int param, PhysicsVector value) + { + if (PhysActor != null) + { + PhysActor.VehicleVectorParam(param, value); + } + } + + public void SetVehicleRotationParam(int param, Quaternion rotation) + { + if (PhysActor != null) + { + PhysActor.VehicleRotationParam(param, rotation); + } + } + + public void SetGroup(UUID groupID, IClientAPI client) + { + _groupID = groupID; + if (client != null) + GetProperties(client); + m_updateFlag = 2; + } + + /// + /// + /// + public void SetParent(SceneObjectGroup parent) + { + m_parentGroup = parent; + } + + // Use this for attachments! LocalID should be avatar's localid + public void SetParentLocalId(uint localID) + { + _parentID = localID; + } + + public void SetPhysicsAxisRotation() + { + if (PhysActor != null) + { + PhysActor.LockAngularMotion(RotationAxis); + m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor); + } + } + + public void SetScriptEvents(UUID scriptid, int events) + { + // scriptEvents oldparts; + lock (m_scriptEvents) + { + if (m_scriptEvents.ContainsKey(scriptid)) + { + // oldparts = m_scriptEvents[scriptid]; + + // remove values from aggregated script events + if (m_scriptEvents[scriptid] == (scriptEvents) events) + return; + m_scriptEvents[scriptid] = (scriptEvents) events; + } + else + { + m_scriptEvents.Add(scriptid, (scriptEvents) events); + } + } + aggregateScriptEvents(); + } + + /// + /// Set the text displayed for this part. + /// + /// + public void SetText(string text) + { + Text = text; + + ParentGroup.HasGroupChanged = true; + ScheduleFullUpdate(); + } + + /// + /// Set the text displayed for this part. + /// + /// + /// + /// + public void SetText(string text, Vector3 color, double alpha) + { + Color = Color.FromArgb(0xff - (int) (alpha*0xff), + (int) (color.X*0xff), + (int) (color.Y*0xff), + (int) (color.Z*0xff)); + SetText(text); + } + + public void StopMoveToTarget() + { + m_parentGroup.stopMoveToTarget(); + + m_parentGroup.ScheduleGroupForTerseUpdate(); + //m_parentGroup.ScheduleGroupForFullUpdate(); + } + + public void StoreUndoState() + { + if (!Undoing) + { + if (m_parentGroup != null) + { + lock (m_undo) + { + if (m_undo.Count > 0) + { + UndoState last = m_undo.Peek(); + if (last != null) + { + if (last.Compare(this)) + return; + } + } + + if (m_parentGroup.GetSceneMaxUndo() > 0) + { + UndoState nUndo = new UndoState(this); + + m_undo.Push(nUndo); + } + + } + } + } + } + + public EntityIntersection TestIntersection(Ray iray, Quaternion parentrot) + { + // In this case we're using a sphere with a radius of the largest dimention of the prim + // TODO: Change to take shape into account + + + EntityIntersection returnresult = new EntityIntersection(); + Vector3 vAbsolutePosition = AbsolutePosition; + Vector3 vScale = Scale; + Vector3 rOrigin = iray.Origin; + Vector3 rDirection = iray.Direction; + + //rDirection = rDirection.Normalize(); + // Buidling the first part of the Quadratic equation + Vector3 r2ndDirection = rDirection*rDirection; + float itestPart1 = r2ndDirection.X + r2ndDirection.Y + r2ndDirection.Z; + + // Buidling the second part of the Quadratic equation + Vector3 tmVal2 = rOrigin - vAbsolutePosition; + Vector3 r2Direction = rDirection*2.0f; + Vector3 tmVal3 = r2Direction*tmVal2; + + float itestPart2 = tmVal3.X + tmVal3.Y + tmVal3.Z; + + // Buidling the third part of the Quadratic equation + Vector3 tmVal4 = rOrigin*rOrigin; + Vector3 tmVal5 = vAbsolutePosition*vAbsolutePosition; + + Vector3 tmVal6 = vAbsolutePosition*rOrigin; + + + // Set Radius to the largest dimention of the prim + float radius = 0f; + if (vScale.X > radius) + radius = vScale.X; + if (vScale.Y > radius) + radius = vScale.Y; + if (vScale.Z > radius) + radius = vScale.Z; + + // the second part of this is the default prim size + // once we factor in the aabb of the prim we're adding we can + // change this to; + // radius = (radius / 2) - 0.01f; + // + radius = (radius / 2) + (0.5f / 2) - 0.1f; + + //radius = radius; + + float itestPart3 = tmVal4.X + tmVal4.Y + tmVal4.Z + tmVal5.X + tmVal5.Y + tmVal5.Z - + (2.0f*(tmVal6.X + tmVal6.Y + tmVal6.Z + (radius*radius))); + + // Yuk Quadradrics.. Solve first + float rootsqr = (itestPart2*itestPart2) - (4.0f*itestPart1*itestPart3); + if (rootsqr < 0.0f) + { + // No intersection + return returnresult; + } + float root = ((-itestPart2) - (float) Math.Sqrt((double) rootsqr))/(itestPart1*2.0f); + + if (root < 0.0f) + { + // perform second quadratic root solution + root = ((-itestPart2) + (float) Math.Sqrt((double) rootsqr))/(itestPart1*2.0f); + + // is there any intersection? + if (root < 0.0f) + { + // nope, no intersection + return returnresult; + } + } + + // We got an intersection. putting together an EntityIntersection object with the + // intersection information + Vector3 ipoint = + new Vector3(iray.Origin.X + (iray.Direction.X*root), iray.Origin.Y + (iray.Direction.Y*root), + iray.Origin.Z + (iray.Direction.Z*root)); + + returnresult.HitTF = true; + returnresult.ipoint = ipoint; + + // Normal is calculated by the difference and then normalizing the result + Vector3 normalpart = ipoint - vAbsolutePosition; + returnresult.normal = normalpart / normalpart.Length(); + + // It's funny how the Vector3 object has a Distance function, but the Axiom.Math object doesn't. + // I can write a function to do it.. but I like the fact that this one is Static. + + Vector3 distanceConvert1 = new Vector3(iray.Origin.X, iray.Origin.Y, iray.Origin.Z); + Vector3 distanceConvert2 = new Vector3(ipoint.X, ipoint.Y, ipoint.Z); + float distance = (float) Util.GetDistanceTo(distanceConvert1, distanceConvert2); + + returnresult.distance = distance; + + return returnresult; + } + + public EntityIntersection TestIntersectionOBB(Ray iray, Quaternion parentrot, bool frontFacesOnly, bool faceCenters) + { + // In this case we're using a rectangular prism, which has 6 faces and therefore 6 planes + // This breaks down into the ray---> plane equation. + // TODO: Change to take shape into account + Vector3[] vertexes = new Vector3[8]; + + // float[] distance = new float[6]; + Vector3[] FaceA = new Vector3[6]; // vertex A for Facei + Vector3[] FaceB = new Vector3[6]; // vertex B for Facei + Vector3[] FaceC = new Vector3[6]; // vertex C for Facei + Vector3[] FaceD = new Vector3[6]; // vertex D for Facei + + Vector3[] normals = new Vector3[6]; // Normal for Facei + Vector3[] AAfacenormals = new Vector3[6]; // Axis Aligned face normals + + AAfacenormals[0] = new Vector3(1, 0, 0); + AAfacenormals[1] = new Vector3(0, 1, 0); + AAfacenormals[2] = new Vector3(-1, 0, 0); + AAfacenormals[3] = new Vector3(0, -1, 0); + AAfacenormals[4] = new Vector3(0, 0, 1); + AAfacenormals[5] = new Vector3(0, 0, -1); + + Vector3 AmBa = new Vector3(0, 0, 0); // Vertex A - Vertex B + Vector3 AmBb = new Vector3(0, 0, 0); // Vertex B - Vertex C + Vector3 cross = new Vector3(); + + Vector3 pos = GetWorldPosition(); + Quaternion rot = GetWorldRotation(); + + // Variables prefixed with AX are Axiom.Math copies of the LL variety. + + Quaternion AXrot = rot; + AXrot.Normalize(); + + Vector3 AXpos = pos; + + // tScale is the offset to derive the vertex based on the scale. + // it's different for each vertex because we've got to rotate it + // to get the world position of the vertex to produce the Oriented Bounding Box + + Vector3 tScale = Vector3.Zero; + + Vector3 AXscale = new Vector3(m_shape.Scale.X * 0.5f, m_shape.Scale.Y * 0.5f, m_shape.Scale.Z * 0.5f); + + //Vector3 pScale = (AXscale) - (AXrot.Inverse() * (AXscale)); + //Vector3 nScale = (AXscale * -1) - (AXrot.Inverse() * (AXscale * -1)); + + // rScale is the rotated offset to find a vertex based on the scale and the world rotation. + Vector3 rScale = new Vector3(); + + // Get Vertexes for Faces Stick them into ABCD for each Face + // Form: Face[face] that corresponds to the below diagram + #region ABCD Face Vertex Map Comment Diagram + // A _________ B + // | | + // | 4 top | + // |_________| + // C D + + // A _________ B + // | Back | + // | 3 | + // |_________| + // C D + + // A _________ B B _________ A + // | Left | | Right | + // | 0 | | 2 | + // |_________| |_________| + // C D D C + + // A _________ B + // | Front | + // | 1 | + // |_________| + // C D + + // C _________ D + // | | + // | 5 bot | + // |_________| + // A B + #endregion + + #region Plane Decomposition of Oriented Bounding Box + tScale = new Vector3(AXscale.X, -AXscale.Y, AXscale.Z); + rScale = tScale * AXrot; + vertexes[0] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z))); + // vertexes[0].X = pos.X + vertexes[0].X; + //vertexes[0].Y = pos.Y + vertexes[0].Y; + //vertexes[0].Z = pos.Z + vertexes[0].Z; + + FaceA[0] = vertexes[0]; + FaceB[3] = vertexes[0]; + FaceA[4] = vertexes[0]; + + tScale = AXscale; + rScale = tScale * AXrot; + vertexes[1] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z))); + + // vertexes[1].X = pos.X + vertexes[1].X; + // vertexes[1].Y = pos.Y + vertexes[1].Y; + //vertexes[1].Z = pos.Z + vertexes[1].Z; + + FaceB[0] = vertexes[1]; + FaceA[1] = vertexes[1]; + FaceC[4] = vertexes[1]; + + tScale = new Vector3(AXscale.X, -AXscale.Y, -AXscale.Z); + rScale = tScale * AXrot; + + vertexes[2] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z))); + + //vertexes[2].X = pos.X + vertexes[2].X; + //vertexes[2].Y = pos.Y + vertexes[2].Y; + //vertexes[2].Z = pos.Z + vertexes[2].Z; + + FaceC[0] = vertexes[2]; + FaceD[3] = vertexes[2]; + FaceC[5] = vertexes[2]; + + tScale = new Vector3(AXscale.X, AXscale.Y, -AXscale.Z); + rScale = tScale * AXrot; + vertexes[3] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z))); + + //vertexes[3].X = pos.X + vertexes[3].X; + // vertexes[3].Y = pos.Y + vertexes[3].Y; + // vertexes[3].Z = pos.Z + vertexes[3].Z; + + FaceD[0] = vertexes[3]; + FaceC[1] = vertexes[3]; + FaceA[5] = vertexes[3]; + + tScale = new Vector3(-AXscale.X, AXscale.Y, AXscale.Z); + rScale = tScale * AXrot; + vertexes[4] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z))); + + // vertexes[4].X = pos.X + vertexes[4].X; + // vertexes[4].Y = pos.Y + vertexes[4].Y; + // vertexes[4].Z = pos.Z + vertexes[4].Z; + + FaceB[1] = vertexes[4]; + FaceA[2] = vertexes[4]; + FaceD[4] = vertexes[4]; + + tScale = new Vector3(-AXscale.X, AXscale.Y, -AXscale.Z); + rScale = tScale * AXrot; + vertexes[5] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z))); + + // vertexes[5].X = pos.X + vertexes[5].X; + // vertexes[5].Y = pos.Y + vertexes[5].Y; + // vertexes[5].Z = pos.Z + vertexes[5].Z; + + FaceD[1] = vertexes[5]; + FaceC[2] = vertexes[5]; + FaceB[5] = vertexes[5]; + + tScale = new Vector3(-AXscale.X, -AXscale.Y, AXscale.Z); + rScale = tScale * AXrot; + vertexes[6] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z))); + + // vertexes[6].X = pos.X + vertexes[6].X; + // vertexes[6].Y = pos.Y + vertexes[6].Y; + // vertexes[6].Z = pos.Z + vertexes[6].Z; + + FaceB[2] = vertexes[6]; + FaceA[3] = vertexes[6]; + FaceB[4] = vertexes[6]; + + tScale = new Vector3(-AXscale.X, -AXscale.Y, -AXscale.Z); + rScale = tScale * AXrot; + vertexes[7] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z))); + + // vertexes[7].X = pos.X + vertexes[7].X; + // vertexes[7].Y = pos.Y + vertexes[7].Y; + // vertexes[7].Z = pos.Z + vertexes[7].Z; + + FaceD[2] = vertexes[7]; + FaceC[3] = vertexes[7]; + FaceD[5] = vertexes[7]; + #endregion + + // Get our plane normals + for (int i = 0; i < 6; i++) + { + //m_log.Info("[FACECALCULATION]: FaceA[" + i + "]=" + FaceA[i] + " FaceB[" + i + "]=" + FaceB[i] + " FaceC[" + i + "]=" + FaceC[i] + " FaceD[" + i + "]=" + FaceD[i]); + + // Our Plane direction + AmBa = FaceA[i] - FaceB[i]; + AmBb = FaceB[i] - FaceC[i]; + + cross = Vector3.Cross(AmBb, AmBa); + + // normalize the cross product to get the normal. + normals[i] = cross / cross.Length(); + + //m_log.Info("[NORMALS]: normals[ " + i + "]" + normals[i].ToString()); + //distance[i] = (normals[i].X * AmBa.X + normals[i].Y * AmBa.Y + normals[i].Z * AmBa.Z) * -1; + } + + EntityIntersection returnresult = new EntityIntersection(); + + returnresult.distance = 1024; + float c = 0; + float a = 0; + float d = 0; + Vector3 q = new Vector3(); + + #region OBB Version 2 Experiment + //float fmin = 999999; + //float fmax = -999999; + //float s = 0; + + //for (int i=0;i<6;i++) + //{ + //s = iray.Direction.Dot(normals[i]); + //d = normals[i].Dot(FaceB[i]); + + //if (s == 0) + //{ + //if (iray.Origin.Dot(normals[i]) > d) + //{ + //return returnresult; + //} + // else + //{ + //continue; + //} + //} + //a = (d - iray.Origin.Dot(normals[i])) / s; + //if (iray.Direction.Dot(normals[i]) < 0) + //{ + //if (a > fmax) + //{ + //if (a > fmin) + //{ + //return returnresult; + //} + //fmax = a; + //} + + //} + //else + //{ + //if (a < fmin) + //{ + //if (a < 0 || a < fmax) + //{ + //return returnresult; + //} + //fmin = a; + //} + //} + //} + //if (fmax > 0) + // a= fmax; + //else + // a=fmin; + + //q = iray.Origin + a * iray.Direction; + #endregion + + // Loop over faces (6 of them) + for (int i = 0; i < 6; i++) + { + AmBa = FaceA[i] - FaceB[i]; + AmBb = FaceB[i] - FaceC[i]; + d = Vector3.Dot(normals[i], FaceB[i]); + + //if (faceCenters) + //{ + // c = normals[i].Dot(normals[i]); + //} + //else + //{ + c = Vector3.Dot(iray.Direction, normals[i]); + //} + if (c == 0) + continue; + + a = (d - Vector3.Dot(iray.Origin, normals[i])) / c; + + if (a < 0) + continue; + + // If the normal is pointing outside the object + if (Vector3.Dot(iray.Direction, normals[i]) < 0 || !frontFacesOnly) + { + //if (faceCenters) + //{ //(FaceA[i] + FaceB[i] + FaceC[1] + FaceD[i]) / 4f; + // q = iray.Origin + a * normals[i]; + //} + //else + //{ + q = iray.Origin + iray.Direction * a; + //} + + float distance2 = (float)GetDistanceTo(q, AXpos); + // Is this the closest hit to the object's origin? + //if (faceCenters) + //{ + // distance2 = (float)GetDistanceTo(q, iray.Origin); + //} + + if (distance2 < returnresult.distance) + { + returnresult.distance = distance2; + returnresult.HitTF = true; + returnresult.ipoint = q; + //m_log.Info("[FACE]:" + i.ToString()); + //m_log.Info("[POINT]: " + q.ToString()); + //m_log.Info("[DIST]: " + distance2.ToString()); + if (faceCenters) + { + returnresult.normal = AAfacenormals[i] * AXrot; + + Vector3 scaleComponent = AAfacenormals[i]; + float ScaleOffset = 0.5f; + if (scaleComponent.X != 0) ScaleOffset = AXscale.X; + if (scaleComponent.Y != 0) ScaleOffset = AXscale.Y; + if (scaleComponent.Z != 0) ScaleOffset = AXscale.Z; + ScaleOffset = Math.Abs(ScaleOffset); + Vector3 offset = returnresult.normal * ScaleOffset; + returnresult.ipoint = AXpos + offset; + + ///pos = (intersectionpoint + offset); + } + else + { + returnresult.normal = normals[i]; + } + returnresult.AAfaceNormal = AAfacenormals[i]; + } + } + } + return returnresult; + } + + /// + /// Serialize this part to xml. + /// + /// + public void ToXml(XmlWriter xmlWriter) + { + serializer.Serialize(xmlWriter, this); + } + + public void TriggerScriptChangedEvent(Changed val) + { + if (m_parentGroup != null) + { + if (m_parentGroup.Scene != null) + m_parentGroup.Scene.TriggerObjectChanged(LocalId, (uint)val); + } + } + + public void TrimPermissions() + { + _baseMask &= (uint)PermissionMask.All; + _ownerMask &= (uint)PermissionMask.All; + _groupMask &= (uint)PermissionMask.All; + _everyoneMask &= (uint)PermissionMask.All; + _nextOwnerMask &= (uint)PermissionMask.All; + } + + public void Undo() + { + lock (m_undo) + { + if (m_undo.Count > 0) + { + UndoState goback = m_undo.Pop(); + if (goback != null) + goback.PlaybackState(this); + } + } + } + + public void UpdateExtraParam(ushort type, bool inUse, byte[] data) + { + m_shape.ReadInUpdateExtraParam(type, inUse, data); + + if (type == 0x30) + { + if (m_shape.SculptEntry && m_shape.SculptTexture != UUID.Zero) + { + //AssetBase tx = m_parentGroup.Scene.getase + m_parentGroup.Scene.AssetCache.GetAsset(m_shape.SculptTexture, SculptTextureCallback, true); + } + } + + ParentGroup.HasGroupChanged = true; + ScheduleFullUpdate(); + } + + public void UpdateGroupPosition(Vector3 pos) + { + if ((pos.X != GroupPosition.X) || + (pos.Y != GroupPosition.Y) || + (pos.Z != GroupPosition.Z)) + { + Vector3 newPos = new Vector3(pos.X, pos.Y, pos.Z); + GroupPosition = newPos; + ScheduleTerseUpdate(); + } + } + + public virtual void UpdateMovement() + { + } + + /// + /// + /// + /// + public void UpdateOffSet(Vector3 pos) + { + if ((pos.X != OffsetPosition.X) || + (pos.Y != OffsetPosition.Y) || + (pos.Z != OffsetPosition.Z)) + { + Vector3 newPos = new Vector3(pos.X, pos.Y, pos.Z); + OffsetPosition = newPos; + ScheduleTerseUpdate(); + } + } + + public void UpdatePermissions(UUID AgentID, byte field, uint localID, uint mask, byte addRemTF) + { + bool set = addRemTF == 1; + bool god = m_parentGroup.Scene.Permissions.IsGod(AgentID); + + uint baseMask = _baseMask; + if (god) + baseMask = 0x7ffffff0; + + // Are we the owner? + if ((AgentID == _ownerID) || god) + { + switch (field) + { + case 1: + if (god) + { + _baseMask = ApplyMask(_baseMask, set, mask); + Inventory.ApplyGodPermissions(_baseMask); + } + + break; + case 2: + _ownerMask = ApplyMask(_ownerMask, set, mask) & + baseMask; + break; + case 4: + _groupMask = ApplyMask(_groupMask, set, mask) & + baseMask; + break; + case 8: + _everyoneMask = ApplyMask(_everyoneMask, set, mask) & + baseMask; + break; + case 16: + _nextOwnerMask = ApplyMask(_nextOwnerMask, set, mask) & + baseMask; + break; + } + SendFullUpdateToAllClients(); + + SendObjectPropertiesToClient(AgentID); + + } + } + + public bool IsHingeJoint() + { + // For now, we use the NINJA naming scheme for identifying joints. + // In the future, we can support other joint specification schemes such as a + // custom checkbox in the viewer GUI. + if (m_parentGroup.Scene.PhysicsScene.SupportsNINJAJoints) + { + string hingeString = "hingejoint"; + return (Name.Length >= hingeString.Length && Name.Substring(0, hingeString.Length) == hingeString); + } + else + { + return false; + } + } + + public bool IsBallJoint() + { + // For now, we use the NINJA naming scheme for identifying joints. + // In the future, we can support other joint specification schemes such as a + // custom checkbox in the viewer GUI. + if (m_parentGroup.Scene.PhysicsScene.SupportsNINJAJoints) + { + string ballString = "balljoint"; + return (Name.Length >= ballString.Length && Name.Substring(0, ballString.Length) == ballString); + } + else + { + return false; + } + } + + public bool IsJoint() + { + // For now, we use the NINJA naming scheme for identifying joints. + // In the future, we can support other joint specification schemes such as a + // custom checkbox in the viewer GUI. + if (m_parentGroup.Scene.PhysicsScene.SupportsNINJAJoints) + { + return IsHingeJoint() || IsBallJoint(); + } + else + { + return false; + } + } + + public void UpdatePrimFlags(bool UsePhysics, bool IsTemporary, bool IsPhantom, bool IsVD) + { + bool wasUsingPhysics = ((ObjectFlags & (uint) PrimFlags.Physics) != 0); + bool wasTemporary = ((ObjectFlags & (uint)PrimFlags.TemporaryOnRez) != 0); + bool wasPhantom = ((ObjectFlags & (uint)PrimFlags.Phantom) != 0); + bool wasVD = VolumeDetectActive; + + if ((UsePhysics == wasUsingPhysics) && (wasTemporary == IsTemporary) && (wasPhantom == IsPhantom) && (IsVD==wasVD) ) + { + return; + } + + // Special cases for VD. VD can only be called from a script + // and can't be combined with changes to other states. So we can rely + // that... + // ... if VD is changed, all others are not. + // ... if one of the others is changed, VD is not. + if (IsVD) // VD is active, special logic applies + { + // State machine logic for VolumeDetect + // More logic below + bool phanReset = (IsPhantom != wasPhantom) && !IsPhantom; + + if (phanReset) // Phantom changes from on to off switch VD off too + { + IsVD = false; // Switch it of for the course of this routine + VolumeDetectActive = false; // and also permanently + if (PhysActor != null) + PhysActor.SetVolumeDetect(0); // Let physics know about it too + } + else + { + IsPhantom = false; + // If volumedetect is active we don't want phantom to be applied. + // If this is a new call to VD out of the state "phantom" + // this will also cause the prim to be visible to physics + } + + } + + if (UsePhysics && IsJoint()) + { + IsPhantom = true; + } + + if (UsePhysics) + { + AddFlag(PrimFlags.Physics); + if (!wasUsingPhysics) + { + DoPhysicsPropertyUpdate(UsePhysics, false); + if (m_parentGroup != null) + { + if (!m_parentGroup.IsDeleted) + { + if (LocalId == m_parentGroup.RootPart.LocalId) + { + m_parentGroup.CheckSculptAndLoad(); + } + } + } + } + } + else + { + RemFlag(PrimFlags.Physics); + if (wasUsingPhysics) + { + DoPhysicsPropertyUpdate(UsePhysics, false); + } + } + + + if (IsPhantom || IsAttachment) // note: this may have been changed above in the case of joints + { + AddFlag(PrimFlags.Phantom); + if (PhysActor != null) + { + m_parentGroup.Scene.PhysicsScene.RemovePrim(PhysActor); + /// that's not wholesome. Had to make Scene public + PhysActor = null; + } + } + else // Not phantom + { + RemFlag(PrimFlags.Phantom); + + if (PhysActor == null) + { + // It's not phantom anymore. So make sure the physics engine get's knowledge of it + PhysActor = m_parentGroup.Scene.PhysicsScene.AddPrimShape( + Name, + Shape, + new PhysicsVector(AbsolutePosition.X, AbsolutePosition.Y, AbsolutePosition.Z), + new PhysicsVector(Scale.X, Scale.Y, Scale.Z), + RotationOffset, + UsePhysics); + + if (PhysActor != null) + { + PhysActor.LocalID = LocalId; + DoPhysicsPropertyUpdate(UsePhysics, true); + if (m_parentGroup != null) + { + if (!m_parentGroup.IsDeleted) + { + if (LocalId == m_parentGroup.RootPart.LocalId) + { + m_parentGroup.CheckSculptAndLoad(); + } + } + } + if ( + ((AggregateScriptEvents & scriptEvents.collision) != 0) || + ((AggregateScriptEvents & scriptEvents.collision_end) != 0) || + ((AggregateScriptEvents & scriptEvents.collision_start) != 0) || + (CollisionSound != UUID.Zero) + ) + { + PhysActor.OnCollisionUpdate += PhysicsCollision; + PhysActor.SubscribeEvents(1000); + } + } + } + else // it already has a physical representation + { + PhysActor.IsPhysical = UsePhysics; + + DoPhysicsPropertyUpdate(UsePhysics, false); // Update physical status. If it's phantom this will remove the prim + if (m_parentGroup != null) + { + if (!m_parentGroup.IsDeleted) + { + if (LocalId == m_parentGroup.RootPart.LocalId) + { + m_parentGroup.CheckSculptAndLoad(); + } + } + } + } + } + + if (IsVD) + { + // If the above logic worked (this is urgent candidate to unit tests!) + // we now have a physicsactor. + // Defensive programming calls for a check here. + // Better would be throwing an exception that could be catched by a unit test as the internal + // logic should make sure, this Physactor is always here. + if (this.PhysActor != null) + { + PhysActor.SetVolumeDetect(1); + AddFlag(PrimFlags.Phantom); // We set this flag also if VD is active + this.VolumeDetectActive = true; + } + + } + else + { // Remove VolumeDetect in any case. Note, it's safe to call SetVolumeDetect as often as you like + // (mumbles, well, at least if you have infinte CPU powers :-) ) + if (this.PhysActor != null) + { + PhysActor.SetVolumeDetect(0); + } + this.VolumeDetectActive = false; + } + + + if (IsTemporary) + { + AddFlag(PrimFlags.TemporaryOnRez); + } + else + { + RemFlag(PrimFlags.TemporaryOnRez); + } + // System.Console.WriteLine("Update: PHY:" + UsePhysics.ToString() + ", T:" + IsTemporary.ToString() + ", PHA:" + IsPhantom.ToString() + " S:" + CastsShadows.ToString()); + + ParentGroup.HasGroupChanged = true; + ScheduleFullUpdate(); + } + + public void UpdateRotation(Quaternion rot) + { + if ((rot.X != RotationOffset.X) || + (rot.Y != RotationOffset.Y) || + (rot.Z != RotationOffset.Z) || + (rot.W != RotationOffset.W)) + { + //StoreUndoState(); + RotationOffset = rot; + ParentGroup.HasGroupChanged = true; + ScheduleTerseUpdate(); + } + } + + /// + /// Update the shape of this part. + /// + /// + public void UpdateShape(ObjectShapePacket.ObjectDataBlock shapeBlock) + { + m_shape.PathBegin = shapeBlock.PathBegin; + m_shape.PathEnd = shapeBlock.PathEnd; + m_shape.PathScaleX = shapeBlock.PathScaleX; + m_shape.PathScaleY = shapeBlock.PathScaleY; + m_shape.PathShearX = shapeBlock.PathShearX; + m_shape.PathShearY = shapeBlock.PathShearY; + m_shape.PathSkew = shapeBlock.PathSkew; + m_shape.ProfileBegin = shapeBlock.ProfileBegin; + m_shape.ProfileEnd = shapeBlock.ProfileEnd; + m_shape.PathCurve = shapeBlock.PathCurve; + m_shape.ProfileCurve = shapeBlock.ProfileCurve; + m_shape.ProfileHollow = shapeBlock.ProfileHollow; + m_shape.PathRadiusOffset = shapeBlock.PathRadiusOffset; + m_shape.PathRevolutions = shapeBlock.PathRevolutions; + m_shape.PathTaperX = shapeBlock.PathTaperX; + m_shape.PathTaperY = shapeBlock.PathTaperY; + m_shape.PathTwist = shapeBlock.PathTwist; + m_shape.PathTwistBegin = shapeBlock.PathTwistBegin; + if (PhysActor != null) + { + PhysActor.Shape = m_shape; + } + + // This is what makes vehicle trailers work + // A script in a child prim re-issues + // llSetPrimitiveParams(PRIM_TYPE) every few seconds. That + // prevents autoreturn. This is not well known. It also works + // in SL. + // + if (ParentGroup.RootPart != this) + ParentGroup.RootPart.Rezzed = DateTime.Now; + + ParentGroup.HasGroupChanged = true; + ScheduleFullUpdate(); + } + + // Added to handle bug in libsecondlife's TextureEntry.ToBytes() + // not handling RGBA properly. Cycles through, and "fixes" the color + // info + public void UpdateTexture(Primitive.TextureEntry tex) + { + //Color4 tmpcolor; + //for (uint i = 0; i < 32; i++) + //{ + // if (tex.FaceTextures[i] != null) + // { + // tmpcolor = tex.GetFace((uint) i).RGBA; + // tmpcolor.A = tmpcolor.A*255; + // tmpcolor.R = tmpcolor.R*255; + // tmpcolor.G = tmpcolor.G*255; + // tmpcolor.B = tmpcolor.B*255; + // tex.FaceTextures[i].RGBA = tmpcolor; + // } + //} + //tmpcolor = tex.DefaultTexture.RGBA; + //tmpcolor.A = tmpcolor.A*255; + //tmpcolor.R = tmpcolor.R*255; + //tmpcolor.G = tmpcolor.G*255; + //tmpcolor.B = tmpcolor.B*255; + //tex.DefaultTexture.RGBA = tmpcolor; + UpdateTextureEntry(tex.ToBytes()); + } + + /// + /// Update the texture entry for this part. + /// + /// + public void UpdateTextureEntry(byte[] textureEntry) + { + m_shape.TextureEntry = textureEntry; + TriggerScriptChangedEvent(Changed.TEXTURE); + + ParentGroup.HasGroupChanged = true; + ParentGroup.ScheduleGroupForFullUpdate(); + } + + public void aggregateScriptEvents() + { + AggregateScriptEvents = 0; + + // Aggregate script events + lock (m_scriptEvents) + { + foreach (scriptEvents s in m_scriptEvents.Values) + { + AggregateScriptEvents |= s; + } + } + + uint objectflagupdate = 0; + + if ( + ((AggregateScriptEvents & scriptEvents.touch) != 0) || + ((AggregateScriptEvents & scriptEvents.touch_end) != 0) || + ((AggregateScriptEvents & scriptEvents.touch_start) != 0) + ) + { + objectflagupdate |= (uint) PrimFlags.Touch; + } + + if ((AggregateScriptEvents & scriptEvents.money) != 0) + { + objectflagupdate |= (uint) PrimFlags.Money; + } + + if (AllowedDrop) + { + objectflagupdate |= (uint) PrimFlags.AllowInventoryDrop; + } + + if ( + ((AggregateScriptEvents & scriptEvents.collision) != 0) || + ((AggregateScriptEvents & scriptEvents.collision_end) != 0) || + ((AggregateScriptEvents & scriptEvents.collision_start) != 0) || + (CollisionSound != UUID.Zero) + ) + { + // subscribe to physics updates. + if (PhysActor != null) + { + PhysActor.OnCollisionUpdate += PhysicsCollision; + PhysActor.SubscribeEvents(1000); + + } + } + else + { + if (PhysActor != null) + { + PhysActor.UnSubscribeEvents(); + PhysActor.OnCollisionUpdate -= PhysicsCollision; + } + } + + if (m_parentGroup == null) + { + ScheduleFullUpdate(); + return; + } + + if ((GetEffectiveObjectFlags() & (uint)PrimFlags.Scripted) != 0) + { + m_parentGroup.Scene.EventManager.OnScriptTimerEvent += handleTimerAccounting; + } + else + { + m_parentGroup.Scene.EventManager.OnScriptTimerEvent -= handleTimerAccounting; + } + + LocalFlags=(PrimFlags)objectflagupdate; + + if (m_parentGroup != null && m_parentGroup.RootPart == this) + m_parentGroup.aggregateScriptEvents(); + else + ScheduleFullUpdate(); + } + + public int registerTargetWaypoint(Vector3 target, float tolerance) + { + if (m_parentGroup != null) + { + return m_parentGroup.registerTargetWaypoint(target, tolerance); + } + return 0; + } + + public void unregisterTargetWaypoint(int handle) + { + if (m_parentGroup != null) + { + m_parentGroup.unregisterTargetWaypoint(handle); + } + } + + public void SetCameraAtOffset(Vector3 v) + { + m_cameraAtOffset = v; + } + + public void SetCameraEyeOffset(Vector3 v) + { + m_cameraEyeOffset = v; + } + + public void SetForceMouselook(bool force) + { + m_forceMouselook = force; + } + + public Vector3 GetCameraAtOffset() + { + return m_cameraAtOffset; + } + + public Vector3 GetCameraEyeOffset() + { + return m_cameraEyeOffset; + } + + public bool GetForceMouselook() + { + return m_forceMouselook; + } + + public override string ToString() + { + return String.Format("{0} {1} (parent {2}))", Name, UUID, ParentGroup); + } + + #endregion Public Methods + + public void SendTerseUpdateToClient(IClientAPI remoteClient) + { + if (ParentGroup == null || ParentGroup.IsDeleted) + return; + + Vector3 lPos = OffsetPosition; + + byte state = Shape.State; + if (IsAttachment) + { + if (ParentGroup.RootPart != this) + return; + + lPos = ParentGroup.RootPart.AttachedPos; + state = (byte)AttachmentPoint; + } + else + { + if (ParentGroup.RootPart == this) + lPos = AbsolutePosition; + } + + remoteClient.SendPrimTerseUpdate(m_regionHandle, + (ushort)(m_parentGroup.GetTimeDilation() * + (float)ushort.MaxValue), LocalId, lPos, + RotationOffset, Velocity, + RotationalVelocity, state, FromAssetID, + OwnerID, (int)AttachmentPoint); + } + + public void AddScriptLPS(int count) + { + m_parentGroup.AddScriptLPS(count); + } + + public void ApplyNextOwnerPermissions() + { + _baseMask &= _nextOwnerMask; + _ownerMask &= _nextOwnerMask; + _everyoneMask &= _nextOwnerMask; + + Inventory.ApplyNextOwnerPermissions(); + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs new file mode 100644 index 0000000..efd486d --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs @@ -0,0 +1,891 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using log4net; +using OpenSim.Framework; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes.Scripting; + +namespace OpenSim.Region.Framework.Scenes +{ + public class SceneObjectPartInventory : IEntityInventory + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_inventoryFileName = String.Empty; + private int m_inventoryFileNameSerial = 0; + + /// + /// The part to which the inventory belongs. + /// + private SceneObjectPart m_part; + + /// + /// Serial count for inventory file , used to tell if inventory has changed + /// no need for this to be part of Database backup + /// + protected uint m_inventorySerial = 0; + + /// + /// Holds in memory prim inventory + /// + protected TaskInventoryDictionary m_items = new TaskInventoryDictionary(); + + /// + /// Tracks whether inventory has changed since the last persistent backup + /// + internal bool HasInventoryChanged; + + /// + /// Inventory serial number + /// + protected internal uint Serial + { + get { return m_inventorySerial; } + set { m_inventorySerial = value; } + } + + /// + /// Raw inventory data + /// + protected internal TaskInventoryDictionary Items + { + get { return m_items; } + set + { + m_items = value; + m_inventorySerial++; + } + } + + /// + /// Constructor + /// + /// + /// A + /// + public SceneObjectPartInventory(SceneObjectPart part) + { + m_part = part; + } + + /// + /// Force the task inventory of this prim to persist at the next update sweep + /// + public void ForceInventoryPersistence() + { + HasInventoryChanged = true; + } + + /// + /// Reset UUIDs for all the items in the prim's inventory. This involves either generating + /// new ones or setting existing UUIDs to the correct parent UUIDs. + /// + /// If this method is called and there are inventory items, then we regard the inventory as having changed. + /// + /// Link number for the part + public void ResetInventoryIDs() + { + lock (Items) + { + if (0 == Items.Count) + return; + + HasInventoryChanged = true; + m_part.ParentGroup.HasGroupChanged = true; + IList items = new List(Items.Values); + Items.Clear(); + + foreach (TaskInventoryItem item in items) + { + item.ResetIDs(m_part.UUID); + Items.Add(item.ItemID, item); + } + } + } + + /// + /// Change every item in this inventory to a new owner. + /// + /// + public void ChangeInventoryOwner(UUID ownerId) + { + lock (Items) + { + if (0 == Items.Count) + { + return; + } + + HasInventoryChanged = true; + m_part.ParentGroup.HasGroupChanged = true; + IList items = new List(Items.Values); + foreach (TaskInventoryItem item in items) + { + if (ownerId != item.OwnerID) + { + item.LastOwnerID = item.OwnerID; + item.OwnerID = ownerId; + } + } + } + } + + /// + /// Change every item in this inventory to a new group. + /// + /// + public void ChangeInventoryGroup(UUID groupID) + { + lock (Items) + { + if (0 == Items.Count) + { + return; + } + + HasInventoryChanged = true; + m_part.ParentGroup.HasGroupChanged = true; + IList items = new List(Items.Values); + foreach (TaskInventoryItem item in items) + { + if (groupID != item.GroupID) + { + item.GroupID = groupID; + } + } + } + } + + /// + /// Start all the scripts contained in this prim's inventory + /// + public void CreateScriptInstances(int startParam, bool postOnRez, string engine, int stateSource) + { + lock (m_items) + { + foreach (TaskInventoryItem item in Items.Values) + { + if ((int)InventoryType.LSL == item.InvType) + { + CreateScriptInstance(item, startParam, postOnRez, engine, stateSource); + } + } + } + } + + /// + /// Stop all the scripts in this prim. + /// + public void RemoveScriptInstances() + { + lock (Items) + { + foreach (TaskInventoryItem item in Items.Values) + { + if ((int)InventoryType.LSL == item.InvType) + { + RemoveScriptInstance(item.ItemID); + m_part.RemoveScriptEvents(item.ItemID); + } + } + } + } + + /// + /// Start a script which is in this prim's inventory. + /// + /// + /// + public void CreateScriptInstance(TaskInventoryItem item, int startParam, bool postOnRez, string engine, int stateSource) + { + // m_log.InfoFormat( + // "[PRIM INVENTORY]: " + + // "Starting script {0}, {1} in prim {2}, {3}", + // item.Name, item.ItemID, Name, UUID); + + if (!m_part.ParentGroup.Scene.Permissions.CanRunScript(item.ItemID, m_part.UUID, item.OwnerID)) + return; + + m_part.AddFlag(PrimFlags.Scripted); + + if (!m_part.ParentGroup.Scene.RegionInfo.RegionSettings.DisableScripts) + { + if (stateSource == 1 && // Prim crossing + m_part.ParentGroup.Scene.m_trustBinaries) + { + m_items[item.ItemID].PermsMask = 0; + m_items[item.ItemID].PermsGranter = UUID.Zero; + m_part.ParentGroup.Scene.EventManager.TriggerRezScript( + m_part.LocalId, item.ItemID, String.Empty, startParam, postOnRez, engine, stateSource); + m_part.ParentGroup.AddActiveScriptCount(1); + m_part.ScheduleFullUpdate(); + return; + } + AssetCache cache = m_part.ParentGroup.Scene.AssetCache; + + cache.GetAsset(item.AssetID, delegate(UUID assetID, AssetBase asset) + { + if (null == asset) + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Couldn't start script {0}, {1} since asset ID {2} could not be found", + item.Name, item.ItemID, item.AssetID); + } + else + { + m_items[item.ItemID].PermsMask = 0; + m_items[item.ItemID].PermsGranter = UUID.Zero; + string script = Utils.BytesToString(asset.Data); + m_part.ParentGroup.Scene.EventManager.TriggerRezScript( + m_part.LocalId, item.ItemID, script, startParam, postOnRez, engine, stateSource); + m_part.ParentGroup.AddActiveScriptCount(1); + m_part.ScheduleFullUpdate(); + } + }, false); + } + } + + /// + /// Start a script which is in this prim's inventory. + /// + /// + /// A + /// + public void CreateScriptInstance(UUID itemId, int startParam, bool postOnRez, string engine, int stateSource) + { + lock (m_items) + { + if (m_items.ContainsKey(itemId)) + { + CreateScriptInstance(m_items[itemId], startParam, postOnRez, engine, stateSource); + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Couldn't start script with ID {0} since it couldn't be found for prim {1}, {2}", + itemId, m_part.Name, m_part.UUID); + } + } + } + + /// + /// Stop a script which is in this prim's inventory. + /// + /// + public void RemoveScriptInstance(UUID itemId) + { + if (m_items.ContainsKey(itemId)) + { + m_part.ParentGroup.Scene.EventManager.TriggerRemoveScript(m_part.LocalId, itemId); + m_part.ParentGroup.AddActiveScriptCount(-1); + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Couldn't stop script with ID {0} since it couldn't be found for prim {1}, {2}", + itemId, m_part.Name, m_part.UUID); + } + } + + /// + /// Check if the inventory holds an item with a given name. + /// This method assumes that the task inventory is already locked. + /// + /// + /// + private bool InventoryContainsName(string name) + { + foreach (TaskInventoryItem item in Items.Values) + { + if (item.Name == name) + return true; + } + return false; + } + + /// + /// For a given item name, return that name if it is available. Otherwise, return the next available + /// similar name (which is currently the original name with the next available numeric suffix). + /// + /// + /// + private string FindAvailableInventoryName(string name) + { + if (!InventoryContainsName(name)) + return name; + + int suffix=1; + while (suffix < 256) + { + string tryName=String.Format("{0} {1}", name, suffix); + if (!InventoryContainsName(tryName)) + return tryName; + suffix++; + } + return String.Empty; + } + + /// + /// Add an item to this prim's inventory. If an item with the same name already exists, then an alternative + /// name is chosen. + /// + /// + public void AddInventoryItem(TaskInventoryItem item, bool allowedDrop) + { + AddInventoryItem(item.Name, item, allowedDrop); + } + + /// + /// Add an item to this prim's inventory. If an item with the same name already exists, it is replaced. + /// + /// + public void AddInventoryItemExclusive(TaskInventoryItem item, bool allowedDrop) + { + List il = new List(m_items.Values); + foreach (TaskInventoryItem i in il) + { + if (i.Name == item.Name) + { + if (i.InvType == (int)InventoryType.LSL) + RemoveScriptInstance(i.ItemID); + + RemoveInventoryItem(i.ItemID); + break; + } + } + + AddInventoryItem(item.Name, item, allowedDrop); + } + + /// + /// Add an item to this prim's inventory. + /// + /// The name that the new item should have. + /// + /// The item itself. The name within this structure is ignored in favour of the name + /// given in this method's arguments + /// + /// + /// Item was only added to inventory because AllowedDrop is set + /// + protected void AddInventoryItem(string name, TaskInventoryItem item, bool allowedDrop) + { + name = FindAvailableInventoryName(name); + if (name == String.Empty) + return; + + item.ParentID = m_part.UUID; + item.ParentPartID = m_part.UUID; + item.Name = name; + + lock (m_items) + { + m_items.Add(item.ItemID, item); + + if (allowedDrop) + m_part.TriggerScriptChangedEvent(Changed.ALLOWED_DROP); + else + m_part.TriggerScriptChangedEvent(Changed.INVENTORY); + } + + m_inventorySerial++; + //m_inventorySerial += 2; + HasInventoryChanged = true; + m_part.ParentGroup.HasGroupChanged = true; + } + + /// + /// Restore a whole collection of items to the prim's inventory at once. + /// We assume that the items already have all their fields correctly filled out. + /// The items are not flagged for persistence to the database, since they are being restored + /// from persistence rather than being newly added. + /// + /// + public void RestoreInventoryItems(ICollection items) + { + lock (m_items) + { + foreach (TaskInventoryItem item in items) + { + m_items.Add(item.ItemID, item); + m_part.TriggerScriptChangedEvent(Changed.INVENTORY); + } + } + + m_inventorySerial++; + } + + /// + /// Returns an existing inventory item. Returns the original, so any changes will be live. + /// + /// + /// null if the item does not exist + public TaskInventoryItem GetInventoryItem(UUID itemId) + { + TaskInventoryItem item; + m_items.TryGetValue(itemId, out item); + + return item; + } + + /// + /// Update an existing inventory item. + /// + /// The updated item. An item with the same id must already exist + /// in this prim's inventory. + /// false if the item did not exist, true if the update occurred successfully + public bool UpdateInventoryItem(TaskInventoryItem item) + { + lock (m_items) + { + if (m_items.ContainsKey(item.ItemID)) + { + item.ParentID = m_part.UUID; + item.ParentPartID = m_part.UUID; + item.Flags = m_items[item.ItemID].Flags; + if (item.AssetID == UUID.Zero) + { + item.AssetID = m_items[item.ItemID].AssetID; + } + else if ((InventoryType)item.Type == InventoryType.Notecard) + { + ScenePresence presence = m_part.ParentGroup.Scene.GetScenePresence(item.OwnerID); + + if (presence != null) + { + presence.ControllingClient.SendAgentAlertMessage( + "Notecard saved", false); + } + } + + m_items[item.ItemID] = item; + m_inventorySerial++; + m_part.TriggerScriptChangedEvent(Changed.INVENTORY); + + HasInventoryChanged = true; + m_part.ParentGroup.HasGroupChanged = true; + + return true; + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Tried to retrieve item ID {0} from prim {1}, {2} but the item does not exist in this inventory", + item.ItemID, m_part.Name, m_part.UUID); + } + } + + return false; + } + + /// + /// Remove an item from this prim's inventory + /// + /// + /// Numeric asset type of the item removed. Returns -1 if the item did not exist + /// in this prim's inventory. + public int RemoveInventoryItem(UUID itemID) + { + lock (m_items) + { + if (m_items.ContainsKey(itemID)) + { + int type = m_items[itemID].InvType; + if (type == 10) // Script + { + m_part.ParentGroup.Scene.EventManager.TriggerRemoveScript(m_part.LocalId, itemID); + } + m_items.Remove(itemID); + m_inventorySerial++; + m_part.TriggerScriptChangedEvent(Changed.INVENTORY); + + HasInventoryChanged = true; + m_part.ParentGroup.HasGroupChanged = true; + + int scriptcount = 0; + lock (m_items) + { + foreach (TaskInventoryItem item in m_items.Values) + { + if (item.Type == 10) + { + scriptcount++; + } + } + } + + if (scriptcount <= 0) + { + m_part.RemFlag(PrimFlags.Scripted); + } + + m_part.ScheduleFullUpdate(); + + return type; + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Tried to remove item ID {0} from prim {1}, {2} but the item does not exist in this inventory", + itemID, m_part.Name, m_part.UUID); + } + } + + return -1; + } + + public string GetInventoryFileName() + { + if (m_inventoryFileName == String.Empty) + m_inventoryFileName = "inventory_" + UUID.Random().ToString() + ".tmp"; + if (m_inventoryFileNameSerial < m_inventorySerial) + { + m_inventoryFileName = "inventory_" + UUID.Random().ToString() + ".tmp"; + } + return m_inventoryFileName; + } + + /// + /// Return the name with which a client can request a xfer of this prim's inventory metadata + /// + /// + /// + public bool GetInventoryFileName(IClientAPI client, uint localID) + { +// m_log.DebugFormat( +// "[PRIM INVENTORY]: Received request from client {0} for inventory file name of {1}, {2}", +// client.AgentId, Name, UUID); + + if (m_inventorySerial > 0) + { + client.SendTaskInventory(m_part.UUID, (short)m_inventorySerial, + Utils.StringToBytes(GetInventoryFileName())); + return true; + } + else + { + client.SendTaskInventory(m_part.UUID, 0, new byte[0]); + return false; + } + } + + /// + /// Serialize all the metadata for the items in this prim's inventory ready for sending to the client + /// + /// + public void RequestInventoryFile(IClientAPI client, IXfer xferManager) + { + byte[] fileData = new byte[0]; + + // Confusingly, the folder item has to be the object id, while the 'parent id' has to be zero. This matches + // what appears to happen in the Second Life protocol. If this isn't the case. then various functionality + // isn't available (such as drag from prim inventory to agent inventory) + InventoryStringBuilder invString = new InventoryStringBuilder(m_part.UUID, UUID.Zero); + + lock (m_items) + { + foreach (TaskInventoryItem item in m_items.Values) + { + UUID ownerID = item.OwnerID; + uint everyoneMask = 0; + uint baseMask = item.BasePermissions; + uint ownerMask = item.CurrentPermissions; + + invString.AddItemStart(); + invString.AddNameValueLine("item_id", item.ItemID.ToString()); + invString.AddNameValueLine("parent_id", m_part.UUID.ToString()); + + invString.AddPermissionsStart(); + + invString.AddNameValueLine("base_mask", Utils.UIntToHexString(baseMask)); + invString.AddNameValueLine("owner_mask", Utils.UIntToHexString(ownerMask)); + invString.AddNameValueLine("group_mask", Utils.UIntToHexString(0)); + invString.AddNameValueLine("everyone_mask", Utils.UIntToHexString(everyoneMask)); + invString.AddNameValueLine("next_owner_mask", Utils.UIntToHexString(item.NextPermissions)); + + invString.AddNameValueLine("creator_id", item.CreatorID.ToString()); + invString.AddNameValueLine("owner_id", ownerID.ToString()); + + invString.AddNameValueLine("last_owner_id", item.LastOwnerID.ToString()); + + invString.AddNameValueLine("group_id", item.GroupID.ToString()); + invString.AddSectionEnd(); + + invString.AddNameValueLine("asset_id", item.AssetID.ToString()); + invString.AddNameValueLine("type", TaskInventoryItem.Types[item.Type]); + invString.AddNameValueLine("inv_type", TaskInventoryItem.InvTypes[item.InvType]); + invString.AddNameValueLine("flags", Utils.UIntToHexString(item.Flags)); + + invString.AddSaleStart(); + invString.AddNameValueLine("sale_type", "not"); + invString.AddNameValueLine("sale_price", "0"); + invString.AddSectionEnd(); + + invString.AddNameValueLine("name", item.Name + "|"); + invString.AddNameValueLine("desc", item.Description + "|"); + + invString.AddNameValueLine("creation_date", item.CreationDate.ToString()); + invString.AddSectionEnd(); + } + } + + fileData = Utils.StringToBytes(invString.BuildString); + + //Console.WriteLine(Utils.BytesToString(fileData)); + //m_log.Debug("[PRIM INVENTORY]: RequestInventoryFile fileData: " + Utils.BytesToString(fileData)); + + if (fileData.Length > 2) + { + xferManager.AddNewFile(m_inventoryFileName, fileData); + } + } + + /// + /// Process inventory backup + /// + /// + public void ProcessInventoryBackup(IRegionDataStore datastore) + { + if (HasInventoryChanged) + { + lock (Items) + { + datastore.StorePrimInventory(m_part.UUID, Items.Values); + } + + HasInventoryChanged = false; + } + } + + public class InventoryStringBuilder + { + public string BuildString = String.Empty; + + public InventoryStringBuilder(UUID folderID, UUID parentID) + { + BuildString += "\tinv_object\t0\n\t{\n"; + AddNameValueLine("obj_id", folderID.ToString()); + AddNameValueLine("parent_id", parentID.ToString()); + AddNameValueLine("type", "category"); + AddNameValueLine("name", "Contents|"); + AddSectionEnd(); + } + + public void AddItemStart() + { + BuildString += "\tinv_item\t0\n"; + AddSectionStart(); + } + + public void AddPermissionsStart() + { + BuildString += "\tpermissions 0\n"; + AddSectionStart(); + } + + public void AddSaleStart() + { + BuildString += "\tsale_info\t0\n"; + AddSectionStart(); + } + + protected void AddSectionStart() + { + BuildString += "\t{\n"; + } + + public void AddSectionEnd() + { + BuildString += "\t}\n"; + } + + public void AddLine(string addLine) + { + BuildString += addLine; + } + + public void AddNameValueLine(string name, string value) + { + BuildString += "\t\t"; + BuildString += name + "\t"; + BuildString += value + "\n"; + } + + public void Close() + { + } + } + + public uint MaskEffectivePermissions() + { + uint mask=0x7fffffff; + + foreach (TaskInventoryItem item in m_items.Values) + { + if (item.InvType != 6) + { + if ((item.CurrentPermissions & item.NextPermissions & (uint)PermissionMask.Copy) == 0) + mask &= ~((uint)PermissionMask.Copy >> 13); + if ((item.CurrentPermissions & item.NextPermissions & (uint)PermissionMask.Transfer) == 0) + mask &= ~((uint)PermissionMask.Transfer >> 13); + if ((item.CurrentPermissions & item.NextPermissions & (uint)PermissionMask.Modify) == 0) + mask &= ~((uint)PermissionMask.Modify >> 13); + } + else + { + if ((item.CurrentPermissions & ((uint)PermissionMask.Copy >> 13)) == 0) + mask &= ~((uint)PermissionMask.Copy >> 13); + if ((item.CurrentPermissions & ((uint)PermissionMask.Transfer >> 13)) == 0) + mask &= ~((uint)PermissionMask.Transfer >> 13); + if ((item.CurrentPermissions & ((uint)PermissionMask.Modify >> 13)) == 0) + mask &= ~((uint)PermissionMask.Modify >> 13); + } + + if ((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0) + mask &= ~(uint)PermissionMask.Copy; + if ((item.CurrentPermissions & (uint)PermissionMask.Transfer) == 0) + mask &= ~(uint)PermissionMask.Transfer; + if ((item.CurrentPermissions & (uint)PermissionMask.Modify) == 0) + mask &= ~(uint)PermissionMask.Modify; + } + return mask; + } + + public void ApplyNextOwnerPermissions() + { + foreach (TaskInventoryItem item in m_items.Values) + { + if (item.InvType == 6 && (item.CurrentPermissions & 7) != 0) + { + if ((item.CurrentPermissions & ((uint)PermissionMask.Copy >> 13)) == 0) + item.CurrentPermissions &= ~(uint)PermissionMask.Copy; + if ((item.CurrentPermissions & ((uint)PermissionMask.Transfer >> 13)) == 0) + item.CurrentPermissions &= ~(uint)PermissionMask.Transfer; + if ((item.CurrentPermissions & ((uint)PermissionMask.Modify >> 13)) == 0) + item.CurrentPermissions &= ~(uint)PermissionMask.Modify; + item.CurrentPermissions |= 8; + } + item.CurrentPermissions &= item.NextPermissions; + item.BasePermissions &= item.NextPermissions; + item.EveryonePermissions &= item.NextPermissions; + } + + m_part.TriggerScriptChangedEvent(Changed.OWNER); + } + + public void ApplyGodPermissions(uint perms) + { + foreach (TaskInventoryItem item in m_items.Values) + { + item.CurrentPermissions = perms; + item.BasePermissions = perms; + } + } + + public bool ContainsScripts() + { + foreach (TaskInventoryItem item in m_items.Values) + { + if (item.InvType == 10) + { + return true; + } + } + return false; + } + + public List GetInventoryList() + { + List ret = new List(); + + foreach (TaskInventoryItem item in m_items.Values) + ret.Add(item.ItemID); + + return ret; + } + + public string[] GetScriptAssemblies() + { + IScriptModule[] engines = m_part.ParentGroup.Scene.RequestModuleInterfaces(); + + List ret = new List(); + + foreach (TaskInventoryItem item in m_items.Values) + { + if (item.InvType == 10) + { + foreach (IScriptModule e in engines) + { + string n = e.GetAssemblyName(item.ItemID); + if (n != "") + { + if (!ret.Contains(n)) + ret.Add(n); + break; + } + } + } + } + return ret.ToArray(); + } + + public Dictionary GetScriptStates() + { + IScriptModule[] engines = m_part.ParentGroup.Scene.RequestModuleInterfaces(); + Dictionary ret = new Dictionary(); + + foreach (TaskInventoryItem item in m_items.Values) + { + if (item.InvType == 10) + { + foreach (IScriptModule e in engines) + { + string n = e.GetXMLState(item.ItemID); + if (n != "") + { + if (!ret.ContainsKey(item.ItemID)) + ret[item.ItemID] = n; + break; + } + } + } + } + return ret; + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs new file mode 100644 index 0000000..36cd862 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -0,0 +1,3649 @@ +/* + * 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 OpenSim 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 System.Runtime.Serialization; +using System.Security.Permissions; +using OpenMetaverse; +using OpenMetaverse.Packets; +using log4net; +using OpenSim.Framework; +using OpenSim.Framework.Client; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes.Types; +using OpenSim.Region.Physics.Manager; +using OSD = OpenMetaverse.StructuredData.OSD; + +namespace OpenSim.Region.Framework.Scenes +{ + enum ScriptControlled : uint + { + CONTROL_ZERO = 0, + CONTROL_FWD = 1, + CONTROL_BACK = 2, + CONTROL_LEFT = 4, + CONTROL_RIGHT = 8, + CONTROL_UP = 16, + CONTROL_DOWN = 32, + CONTROL_ROT_LEFT = 256, + CONTROL_ROT_RIGHT = 512, + CONTROL_LBUTTON = 268435456, + CONTROL_ML_LBUTTON = 1073741824 + } + + struct ScriptControllers + { + public UUID itemID; + public uint objID; + public ScriptControlled ignoreControls; + public ScriptControlled eventControls; + } + + [Serializable] + public class ScenePresence : EntityBase, ISerializable + { +// ~ScenePresence() +// { +// System.Console.WriteLine("[ScenePresence] Destructor called"); +// } + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static byte[] DefaultTexture; + + public UUID currentParcelUUID = UUID.Zero; + private AnimationSet m_animations = new AnimationSet(); + private Dictionary scriptedcontrols = new Dictionary(); + private ScriptControlled IgnoredControls = ScriptControlled.CONTROL_ZERO; + private ScriptControlled LastCommands = ScriptControlled.CONTROL_ZERO; + private bool MouseDown = false; + private SceneObjectGroup proxyObjectGroup = null; + //private SceneObjectPart proxyObjectPart = null; + + public Vector3 lastKnownAllowedPosition = new Vector3(); + public bool sentMessageAboutRestrictedParcelFlyingDown = false; + + private bool m_updateflag = false; + private byte m_movementflag = 0; + private readonly List m_forcesList = new List(); + private short m_updateCount = 0; + private uint m_requestedSitTargetID = 0; + private UUID m_requestedSitTargetUUID = UUID.Zero; + + private bool m_startAnimationSet = false; + + private Vector3 m_requestedSitOffset = new Vector3(); + + private Vector3 m_LastFinitePos = new Vector3(); + + private float m_sitAvatarHeight = 2.0f; + + // experimentally determined "fudge factor" to make sit-target positions + // the same as in SecondLife. Fudge factor was tested for 36 different + // test cases including prims of type box, sphere, cylinder, and torus, + // with varying parameters for sit target location, prim size, prim + // rotation, prim cut, prim twist, prim taper, and prim shear. See mantis + // issue #1716 + private static readonly Vector3 m_sitTargetCorrectionOffset = new Vector3(0.1f, 0.0f, 0.3f); + private float m_godlevel = 0; + + private bool m_invulnerable = true; + + private Vector3 m_LastChildAgentUpdatePosition = new Vector3(); + + private int m_perfMonMS = 0; + + private bool m_setAlwaysRun = false; + + private Quaternion m_bodyRot= Quaternion.Identity; + + public bool IsRestrictedToRegion = false; + + public string JID = string.Empty; + + // Agent moves with a PID controller causing a force to be exerted. + private bool m_newForce = false; + private bool m_newCoarseLocations = true; + private float m_health = 100f; + + private Vector3 m_lastVelocity = Vector3.Zero; + + // Default AV Height + private float m_avHeight = 127.0f; + + protected RegionInfo m_regionInfo; + protected ulong crossingFromRegion = 0; + + private readonly Vector3[] Dir_Vectors = new Vector3[6]; + + /// + /// The avatar position last sent to clients + /// + private Vector3 lastPhysPos = Vector3.Zero; + + /// + /// The avatar body rotation last sent to clients + /// + private Quaternion lastPhysRot = Quaternion.Identity; + + // Position of agent's camera in world (region cordinates) + protected Vector3 m_CameraCenter = Vector3.Zero; + + // Use these three vectors to figure out what the agent is looking at + // Convert it to a Matrix and/or Quaternion + protected Vector3 m_CameraAtAxis = Vector3.Zero; + protected Vector3 m_CameraLeftAxis = Vector3.Zero; + protected Vector3 m_CameraUpAxis = Vector3.Zero; + private uint m_AgentControlFlags = 0; + private Quaternion m_headrotation = Quaternion.Identity; + private byte m_state = 0; + + //Reuse the Vector3 instead of creating a new one on the UpdateMovement method + private Vector3 movementvector = Vector3.Zero; + + private bool m_autopilotMoving = false; + private Vector3 m_autoPilotTarget = Vector3.Zero; + private bool m_sitAtAutoTarget = false; + + private string m_nextSitAnimation = String.Empty; + + //PauPaw:Proper PID Controler for autopilot************ + private bool m_moveToPositionInProgress = false; + private Vector3 m_moveToPositionTarget = Vector3.Zero; + //private int m_moveToPositionStateStatus = 0; + //***************************************************** + + // Agent's Draw distance. + protected float m_DrawDistance = 0f; + + protected AvatarAppearance m_appearance; + + protected List m_attachments = new List(); + + // neighbouring regions we have enabled a child agent in + // holds the seed cap for the child agent in that region + private Dictionary m_knownChildRegions = new Dictionary(); + + /// + /// Implemented Control Flags + /// + private enum Dir_ControlFlags + { + DIR_CONTROL_FLAG_FORWARD = AgentManager.ControlFlags.AGENT_CONTROL_AT_POS, + DIR_CONTROL_FLAG_BACK = AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG, + DIR_CONTROL_FLAG_LEFT = AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS, + DIR_CONTROL_FLAG_RIGHT = AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG, + DIR_CONTROL_FLAG_UP = AgentManager.ControlFlags.AGENT_CONTROL_UP_POS, + DIR_CONTROL_FLAG_DOWN = AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG, + DIR_CONTROL_FLAG_DOWN_NUDGE = AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG + } + + /// + /// Position at which a significant movement was made + /// + private Vector3 posLastSignificantMove = new Vector3(); + + private UpdateQueue m_partsUpdateQueue = new UpdateQueue(); + private Queue m_pendingObjects = null; + + private Dictionary m_updateTimes = new Dictionary(); + + // For teleports and crossings callbacks + string m_callbackURI; + ulong m_rootRegionHandle; + + #region Properties + + /// + /// Physical scene representation of this Avatar. + /// + public PhysicsActor PhysicsActor + { + set { m_physicsActor = value; } + get { return m_physicsActor; } + } + + public byte MovementFlag + { + set { m_movementflag = value; } + get { return m_movementflag; } + } + + public bool Updated + { + set { m_updateflag = value; } + get { return m_updateflag; } + } + + public bool Invulnerable + { + set { m_invulnerable = value; } + get { return m_invulnerable; } + } + + public float GodLevel + { + get { return m_godlevel; } + } + + private readonly ulong m_regionHandle; + + public ulong RegionHandle + { + get { return m_regionHandle; } + } + + public Vector3 CameraPosition + { + get { return m_CameraCenter; } + } + + public Quaternion CameraRotation + { + get { return Util.Axes2Rot(m_CameraAtAxis, m_CameraLeftAxis, m_CameraUpAxis); } + } + + public Vector3 Lookat + { + get + { + Vector3 a = new Vector3(m_CameraAtAxis.X, m_CameraAtAxis.Y, 0); + + if (a == Vector3.Zero) + return a; + + return Util.GetNormalizedVector(a); + } + } + + private readonly string m_firstname; + + public string Firstname + { + get { return m_firstname; } + } + + private readonly string m_lastname; + + public string Lastname + { + get { return m_lastname; } + } + + private string m_grouptitle; + + public string Grouptitle + { + get { return m_grouptitle; } + set { m_grouptitle = value; } + } + + public float DrawDistance + { + get { return m_DrawDistance; } + } + + protected bool m_allowMovement = true; + + public bool AllowMovement + { + get { return m_allowMovement; } + set { m_allowMovement = value; } + } + + public bool SetAlwaysRun + { + get + { + if (PhysicsActor != null) + { + return PhysicsActor.SetAlwaysRun; + } + else + { + return m_setAlwaysRun; + } + } + set + { + m_setAlwaysRun = value; + if (PhysicsActor != null) + { + PhysicsActor.SetAlwaysRun = value; + } + } + } + + public byte State + { + get { return m_state; } + set { m_state = value; } + } + + public uint AgentControlFlags + { + get { return m_AgentControlFlags; } + set { m_AgentControlFlags = value; } + } + + /// + /// This works out to be the ClientView object associated with this avatar, or it's client connection manager + /// + private IClientAPI m_controllingClient; + + protected PhysicsActor m_physicsActor; + + /// + /// The client controlling this presence + /// + public IClientAPI ControllingClient + { + get { return m_controllingClient; } + } + + public IClientCore ClientView + { + get { return (IClientCore) m_controllingClient; } + } + + protected Vector3 m_parentPosition = new Vector3(); + public Vector3 ParentPosition + { + get { return m_parentPosition; } + set { m_parentPosition = value; } + } + + /// + /// Absolute position of this avatar in 'region cordinates' + /// + public override Vector3 AbsolutePosition + { + get + { + if (m_physicsActor != null) + { + m_pos.X = m_physicsActor.Position.X; + m_pos.Y = m_physicsActor.Position.Y; + m_pos.Z = m_physicsActor.Position.Z; + } + + return m_parentPosition + m_pos; + } + set + { + if (m_physicsActor != null) + { + try + { + lock (m_scene.SyncRoot) + { + m_physicsActor.Position = new PhysicsVector(value.X, value.Y, value.Z); + } + } + catch (Exception e) + { + Console.WriteLine("[SCENEPRESENCE]: ABSOLUTE POSITION " + e.Message); + } + } + + m_pos = value; + m_parentPosition = new Vector3(0, 0, 0); + } + } + + /// + /// Current velocity of the avatar. + /// + public override Vector3 Velocity + { + get + { + if (m_physicsActor != null) + { + m_velocity.X = m_physicsActor.Velocity.X; + m_velocity.Y = m_physicsActor.Velocity.Y; + m_velocity.Z = m_physicsActor.Velocity.Z; + } + + return m_velocity; + } + set + { + //m_log.DebugFormat("In {0} setting velocity of {1} to {2}", m_scene.RegionInfo.RegionName, Name, value); + + if (m_physicsActor != null) + { + try + { + lock (m_scene.SyncRoot) + { + m_physicsActor.Velocity = new PhysicsVector(value.X, value.Y, value.Z); + } + } + catch (Exception e) + { + Console.WriteLine("[SCENEPRESENCE]: VELOCITY " + e.Message); + } + } + + m_velocity = value; + } + } + + /// + /// If this is true, agent doesn't have a representation in this scene. + /// this is an agent 'looking into' this scene from a nearby scene(region) + /// + /// if False, this agent has a representation in this scene + /// + private bool m_isChildAgent = true; + + public bool IsChildAgent + { + get { return m_isChildAgent; } + set { m_isChildAgent = value; } + } + + private uint m_parentID = 0; + + public uint ParentID + { + get { return m_parentID; } + set { m_parentID = value; } + } + public float Health + { + get { return m_health; } + set { m_health = value; } + } + + /// + /// These are the region handles known by the avatar. + /// + public List KnownChildRegionHandles + { + get + { + if (m_knownChildRegions.Count == 0) + return new List(); + else + return new List(m_knownChildRegions.Keys); + } + } + + public Dictionary KnownRegions + { + get { return m_knownChildRegions; } + set + { + m_knownChildRegions = value; + } + } + + public void AdjustKnownSeeds() + { + Dictionary seeds = Scene.CapsModule.GetChildrenSeeds(UUID); + List old = new List(); + foreach (ulong handle in seeds.Keys) + { + uint x, y; + Utils.LongToUInts(handle, out x, out y); + x = x / Constants.RegionSize; + y = y / Constants.RegionSize; + if (Util.IsOutsideView(x, Scene.RegionInfo.RegionLocX, y, Scene.RegionInfo.RegionLocY)) + { + old.Add(handle); + } + } + DropOldNeighbours(old); + Scene.CapsModule.SetChildrenSeed(UUID, seeds); + KnownRegions = seeds; + //Console.WriteLine(" ++++++++++AFTER+++++++++++++ "); + //DumpKnownRegions(); + } + + public void DumpKnownRegions() + { + Console.WriteLine("================ KnownRegions {0} ================", Scene.RegionInfo.RegionName); + foreach (KeyValuePair kvp in KnownRegions) + { + uint x, y; + Utils.LongToUInts(kvp.Key, out x, out y); + x = x / Constants.RegionSize; + y = y / Constants.RegionSize; + Console.WriteLine(" >> {0}, {1}: {2}", x, y, kvp.Value); + } + } + + public AnimationSet Animations + { + get { return m_animations; } + } + + #endregion + + #region Constructor(s) + + private ScenePresence(IClientAPI client, Scene world, RegionInfo reginfo) + { + m_regionHandle = reginfo.RegionHandle; + m_controllingClient = client; + m_firstname = m_controllingClient.FirstName; + m_lastname = m_controllingClient.LastName; + m_name = String.Format("{0} {1}", m_firstname, m_lastname); + + m_scene = world; + m_uuid = client.AgentId; + m_regionInfo = reginfo; + m_localId = m_scene.AllocateLocalId(); + + IGroupsModule gm = m_scene.RequestModuleInterface(); + if (gm != null) + m_grouptitle = gm.GetGroupTitle(m_uuid); + + AbsolutePosition = m_controllingClient.StartPos; + AdjustKnownSeeds(); + + TrySetMovementAnimation("STAND"); // TODO: I think, this won't send anything, as we are still a child here... + + // we created a new ScenePresence (a new child agent) in a fresh region. + // Request info about all the (root) agents in this region + // Note: This won't send data *to* other clients in that region (children don't send) + SendInitialFullUpdateToAllClients(); + + RegisterToEvents(); + SetDirectionVectors(); + + CachedUserInfo userInfo = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(m_uuid); + if (userInfo != null) + userInfo.OnItemReceived += ItemReceived; + } + + public ScenePresence(IClientAPI client, Scene world, RegionInfo reginfo, byte[] visualParams, + AvatarWearable[] wearables) + : this(client, world, reginfo) + { + m_appearance = new AvatarAppearance(m_uuid, wearables, visualParams); + } + + public ScenePresence(IClientAPI client, Scene world, RegionInfo reginfo, AvatarAppearance appearance) + : this(client, world, reginfo) + { + m_appearance = appearance; + } + + public void RegisterToEvents() + { + m_controllingClient.OnRequestWearables += SendWearables; + m_controllingClient.OnSetAppearance += SetAppearance; + m_controllingClient.OnCompleteMovementToRegion += CompleteMovement; + m_controllingClient.OnCompleteMovementToRegion += SendInitialData; + m_controllingClient.OnAgentUpdate += HandleAgentUpdate; + m_controllingClient.OnAgentRequestSit += HandleAgentRequestSit; + m_controllingClient.OnAgentSit += HandleAgentSit; + m_controllingClient.OnSetAlwaysRun += HandleSetAlwaysRun; + m_controllingClient.OnStartAnim += HandleStartAnim; + m_controllingClient.OnStopAnim += HandleStopAnim; + m_controllingClient.OnForceReleaseControls += HandleForceReleaseControls; + m_controllingClient.OnAutoPilotGo += DoAutoPilot; + m_controllingClient.AddGenericPacketHandler("autopilot", DoMoveToPosition); + + // ControllingClient.OnChildAgentStatus += new StatusChange(this.ChildStatusChange); + // ControllingClient.OnStopMovement += new GenericCall2(this.StopMovement); + } + + private void SetDirectionVectors() + { + Dir_Vectors[0] = new Vector3(1, 0, 0); //FORWARD + Dir_Vectors[1] = new Vector3(-1, 0, 0); //BACK + Dir_Vectors[2] = new Vector3(0, 1, 0); //LEFT + Dir_Vectors[3] = new Vector3(0, -1, 0); //RIGHT + Dir_Vectors[4] = new Vector3(0, 0, 1); //UP + Dir_Vectors[5] = new Vector3(0, 0, -1); //DOWN + Dir_Vectors[5] = new Vector3(0, 0, -0.5f); //DOWN_Nudge + } + + #endregion + + /// + /// Add the part to the queue of parts for which we need to send an update to the client + /// + /// + public void QueuePartForUpdate(SceneObjectPart part) + { + //if (InterestList.Contains(part.ParentGroup)) + //{ + lock (m_partsUpdateQueue) + { + m_partsUpdateQueue.Enqueue(part); + } + // } + } + + public uint GenerateClientFlags(UUID ObjectID) + { + return m_scene.Permissions.GenerateClientFlags(m_uuid, ObjectID); + } + + /// + /// Send updates to the client about prims which have been placed on the update queue. We don't + /// necessarily send updates for all the parts on the queue, e.g. if an updates with a more recent + /// timestamp has already been sent. + /// + public void SendPrimUpdates() + { + // if (m_scene.QuadTree.GetNodeID(this.AbsolutePosition.X, this.AbsolutePosition.Y) != m_currentQuadNode) + //{ + // this.UpdateQuadTreeNode(); + //this.RefreshQuadObject(); + //} + m_perfMonMS = System.Environment.TickCount; + + if (m_pendingObjects == null) + { + if (!m_isChildAgent || m_scene.m_seeIntoRegionFromNeighbor) + { + m_pendingObjects = new Queue(); + + List ents = new List(m_scene.Entities); + if (!m_isChildAgent) // Proximity sort makes no sense for + { // Child agents + ents.Sort(delegate(EntityBase a, EntityBase b) + { + return Vector3.Distance(AbsolutePosition, a.AbsolutePosition).CompareTo(Vector3.Distance(AbsolutePosition, b.AbsolutePosition)); + }); + } + + foreach (EntityBase e in ents) + { + if (e is SceneObjectGroup) + m_pendingObjects.Enqueue((SceneObjectGroup)e); + } + } + } + + while (m_pendingObjects.Count > 0 && m_partsUpdateQueue.Count < 60) + { + SceneObjectGroup g = m_pendingObjects.Dequeue(); + + // This is where we should check for draw distance + // do culling and stuff. Problem with that is that until + // we recheck in movement, that won't work right. + // So it's not implemented now. + // + + // Don't even queue if we have sent this one + // + if (!m_updateTimes.ContainsKey(g.UUID)) + g.ScheduleFullUpdateToAvatar(this); + } + + int updateCount = 0; + + while (m_partsUpdateQueue.Count > 0) + { + SceneObjectPart part = m_partsUpdateQueue.Dequeue(); + + if (part.ParentGroup == null || part.ParentGroup.IsDeleted) + continue; + + if (m_updateTimes.ContainsKey(part.UUID)) + { + ScenePartUpdate update = m_updateTimes[part.UUID]; + + // We deal with the possibility that two updates occur at + // the same unix time at the update point itself. + + if ((update.LastFullUpdateTime < part.TimeStampFull) || + part.IsAttachment) + { +// m_log.DebugFormat( +// "[SCENE PRESENCE]: Fully updating prim {0}, {1} - part timestamp {2}", +// part.Name, part.UUID, part.TimeStampFull); + + part.SendFullUpdate(ControllingClient, + GenerateClientFlags(part.UUID)); + + // We'll update to the part's timestamp rather than + // the current time to avoid the race condition + // whereby the next tick occurs while we are doing + // this update. If this happened, then subsequent + // updates which occurred on the same tick or the + // next tick of the last update would be ignored. + + update.LastFullUpdateTime = part.TimeStampFull; + + updateCount++; + } + else if (update.LastTerseUpdateTime <= part.TimeStampTerse) + { +// m_log.DebugFormat( +// "[SCENE PRESENCE]: Tersely updating prim {0}, {1} - part timestamp {2}", +// part.Name, part.UUID, part.TimeStampTerse); + + part.SendTerseUpdateToClient(ControllingClient); + + update.LastTerseUpdateTime = part.TimeStampTerse; + updateCount++; + } + } + else + { + //never been sent to client before so do full update + ScenePartUpdate update = new ScenePartUpdate(); + update.FullID = part.UUID; + update.LastFullUpdateTime = part.TimeStampFull; + m_updateTimes.Add(part.UUID, update); + + // Attachment handling + // + if (part.ParentGroup.RootPart.Shape.PCode == 9 && part.ParentGroup.RootPart.Shape.State != 0) + { + if (part != part.ParentGroup.RootPart) + continue; + + part.ParentGroup.SendFullUpdateToClient(ControllingClient); + continue; + } + + part.SendFullUpdate(ControllingClient, + GenerateClientFlags(part.UUID)); + updateCount++; + } + + if (updateCount > 60) + break; + } + + m_scene.AddAgentTime(System.Environment.TickCount - m_perfMonMS); + } + + #region Status Methods + + /// + /// This turns a child agent, into a root agent + /// This is called when an agent teleports into a region, or if an + /// agent crosses into this region from a neighbor over the border + /// + public void MakeRootAgent(Vector3 pos, bool isFlying) + { + m_log.DebugFormat( + "[SCENE]: Upgrading child to root agent for {0} in {1}", + Name, m_scene.RegionInfo.RegionName); + + //m_log.DebugFormat("[SCENE]: known regions in {0}: {1}", Scene.RegionInfo.RegionName, KnownChildRegionHandles.Count); + + IGroupsModule gm = m_scene.RequestModuleInterface(); + if (gm != null) + m_grouptitle = gm.GetGroupTitle(m_uuid); + + m_scene.SetRootAgentScene(m_uuid); + + // Moved this from SendInitialData to ensure that m_appearance is initialized + // before the inventory is processed in MakeRootAgent. This fixes a race condition + // related to the handling of attachments + m_scene.GetAvatarAppearance(m_controllingClient, out m_appearance); + + if (pos.X < 0 || pos.X > Constants.RegionSize || pos.Y < 0 || pos.Y > Constants.RegionSize || pos.Z < 0) + { + Vector3 emergencyPos = new Vector3(128, 128, 128); + + m_log.WarnFormat( + "[SCENE]: MakeRootAgent() was given an illegal position of {0} for avatar {1}, {2}. Substituting {3}", + pos, Name, UUID, emergencyPos); + + pos = emergencyPos; + } + + m_isChildAgent = false; + + float localAVHeight = 1.56f; + if (m_avHeight != 127.0f) + { + localAVHeight = m_avHeight; + } + + float posZLimit = (float)m_scene.GetLandHeight((int)pos.X, (int)pos.Y); + float newPosZ = posZLimit + localAVHeight / 2; + if (posZLimit >= (pos.Z - (localAVHeight / 2)) && !(Single.IsInfinity(newPosZ) || Single.IsNaN(newPosZ))) + { + pos.Z = newPosZ; + } + AbsolutePosition = pos; + + AddToPhysicalScene(); + m_physicsActor.Flying = isFlying; + + // Don't send an animation pack here, since on a region crossing this will sometimes cause a flying + // avatar to return to the standing position in mid-air. On login it looks like this is being sent + // elsewhere anyway + //SendAnimPack(); + + m_scene.SwapRootAgentCount(false); + m_scene.CommsManager.UserProfileCacheService.RequestInventoryForUser(m_uuid); + m_scene.CapsModule.AddCapsHandler(m_uuid); + + // On the next prim update, all objects will be sent + // + m_pendingObjects = null; + + m_scene.EventManager.TriggerOnMakeRootAgent(this); + } + + /// + /// This turns a root agent into a child agent + /// when an agent departs this region for a neighbor, this gets called. + /// + /// It doesn't get called for a teleport. Reason being, an agent that + /// teleports out may not end up anywhere near this region + /// + public void MakeChildAgent() + { + m_animations.Clear(); + +// m_log.DebugFormat( +// "[SCENEPRESENCE]: Downgrading root agent {0}, {1} to a child agent in {2}", +// Name, UUID, m_scene.RegionInfo.RegionName); + + // Don't zero out the velocity since this can cause problems when an avatar is making a region crossing, + // depending on the exact timing. This shouldn't matter anyway since child agent positions are not updated. + //Velocity = new Vector3(0, 0, 0); + + m_isChildAgent = true; + m_scene.SwapRootAgentCount(true); + RemoveFromPhysicalScene(); + m_scene.EventManager.TriggerOnMakeChildAgent(this); + } + + /// + /// Removes physics plugin scene representation of this agent if it exists. + /// + private void RemoveFromPhysicalScene() + { + if (PhysicsActor != null) + { + m_scene.PhysicsScene.RemoveAvatar(PhysicsActor); + m_physicsActor.OnRequestTerseUpdate -= SendTerseUpdateToAllClients; + m_physicsActor.UnSubscribeEvents(); + m_physicsActor.OnCollisionUpdate -= PhysicsCollisionUpdate; + PhysicsActor = null; + } + } + + /// + /// + /// + /// + public void Teleport(Vector3 pos) + { + RemoveFromPhysicalScene(); + Velocity = new Vector3(0, 0, 0); + AbsolutePosition = pos; + AddToPhysicalScene(); + SendTerseUpdateToAllClients(); + } + + /// + /// + /// + public void StopMovement() + { + } + + public void StopFlying() + { + // It turns out to get the agent to stop flying, you have to feed it stop flying velocities + // and send a full object update. + // There's no message to send the client to tell it to stop flying + + // Add 1/6 the avatar's height to it's position so it doesn't shoot into the air + // when the avatar stands up + + if (m_avHeight != 127.0f) + { + AbsolutePosition = AbsolutePosition + new Vector3(0, 0, (m_avHeight / 6f)); + } + else + { + AbsolutePosition = AbsolutePosition + new Vector3(0, 0, (1.56f / 6f)); + } + TrySetMovementAnimation("LAND"); + SendFullUpdateToAllClients(); + } + + public void AddNeighbourRegion(ulong regionHandle, string cap) + { + lock (m_knownChildRegions) + { + if (!m_knownChildRegions.ContainsKey(regionHandle)) + { + uint x, y; + Utils.LongToUInts(regionHandle, out x, out y); + m_knownChildRegions.Add(regionHandle, cap); + } + } + } + + public void RemoveNeighbourRegion(ulong regionHandle) + { + lock (m_knownChildRegions) + { + if (m_knownChildRegions.ContainsKey(regionHandle)) + { + m_knownChildRegions.Remove(regionHandle); + //Console.WriteLine(" !!! removing known region {0} in {1}. Count = {2}", regionHandle, Scene.RegionInfo.RegionName, m_knownChildRegions.Count); + } + } + } + + public void DropOldNeighbours(List oldRegions) + { + foreach (ulong handle in oldRegions) + { + RemoveNeighbourRegion(handle); + Scene.CapsModule.DropChildSeed(UUID, handle); + } + } + + public List GetKnownRegionList() + { + return new List(m_knownChildRegions.Keys); + } + + #endregion + + #region Event Handlers + + /// + /// Sets avatar height in the phyiscs plugin + /// + internal void SetHeight(float height) + { + m_avHeight = height; + if (PhysicsActor != null) + { + PhysicsVector SetSize = new PhysicsVector(0.45f, 0.6f, m_avHeight); + PhysicsActor.Size = SetSize; + } + } + + /// + /// Complete Avatar's movement into the region + /// + public void CompleteMovement() + { + Vector3 look = Velocity; + if ((look.X == 0) && (look.Y == 0) && (look.Z == 0)) + { + look = new Vector3(0.99f, 0.042f, 0); + } + + m_controllingClient.MoveAgentIntoRegion(m_regionInfo, AbsolutePosition, look); + + if (m_isChildAgent) + { + m_isChildAgent = false; + + MakeRootAgent(AbsolutePosition, false); + + if ((m_callbackURI != null) && !m_callbackURI.Equals("")) + { + m_log.DebugFormat("[SCENE PRESENCE]: Releasing agent in URI {0}", m_callbackURI); + Scene.SendReleaseAgent(m_rootRegionHandle, UUID, m_callbackURI); + m_callbackURI = null; + } + + //m_log.DebugFormat("Completed movement"); + } + } + + /// + /// This is the event handler for client movement. If a client is moving, this event is triggering. + /// + public void HandleAgentUpdate(IClientAPI remoteClient, AgentUpdateArgs agentData) + { + //if (m_isChildAgent) + //{ + // // Console.WriteLine("DEBUG: HandleAgentUpdate: child agent"); + // return; + //} + + // Must check for standing up even when PhysicsActor is null, + // since sitting currently removes avatar from physical scene + //m_log.Debug("agentPos:" + AbsolutePosition.ToString()); + + // This is irritating. Really. + if (!AbsolutePosition.IsFinite()) + { + RemoveFromPhysicalScene(); + m_log.Error("[AVATAR]: NonFinite Avatar position detected... Reset Position. Mantis this please. Error# 9999902"); + + m_pos = m_LastFinitePos; + if (!m_pos.IsFinite()) + { + m_pos.X = 127f; + m_pos.Y = 127f; + m_pos.Z = 127f; + m_log.Error("[AVATAR]: NonFinite Avatar position detected... Reset Position. Mantis this please. Error# 9999903"); + } + + AddToPhysicalScene(); + } + else + { + m_LastFinitePos = m_pos; + } + //m_physicsActor.AddForce(new PhysicsVector(999999999, 99999999, 999999999999999), true); + + + //ILandObject land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y); + //if (land != null) + //{ + //if (land.landData.landingType == (byte)1 && land.landData.userLocation != Vector3.Zero) + //{ + // agent.startpos = land.landData.userLocation; + //} + //} + + m_perfMonMS = System.Environment.TickCount; + + uint flags = agentData.ControlFlags; + Quaternion bodyRotation = agentData.BodyRotation; + + // Camera location in world. We'll need to raytrace + // from this location from time to time. + m_CameraCenter = agentData.CameraCenter; + + // Use these three vectors to figure out what the agent is looking at + // Convert it to a Matrix and/or Quaternion + m_CameraAtAxis = agentData.CameraAtAxis; + m_CameraLeftAxis = agentData.CameraLeftAxis; + m_CameraUpAxis = agentData.CameraUpAxis; + + // The Agent's Draw distance setting + m_DrawDistance = agentData.Far; + + if ((flags & (uint) AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) != 0) + { + StandUp(); + } + lock (scriptedcontrols) + { + if (scriptedcontrols.Count > 0) + { + SendControlToScripts(flags); + flags = this.RemoveIgnoredControls(flags, IgnoredControls); + + } + } + + if (PhysicsActor == null) + { + return; + } + + if (m_autopilotMoving) + CheckAtSitTarget(); + + if ((flags & (uint) AgentManager.ControlFlags.AGENT_CONTROL_SIT_ON_GROUND) != 0) + { + // TODO: This doesn't prevent the user from walking yet. + // Setting parent ID would fix this, if we knew what value + // to use. Or we could add a m_isSitting variable. + + TrySetMovementAnimation("SIT_GROUND_CONSTRAINED"); + } + // In the future, these values might need to go global. + // Here's where you get them. + + m_AgentControlFlags = flags; + m_headrotation = agentData.HeadRotation; + m_state = agentData.State; + + if (m_allowMovement) + { + int i = 0; + bool update_movementflag = false; + bool update_rotation = false; + bool DCFlagKeyPressed = false; + Vector3 agent_control_v3 = new Vector3(0, 0, 0); + Quaternion q = bodyRotation; + if (PhysicsActor != null) + { + bool oldflying = PhysicsActor.Flying; + + PhysicsActor.Flying = ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0); + + if (PhysicsActor.Flying != oldflying) + { + update_movementflag = true; + } + } + + if (q != m_bodyRot) + { + m_bodyRot = q; + update_rotation = true; + } + + if (m_parentID == 0) + { + bool bAllowUpdateMoveToPosition = false; + bool bResetMoveToPosition = false; + foreach (Dir_ControlFlags DCF in Enum.GetValues(typeof (Dir_ControlFlags))) + { + if ((flags & (uint) DCF) != 0) + { + bResetMoveToPosition = true; + DCFlagKeyPressed = true; + try + { + agent_control_v3 += Dir_Vectors[i]; + } + catch (IndexOutOfRangeException) + { + // Why did I get this? + } + + if ((m_movementflag & (uint) DCF) == 0) + { + m_movementflag += (byte) (uint) DCF; + update_movementflag = true; + } + } + else + { + if ((m_movementflag & (uint) DCF) != 0) + { + m_movementflag -= (byte) (uint) DCF; + update_movementflag = true; + } + else + { + bAllowUpdateMoveToPosition = true; + } + } + i++; + } + + //Paupaw:Do Proper PID for Autopilot here + if (bResetMoveToPosition) + { + m_moveToPositionTarget = Vector3.Zero; + m_moveToPositionInProgress = false; + update_movementflag = true; + bAllowUpdateMoveToPosition = false; + } + + if (bAllowUpdateMoveToPosition && (m_moveToPositionInProgress && !m_autopilotMoving)) + { + //Check the error term of the current position in relation to the target position + if (Util.GetDistanceTo(AbsolutePosition, m_moveToPositionTarget) <= 1.5) + { + // we are close enough to the target + m_moveToPositionTarget = Vector3.Zero; + m_moveToPositionInProgress = false; + update_movementflag = true; + } + else + { + try + { + // move avatar in 2D at one meter/second towards target, in avatar coordinate frame. + // This movement vector gets added to the velocity through AddNewMovement(). + // Theoretically we might need a more complex PID approach here if other + // unknown forces are acting on the avatar and we need to adaptively respond + // to such forces, but the following simple approach seems to works fine. + Vector3 LocalVectorToTarget3D = + (m_moveToPositionTarget - AbsolutePosition) // vector from cur. pos to target in global coords + * Matrix4.CreateFromQuaternion(Quaternion.Inverse(bodyRotation)); // change to avatar coords + // Ignore z component of vector + Vector3 LocalVectorToTarget2D = new Vector3((float)(LocalVectorToTarget3D.X), (float)(LocalVectorToTarget3D.Y), 0f); + LocalVectorToTarget2D.Normalize(); + agent_control_v3 += LocalVectorToTarget2D; + + // update avatar movement flags. the avatar coordinate system is as follows: + // + // +X (forward) + // + // ^ + // | + // | + // | + // | + // (left) +Y <--------o--------> -Y + // avatar + // | + // | + // | + // | + // v + // -X + // + + // based on the above avatar coordinate system, classify the movement into + // one of left/right/back/forward. + if (LocalVectorToTarget2D.Y > 0)//MoveLeft + { + m_movementflag += (byte)(uint)Dir_ControlFlags.DIR_CONTROL_FLAG_LEFT; + update_movementflag = true; + } + else if (LocalVectorToTarget2D.Y < 0) //MoveRight + { + m_movementflag += (byte)(uint)Dir_ControlFlags.DIR_CONTROL_FLAG_RIGHT; + update_movementflag = true; + } + if (LocalVectorToTarget2D.X < 0) //MoveBack + { + m_movementflag += (byte)(uint)Dir_ControlFlags.DIR_CONTROL_FLAG_BACK; + update_movementflag = true; + } + else if (LocalVectorToTarget2D.X > 0) //Move Forward + { + m_movementflag += (byte)(uint)Dir_ControlFlags.DIR_CONTROL_FLAG_FORWARD; + update_movementflag = true; + } + } + catch (Exception) + { + + //Avoid system crash, can be slower but... + } + + } + } + } + + // Cause the avatar to stop flying if it's colliding + // with something with the down arrow pressed. + + // Only do this if we're flying + if (m_physicsActor != null && m_physicsActor.Flying) + { + // Are the landing controls requirements filled? + bool controlland = (((flags & (uint) AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0) || + ((flags & (uint) AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG) != 0)); + + // Are the collision requirements fulfilled? + bool colliding = (m_physicsActor.IsColliding == true); + + if (m_physicsActor.Flying && colliding && controlland) + { + StopFlying(); + } + } + + if (update_movementflag || (update_rotation && DCFlagKeyPressed)) + { +// m_log.DebugFormat("{0} {1}", update_movementflag, (update_rotation && DCFlagKeyPressed)); +// m_log.DebugFormat( +// "In {0} adding velocity to {1} of {2}", m_scene.RegionInfo.RegionName, Name, agent_control_v3); + + AddNewMovement(agent_control_v3, q); + + if (update_movementflag) + UpdateMovementAnimations(); + } + } + + m_scene.EventManager.TriggerOnClientMovement(this); + + m_scene.AddAgentTime(System.Environment.TickCount - m_perfMonMS); + } + + public void DoAutoPilot(uint not_used, Vector3 Pos, IClientAPI remote_client) + { + m_autopilotMoving = true; + m_autoPilotTarget = Pos; + m_sitAtAutoTarget = false; + PrimitiveBaseShape proxy = PrimitiveBaseShape.Default; + //proxy.PCode = (byte)PCode.ParticleSystem; + + proxyObjectGroup = new SceneObjectGroup(UUID, Pos, Rotation, proxy); + proxyObjectGroup.AttachToScene(m_scene); + + // Commented out this code since it could never have executed, but might still be informative. +// if (proxyObjectGroup != null) +// { + proxyObjectGroup.SendGroupFullUpdate(); + remote_client.SendSitResponse(proxyObjectGroup.UUID, Vector3.Zero, Quaternion.Identity, true, Vector3.Zero, Vector3.Zero, false); + m_scene.DeleteSceneObject(proxyObjectGroup, false); +// } +// else +// { +// m_autopilotMoving = false; +// m_autoPilotTarget = Vector3.Zero; +// ControllingClient.SendAlertMessage("Autopilot cancelled"); +// } + } + + public void DoMoveToPosition(Object sender, string method, List args) + { + try + { + float locx = 0f; + float locy = 0f; + float locz = 0f; + uint regionX = 0; + uint regionY = 0; + try + { + Utils.LongToUInts(Scene.RegionInfo.RegionHandle, out regionX, out regionY); + locx = Convert.ToSingle(args[0]) - (float)regionX; + locy = Convert.ToSingle(args[1]) - (float)regionY; + locz = Convert.ToSingle(args[2]); + } + catch (InvalidCastException) + { + m_log.Error("[CLIENT]: Invalid autopilot request"); + return; + } + m_moveToPositionInProgress = true; + m_moveToPositionTarget = new Vector3(locx, locy, locz); + } + catch (Exception ex) + { + //Why did I get this error? + Console.WriteLine("[SCENEPRESENCE]: DoMoveToPosition" + ex.ToString()); + } + } + + private void CheckAtSitTarget() + { + //m_log.Debug("[AUTOPILOT]: " + Util.GetDistanceTo(AbsolutePosition, m_autoPilotTarget).ToString()); + if (Util.GetDistanceTo(AbsolutePosition, m_autoPilotTarget) <= 1.5) + { + if (m_sitAtAutoTarget) + { + SceneObjectPart part = m_scene.GetSceneObjectPart(m_requestedSitTargetUUID); + if (part != null) + { + AbsolutePosition = part.AbsolutePosition; + Velocity = new Vector3(0, 0, 0); + SendFullUpdateToAllClients(); + + //HandleAgentSit(ControllingClient, m_requestedSitTargetUUID); + } + //ControllingClient.SendSitResponse(m_requestedSitTargetID, m_requestedSitOffset, Quaternion.Identity, false, Vector3.Zero, Vector3.Zero, false); + m_requestedSitTargetUUID = UUID.Zero; + } + else + { + //ControllingClient.SendAlertMessage("Autopilot cancelled"); + //SendTerseUpdateToAllClients(); + //PrimitiveBaseShape proxy = PrimitiveBaseShape.Default; + //proxy.PCode = (byte)PCode.ParticleSystem; + ////uint nextUUID = m_scene.NextLocalId; + + //proxyObjectGroup = new SceneObjectGroup(m_scene, m_scene.RegionInfo.RegionHandle, UUID, nextUUID, m_autoPilotTarget, Quaternion.Identity, proxy); + //if (proxyObjectGroup != null) + //{ + //proxyObjectGroup.SendGroupFullUpdate(); + //ControllingClient.SendSitResponse(UUID.Zero, m_autoPilotTarget, Quaternion.Identity, true, Vector3.Zero, Vector3.Zero, false); + //m_scene.DeleteSceneObject(proxyObjectGroup); + //} + } + + m_autoPilotTarget = Vector3.Zero; + m_autopilotMoving = false; + } + } + /// + /// Perform the logic necessary to stand the client up. This method also executes + /// the stand animation. + /// + public void StandUp() + { + if (m_parentID != 0) + { + SceneObjectPart part = m_scene.GetSceneObjectPart(m_parentID); + if (part != null) + { + TaskInventoryDictionary taskIDict = part.TaskInventory; + if (taskIDict != null) + { + lock (taskIDict) + { + foreach (UUID taskID in taskIDict.Keys) + { + UnRegisterControlEventsToScript(LocalId, taskID); + taskIDict[taskID].PermsMask &= ~( + 2048 | //PERMISSION_CONTROL_CAMERA + 4); // PERMISSION_TAKE_CONTROLS + } + } + + } + // Reset sit target. + if (part.GetAvatarOnSitTarget() == UUID) + part.SetAvatarOnSitTarget(UUID.Zero); + + m_parentPosition = part.GetWorldPosition(); + } + + if (m_physicsActor == null) + { + AddToPhysicalScene(); + } + + m_pos += m_parentPosition + new Vector3(0.0f, 0.0f, 2.0f*m_sitAvatarHeight); + m_parentPosition = new Vector3(); + + m_parentID = 0; + SendFullUpdateToAllClients(); + m_requestedSitTargetID = 0; + if (m_physicsActor != null) + { + SetHeight(m_avHeight); + } + } + + TrySetMovementAnimation("STAND"); + } + + private SceneObjectPart FindNextAvailableSitTarget(UUID targetID) + { + SceneObjectPart targetPart = m_scene.GetSceneObjectPart(targetID); + if (targetPart == null) + return null; + + // If the primitive the player clicked on has a sit target and that sit target is not full, that sit target is used. + // If the primitive the player clicked on has no sit target, and one or more other linked objects have sit targets that are not full, the sit target of the object with the lowest link number will be used. + + // Get our own copy of the part array, and sort into the order we want to test + SceneObjectPart[] partArray = targetPart.ParentGroup.GetParts(); + Array.Sort(partArray, delegate(SceneObjectPart p1, SceneObjectPart p2) + { + // we want the originally selected part first, then the rest in link order -- so make the selected part link num (-1) + int linkNum1 = p1==targetPart ? -1 : p1.LinkNum; + int linkNum2 = p2==targetPart ? -1 : p2.LinkNum; + return linkNum1 - linkNum2; + } + ); + + //look for prims with explicit sit targets that are available + foreach (SceneObjectPart part in partArray) + { + // Is a sit target available? + Vector3 avSitOffSet = part.SitTargetPosition; + Quaternion avSitOrientation = part.SitTargetOrientation; + UUID avOnTargetAlready = part.GetAvatarOnSitTarget(); + + bool SitTargetUnOccupied = (!(avOnTargetAlready != UUID.Zero)); + bool SitTargetisSet = + (!(avSitOffSet.X == 0f && avSitOffSet.Y == 0f && avSitOffSet.Z == 0f && avSitOrientation.W == 1f && + avSitOrientation.X == 0f && avSitOrientation.Y == 0f && avSitOrientation.Z == 0f)); + + if (SitTargetisSet && SitTargetUnOccupied) + { + //switch the target to this prim + return part; + } + } + + // no explicit sit target found - use original target + return targetPart; + } + + private void SendSitResponse(IClientAPI remoteClient, UUID targetID, Vector3 offset) + { + bool autopilot = true; + Vector3 pos = new Vector3(); + Quaternion sitOrientation = Quaternion.Identity; + Vector3 cameraEyeOffset = Vector3.Zero; + Vector3 cameraAtOffset = Vector3.Zero; + bool forceMouselook = false; + + //SceneObjectPart part = m_scene.GetSceneObjectPart(targetID); + SceneObjectPart part = FindNextAvailableSitTarget(targetID); + if (part != null) + { + // TODO: determine position to sit at based on scene geometry; don't trust offset from client + // see http://wiki.secondlife.com/wiki/User:Andrew_Linden/Office_Hours/2007_11_06 for details on how LL does it + + // Is a sit target available? + Vector3 avSitOffSet = part.SitTargetPosition; + Quaternion avSitOrientation = part.SitTargetOrientation; + UUID avOnTargetAlready = part.GetAvatarOnSitTarget(); + + bool SitTargetUnOccupied = (!(avOnTargetAlready != UUID.Zero)); + bool SitTargetisSet = + (!(avSitOffSet.X == 0f && avSitOffSet.Y == 0f && avSitOffSet.Z == 0f && avSitOrientation.W == 0f && + avSitOrientation.X == 0f && avSitOrientation.Y == 0f && avSitOrientation.Z == 1f)); + + if (SitTargetisSet && SitTargetUnOccupied) + { + part.SetAvatarOnSitTarget(UUID); + offset = new Vector3(avSitOffSet.X, avSitOffSet.Y, avSitOffSet.Z); + sitOrientation = avSitOrientation; + autopilot = false; + } + + pos = part.AbsolutePosition + offset; + //if (Math.Abs(part.AbsolutePosition.Z - AbsolutePosition.Z) > 1) + //{ + // offset = pos; + //autopilot = false; + //} + if (m_physicsActor != null) + { + // If we're not using the client autopilot, we're immediately warping the avatar to the location + // We can remove the physicsActor until they stand up. + m_sitAvatarHeight = m_physicsActor.Size.Z; + + if (autopilot) + { + if (Util.GetDistanceTo(AbsolutePosition, pos) < 4.5) + { + autopilot = false; + + RemoveFromPhysicalScene(); + AbsolutePosition = pos + new Vector3(0.0f, 0.0f, m_sitAvatarHeight); + } + } + else + { + RemoveFromPhysicalScene(); + } + } + + cameraAtOffset = part.GetCameraAtOffset(); + cameraEyeOffset = part.GetCameraEyeOffset(); + forceMouselook = part.GetForceMouselook(); + } + + ControllingClient.SendSitResponse(targetID, offset, sitOrientation, autopilot, cameraAtOffset, cameraEyeOffset, forceMouselook); + m_requestedSitTargetUUID = targetID; + // This calls HandleAgentSit twice, once from here, and the client calls + // HandleAgentSit itself after it gets to the location + // It doesn't get to the location until we've moved them there though + // which happens in HandleAgentSit :P + m_autopilotMoving = autopilot; + m_autoPilotTarget = pos; + m_sitAtAutoTarget = autopilot; + if (!autopilot) + HandleAgentSit(remoteClient, UUID); + } + + public void HandleAgentRequestSit(IClientAPI remoteClient, UUID agentID, UUID targetID, Vector3 offset) + { + if (m_parentID != 0) + { + StandUp(); + } + m_nextSitAnimation = "SIT"; + + //SceneObjectPart part = m_scene.GetSceneObjectPart(targetID); + SceneObjectPart part = FindNextAvailableSitTarget(targetID); + + if (part != null) + { + if (!String.IsNullOrEmpty(part.SitAnimation)) + { + m_nextSitAnimation = part.SitAnimation; + } + m_requestedSitTargetID = part.LocalId; + m_requestedSitOffset = offset; + } + else + { + + m_log.Warn("Sit requested on unknown object: " + targetID.ToString()); + } + SendSitResponse(remoteClient, targetID, offset); + } + + public void HandleAgentRequestSit(IClientAPI remoteClient, UUID agentID, UUID targetID, Vector3 offset, string sitAnimation) + { + if (m_parentID != 0) + { + StandUp(); + } + if (!String.IsNullOrEmpty(sitAnimation)) + { + m_nextSitAnimation = sitAnimation; + } + else + { + m_nextSitAnimation = "SIT"; + } + + //SceneObjectPart part = m_scene.GetSceneObjectPart(targetID); + SceneObjectPart part = FindNextAvailableSitTarget(targetID); + if (part != null) + { + m_requestedSitTargetID = part.LocalId; + m_requestedSitOffset = offset; + } + else + { + m_log.Warn("Sit requested on unknown object: " + targetID.ToString()); + } + SendSitResponse(remoteClient, targetID, offset); + } + + public void HandleAgentSit(IClientAPI remoteClient, UUID agentID) + { + if (!String.IsNullOrEmpty(m_nextSitAnimation)) + { + HandleAgentSit(remoteClient, agentID, m_nextSitAnimation); + } + else + { + HandleAgentSit(remoteClient, agentID, "SIT"); + } + } + + public void HandleAgentSit(IClientAPI remoteClient, UUID agentID, string sitAnimation) + { + SceneObjectPart part = m_scene.GetSceneObjectPart(m_requestedSitTargetID); + + if (m_sitAtAutoTarget || !m_autopilotMoving) + { + if (part != null) + { + if (part.GetAvatarOnSitTarget() == UUID) + { + Vector3 sitTargetPos = part.SitTargetPosition; + Quaternion sitTargetOrient = part.SitTargetOrientation; + + //Quaternion vq = new Quaternion(sitTargetPos.X, sitTargetPos.Y+0.2f, sitTargetPos.Z+0.2f, 0); + //Quaternion nq = new Quaternion(-sitTargetOrient.X, -sitTargetOrient.Y, -sitTargetOrient.Z, sitTargetOrient.w); + + //Quaternion result = (sitTargetOrient * vq) * nq; + + m_pos = new Vector3(sitTargetPos.X, sitTargetPos.Y, sitTargetPos.Z); + m_pos += m_sitTargetCorrectionOffset; + m_bodyRot = sitTargetOrient; + //Rotation = sitTargetOrient; + m_parentPosition = part.AbsolutePosition; + + //SendTerseUpdateToAllClients(); + } + else + { + m_pos -= part.AbsolutePosition; + m_parentPosition = part.AbsolutePosition; + } + } + else + { + return; + } + } + m_parentID = m_requestedSitTargetID; + + Velocity = new Vector3(0, 0, 0); + RemoveFromPhysicalScene(); + + TrySetMovementAnimation(sitAnimation); + SendFullUpdateToAllClients(); + // This may seem stupid, but Our Full updates don't send avatar rotation :P + // So we're also sending a terse update (which has avatar rotation) + // [Update] We do now. + //SendTerseUpdateToAllClients(); + } + + /// + /// Event handler for the 'Always run' setting on the client + /// Tells the physics plugin to increase speed of movement. + /// + public void HandleSetAlwaysRun(IClientAPI remoteClient, bool SetAlwaysRun) + { + m_setAlwaysRun = SetAlwaysRun; + if (PhysicsActor != null) + { + PhysicsActor.SetAlwaysRun = SetAlwaysRun; + } + } + + public void AddAnimation(UUID animID) + { + if (m_isChildAgent) + return; + + if (m_animations.Add(animID, m_controllingClient.NextAnimationSequenceNumber)) + { + SendAnimPack(); + } + } + + public void AddAnimation(string name) + { + if (m_isChildAgent) + return; + + UUID animID = m_controllingClient.GetDefaultAnimation(name); + if (animID == UUID.Zero) + return; + + AddAnimation(animID); + } + + public void RemoveAnimation(UUID animID) + { + if (m_isChildAgent) + return; + + if (m_animations.Remove(animID)) + { + SendAnimPack(); + } + } + + public void RemoveAnimation(string name) + { + if (m_isChildAgent) + return; + + UUID animID = m_controllingClient.GetDefaultAnimation(name); + if (animID == UUID.Zero) + return; + + RemoveAnimation(animID); + } + + public UUID[] GetAnimationArray() + { + UUID[] animIDs; + int[] sequenceNums; + m_animations.GetArrays( out animIDs, out sequenceNums ); + return animIDs; + } + + public void HandleStartAnim(IClientAPI remoteClient, UUID animID) + { + AddAnimation(animID); + } + + public void HandleStopAnim(IClientAPI remoteClient, UUID animID) + { + RemoveAnimation(animID); + } + + /// + /// The movement animation is reserved for "main" animations + /// that are mutually exclusive, e.g. flying and sitting. + /// + protected void TrySetMovementAnimation(string anim) + { + //m_log.DebugFormat("Updating movement animation to {0}", anim); + + if (m_animations.TrySetDefaultAnimation(anim, m_controllingClient.NextAnimationSequenceNumber)) + { + SendAnimPack(); + } + } + + /// + /// This method determines the proper movement related animation + /// + public string GetMovementAnimation() + { + if (m_movementflag != 0) + { + // We are moving + if (PhysicsActor != null && PhysicsActor.Flying) + { + return "FLY"; + } + else if (PhysicsActor != null && (m_movementflag & (uint) AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0 && + PhysicsActor.IsColliding) + { + if ((m_movementflag & (uint) AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) != 0 || + (m_movementflag & (uint) AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) != 0) + { + return "CROUCHWALK"; + } + else + { + return "CROUCH"; + } + } + else if (PhysicsActor != null && !PhysicsActor.IsColliding && PhysicsActor.Velocity.Z < -2) + { + return "FALLDOWN"; + } + else if (PhysicsActor != null && !PhysicsActor.IsColliding && Velocity.Z > 1e-6 && + (m_movementflag & (uint) AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) != 0) + { + return "JUMP"; + } + else if (m_setAlwaysRun) + { + return "RUN"; + } + else + { + return "WALK"; + } + } + else + { + // We are not moving + if (PhysicsActor != null && !PhysicsActor.IsColliding && PhysicsActor.Velocity.Z < -2 && !PhysicsActor.Flying) + { + return "FALLDOWN"; + } + else if (PhysicsActor != null && !PhysicsActor.IsColliding && Velocity.Z > 6 && !PhysicsActor.Flying) + { + // HACK: We check if Velocity.Z > 6 for this animation in order to avoid false positives during normal movement. + // TODO: set this animation only when on the ground and UP_POS is received? + + // This is the standing jump + return "JUMP"; + } + else if (PhysicsActor != null && PhysicsActor.Flying) + { + return "HOVER"; + } + else + { + return "STAND"; + } + } + } + + /// + /// Update the movement animation of this avatar according to its current state + /// + protected void UpdateMovementAnimations() + { + string animation = GetMovementAnimation(); + TrySetMovementAnimation(animation); + } + + /// + /// Rotate the avatar to the given rotation and apply a movement in the given relative vector + /// + /// The vector in which to move. This is relative to the rotation argument + /// The direction in which this avatar should now face. + public void AddNewMovement(Vector3 vec, Quaternion rotation) + { + if (m_isChildAgent) + { + Console.WriteLine("DEBUG: AddNewMovement: child agent"); + return; + } + + m_perfMonMS = System.Environment.TickCount; + + m_rotation = rotation; + NewForce newVelocity = new NewForce(); + Vector3 direc = vec * rotation; + direc.Normalize(); + + direc *= 0.03f * 128f; + if (m_physicsActor.Flying) + { + direc *= 4; + //bool controlland = (((m_AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0) || ((m_AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG) != 0)); + //bool colliding = (m_physicsActor.IsColliding==true); + //if (controlland) + // m_log.Info("[AGENT]: landCommand"); + //if (colliding) + // m_log.Info("[AGENT]: colliding"); + //if (m_physicsActor.Flying && colliding && controlland) + //{ + // StopFlying(); + // m_log.Info("[AGENT]: Stop FLying"); + //} + } + else + { + if (!m_physicsActor.Flying && m_physicsActor.IsColliding) + { + if (direc.Z > 2.0f) + { + direc.Z *= 3; + + // TODO: PreJump and jump happen too quickly. Many times prejump gets ignored. + TrySetMovementAnimation("PREJUMP"); + TrySetMovementAnimation("JUMP"); + } + } + } + + newVelocity.X = direc.X; + newVelocity.Y = direc.Y; + newVelocity.Z = direc.Z; + m_forcesList.Add(newVelocity); + + m_scene.AddAgentTime(System.Environment.TickCount - m_perfMonMS); + } + + #endregion + + #region Overridden Methods + + public override void Update() + { + SendPrimUpdates(); + + if (m_newCoarseLocations) + { + SendCoarseLocations(); + m_newCoarseLocations = false; + } + + if (m_isChildAgent == false) + { + if (m_newForce) // user movement 'forces' (ie commands to move) + { + SendTerseUpdateToAllClients(); + m_updateCount = 0; + } + else if (m_movementflag != 0) // scripted movement (?) + { + m_updateCount++; + if (m_updateCount > 3) + { + SendTerseUpdateToAllClients(); + m_updateCount = 0; + } + } + else if ((Util.GetDistanceTo(lastPhysPos, AbsolutePosition) > 0.02) + || (Util.GetDistanceTo(m_lastVelocity, m_velocity) > 0.02) + || lastPhysRot != m_bodyRot) + { + // Send Terse Update to all clients updates lastPhysPos and m_lastVelocity + // doing the above assures us that we know what we sent the clients last + SendTerseUpdateToAllClients(); + m_updateCount = 0; + } + + // followed suggestion from mic bowman. reversed the two lines below. + CheckForBorderCrossing(); + CheckForSignificantMovement(); // sends update to the modules. + } + } + + #endregion + + #region Update Client(s) + + /// + /// Sends a location update to the client connected to this scenePresence + /// + /// + public void SendTerseUpdateToClient(IClientAPI remoteClient) + { + m_perfMonMS = System.Environment.TickCount; + + Vector3 pos = m_pos; + Vector3 vel = Velocity; + Quaternion rot = m_bodyRot; + pos.Z -= m_appearance.HipOffset; + remoteClient.SendAvatarTerseUpdate(m_regionHandle, (ushort)(m_scene.TimeDilation * (float)ushort.MaxValue), LocalId, new Vector3(pos.X, pos.Y, pos.Z), + new Vector3(vel.X, vel.Y, vel.Z), rot); + + m_scene.AddAgentTime(System.Environment.TickCount - m_perfMonMS); + m_scene.AddAgentUpdates(1); + } + + /// + /// Send a location/velocity/accelleration update to all agents in scene + /// + public void SendTerseUpdateToAllClients() + { + m_perfMonMS = System.Environment.TickCount; + + m_scene.Broadcast(SendTerseUpdateToClient); + + m_lastVelocity = m_velocity; + lastPhysPos = AbsolutePosition; + lastPhysRot = m_bodyRot; + + m_scene.AddAgentTime(System.Environment.TickCount - m_perfMonMS); + + } + + public void SendCoarseLocations() + { + m_perfMonMS = System.Environment.TickCount; + + List CoarseLocations = new List(); + List avatars = m_scene.GetAvatars(); + for (int i = 0; i < avatars.Count; i++) + { + if (avatars[i] != this) + { + if (avatars[i].ParentID != 0) + { + // sitting avatar + SceneObjectPart sop = m_scene.GetSceneObjectPart(avatars[i].ParentID); + if (sop != null) + { + CoarseLocations.Add(sop.AbsolutePosition + avatars[i].m_pos); + } + else + { + // we can't find the parent.. ! arg! + CoarseLocations.Add(avatars[i].m_pos); + } + } + else + { + CoarseLocations.Add(avatars[i].m_pos); + } + } + } + + m_controllingClient.SendCoarseLocationUpdate(CoarseLocations); + + m_scene.AddAgentTime(System.Environment.TickCount - m_perfMonMS); + } + + public void CoarseLocationChange() + { + m_newCoarseLocations = true; + } + + /// + /// Tell other client about this avatar (The client previously didn't know or had outdated details about this avatar) + /// + /// + public void SendFullUpdateToOtherClient(ScenePresence remoteAvatar) + { + // 2 stage check is needed. + if (remoteAvatar == null) + return; + IClientAPI cl=remoteAvatar.ControllingClient; + if (cl == null) + return; + if (m_appearance.Texture == null) + return; + + // Note: because Quaternion is a struct, it can't be null + Quaternion rot = m_bodyRot; + + Vector3 pos = m_pos; + pos.Z -= m_appearance.HipOffset; + + remoteAvatar.m_controllingClient.SendAvatarData(m_regionInfo.RegionHandle, m_firstname, m_lastname, m_grouptitle, m_uuid, + LocalId, m_pos, m_appearance.Texture.ToBytes(), + m_parentID, rot); + m_scene.AddAgentUpdates(1); + } + + /// + /// Tell *ALL* agents about this agent + /// + public void SendInitialFullUpdateToAllClients() + { + m_perfMonMS = System.Environment.TickCount; + + List avatars = m_scene.GetScenePresences(); + foreach (ScenePresence avatar in avatars) + { + // only send if this is the root (children are only "listening posts" in a foreign region) + if (!IsChildAgent) + { + SendFullUpdateToOtherClient(avatar); + } + + if (avatar.LocalId != LocalId) + { + if (!avatar.IsChildAgent) + { + avatar.SendFullUpdateToOtherClient(this); + avatar.SendAppearanceToOtherAgent(this); + avatar.SendAnimPackToClient(this.ControllingClient); + } + } + } + m_scene.AddAgentUpdates(avatars.Count); + m_scene.AddAgentTime(System.Environment.TickCount - m_perfMonMS); + } + + public void SendFullUpdateToAllClients() + { + m_perfMonMS = System.Environment.TickCount; + + // only send update from root agents to other clients; children are only "listening posts" + List avatars = m_scene.GetAvatars(); + foreach (ScenePresence avatar in avatars) + { + SendFullUpdateToOtherClient(avatar); + + } + m_scene.AddAgentUpdates(avatars.Count); + m_scene.AddAgentTime(System.Environment.TickCount - m_perfMonMS); + + SendAnimPack(); + } + + /// + /// Do everything required once a client completes its movement into a region + /// + public void SendInitialData() + { + // Moved this into CompleteMovement to ensure that m_appearance is initialized before + // the inventory arrives + // m_scene.GetAvatarAppearance(m_controllingClient, out m_appearance); + + // Note: because Quaternion is a struct, it can't be null + Quaternion rot = m_bodyRot; + + Vector3 pos = m_pos; + pos.Z -= m_appearance.HipOffset; + + m_controllingClient.SendAvatarData(m_regionInfo.RegionHandle, m_firstname, m_lastname, m_grouptitle, m_uuid, LocalId, + m_pos, m_appearance.Texture.ToBytes(), m_parentID, rot); + + if (!m_isChildAgent) + { + m_scene.InformClientOfNeighbours(this); + } + + SendInitialFullUpdateToAllClients(); + SendAppearanceToAllOtherAgents(); + } + + /// + /// Tell the client for this scene presence what items it should be wearing now + /// + /// + public void SendWearables() + { + ControllingClient.SendWearables(m_appearance.Wearables, m_appearance.Serial++); + } + + /// + /// + /// + public void SendAppearanceToAllOtherAgents() + { + m_perfMonMS = System.Environment.TickCount; + + m_scene.ForEachScenePresence(delegate(ScenePresence scenePresence) + { + if (scenePresence.UUID != UUID) + { + SendAppearanceToOtherAgent(scenePresence); + } + }); + + m_scene.AddAgentTime(System.Environment.TickCount - m_perfMonMS); + } + + /// + /// Send appearance data to an agent that isn't this one. + /// + /// + public void SendAppearanceToOtherAgent(ScenePresence avatar) + { + avatar.ControllingClient.SendAppearance( + m_appearance.Owner, m_appearance.VisualParams, m_appearance.Texture.ToBytes()); + } + + /// + /// Set appearance data (textureentry and slider settings) received from the client + /// + /// + /// + public void SetAppearance(byte[] texture, List visualParam) + { + if (m_physicsActor != null) + { + // This may seem like it's redundant, remove the avatar from the physics scene + // just to add it back again, but it saves us from having to update + // 3 variables 10 times a second. + m_scene.PhysicsScene.RemoveAvatar(m_physicsActor); + AddToPhysicalScene(); + } + m_appearance.SetAppearance(texture, visualParam); + SetHeight(m_appearance.AvatarHeight); + m_scene.CommsManager.AvatarService.UpdateUserAppearance(m_controllingClient.AgentId, m_appearance); + + SendAppearanceToAllOtherAgents(); + //SendWearables(); + if (!m_startAnimationSet) + { + UpdateMovementAnimations(); + m_startAnimationSet = true; + } + } + + public void SetWearable(int wearableId, AvatarWearable wearable) + { + m_appearance.SetWearable(wearableId, wearable); + m_scene.CommsManager.AvatarService.UpdateUserAppearance(m_controllingClient.AgentId, m_appearance); + m_controllingClient.SendWearables(m_appearance.Wearables, m_appearance.Serial++); + } + + // Because appearance setting is in a module, we actually need + // to give it access to our appearance directly, otherwise we + // get a synchronization issue. + public AvatarAppearance Appearance + { + get { return m_appearance; } + set { m_appearance = value; } + } + + /// + /// + /// + /// + /// + public void SendAnimPack(UUID[] animations, int[] seqs) + { + if (m_isChildAgent) + return; + + m_scene.Broadcast( + delegate(IClientAPI client) { client.SendAnimations(animations, seqs, m_controllingClient.AgentId); }); + } + + public void SendAnimPackToClient(IClientAPI client) + { + if (m_isChildAgent) + return; + UUID[] animIDs; + int[] sequenceNums; + + m_animations.GetArrays(out animIDs, out sequenceNums); + + client.SendAnimations(animIDs, sequenceNums, m_controllingClient.AgentId); + } + + /// + /// Send animation information about this avatar to all clients. + /// + public void SendAnimPack() + { + //m_log.Debug("Sending animation pack"); + + if (m_isChildAgent) + return; + + UUID[] animIDs; + int[] sequenceNums; + + m_animations.GetArrays(out animIDs, out sequenceNums); + + SendAnimPack(animIDs, sequenceNums); + } + + #endregion + + #region Significant Movement Method + + /// + /// This checks for a significant movement and sends a courselocationchange update + /// + protected void CheckForSignificantMovement() + { + if (Util.GetDistanceTo(AbsolutePosition, posLastSignificantMove) > 0.5) + { + posLastSignificantMove = AbsolutePosition; + m_scene.EventManager.TriggerSignificantClientMovement(m_controllingClient); + m_scene.NotifyMyCoarseLocationChange(); + } + + // Minimum Draw distance is 64 meters, the Radius of the draw distance sphere is 32m + if (Util.GetDistanceTo(AbsolutePosition,m_LastChildAgentUpdatePosition) > 32) + { + ChildAgentDataUpdate cadu = new ChildAgentDataUpdate(); + cadu.ActiveGroupID = UUID.Zero.Guid; + cadu.AgentID = UUID.Guid; + cadu.alwaysrun = m_setAlwaysRun; + cadu.AVHeight = m_avHeight; + sLLVector3 tempCameraCenter = new sLLVector3(new Vector3(m_CameraCenter.X, m_CameraCenter.Y, m_CameraCenter.Z)); + cadu.cameraPosition = tempCameraCenter; + cadu.drawdistance = m_DrawDistance; + if (m_scene.Permissions.IsGod(new UUID(cadu.AgentID))) + cadu.godlevel = m_godlevel; + cadu.GroupAccess = 0; + cadu.Position = new sLLVector3(AbsolutePosition); + cadu.regionHandle = m_scene.RegionInfo.RegionHandle; + float multiplier = 1; + int innacurateNeighbors = m_scene.GetInaccurateNeighborCount(); + if (innacurateNeighbors != 0) + { + multiplier = 1f / (float)innacurateNeighbors; + } + if (multiplier <= 0f) + { + multiplier = 0.25f; + } + + //m_log.Info("[NeighborThrottle]: " + m_scene.GetInaccurateNeighborCount().ToString() + " - m: " + multiplier.ToString()); + cadu.throttles = ControllingClient.GetThrottlesPacked(multiplier); + cadu.Velocity = new sLLVector3(Velocity); + + AgentPosition agentpos = new AgentPosition(); + agentpos.CopyFrom(cadu); + + m_scene.SendOutChildAgentUpdates(agentpos, this); + + m_LastChildAgentUpdatePosition.X = AbsolutePosition.X; + m_LastChildAgentUpdatePosition.Y = AbsolutePosition.Y; + m_LastChildAgentUpdatePosition.Z = AbsolutePosition.Z; + } + } + + #endregion + + #region Border Crossing Methods + + /// + /// Checks to see if the avatar is in range of a border and calls CrossToNewRegion + /// + protected void CheckForBorderCrossing() + { + if (IsChildAgent) + return; + + Vector3 pos2 = AbsolutePosition; + Vector3 vel = Velocity; + + float timeStep = 0.1f; + pos2.X = pos2.X + (vel.X*timeStep); + pos2.Y = pos2.Y + (vel.Y*timeStep); + pos2.Z = pos2.Z + (vel.Z*timeStep); + + if ((pos2.X < 0) || (pos2.X > Constants.RegionSize)) + { + CrossToNewRegion(); + } + + if ((pos2.Y < 0) || (pos2.Y > Constants.RegionSize)) + { + CrossToNewRegion(); + } + } + + /// + /// Moves the agent outside the region bounds + /// Tells neighbor region that we're crossing to it + /// If the neighbor accepts, remove the agent's viewable avatar from this scene + /// set them to a child agent. + /// + protected void CrossToNewRegion() + { + Vector3 pos = AbsolutePosition; + Vector3 newpos = new Vector3(pos.X, pos.Y, pos.Z); + uint neighbourx = m_regionInfo.RegionLocX; + uint neighboury = m_regionInfo.RegionLocY; + + // distance to edge that will trigger crossing + const float boundaryDistance = 1.7f; + + // distance into new region to place avatar + const float enterDistance = 0.1f; + + if (pos.X < boundaryDistance) + { + neighbourx--; + newpos.X = Constants.RegionSize - enterDistance; + } + else if (pos.X > Constants.RegionSize - boundaryDistance) + { + neighbourx++; + newpos.X = enterDistance; + } + + if (pos.Y < boundaryDistance) + { + neighboury--; + newpos.Y = Constants.RegionSize - enterDistance; + } + else if (pos.Y > Constants.RegionSize - boundaryDistance) + { + neighboury++; + newpos.Y = enterDistance; + } + + Vector3 vel = m_velocity; + ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize)); + SimpleRegionInfo neighbourRegion = m_scene.RequestNeighbouringRegionInfo(neighbourHandle); + if (neighbourRegion != null && ValidateAttachments()) + { + // When the neighbour is informed of the border crossing, it will set up CAPS handlers for the avatar + // This means we need to remove the current caps handler here and possibly compensate later, + // in case both scenes are being hosted on the same region server. Messy + //m_scene.RemoveCapsHandler(UUID); + newpos = newpos + (vel); + + CachedUserInfo userInfo = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(UUID); + if (userInfo != null) + { + userInfo.DropInventory(); + } + else + { + m_log.WarnFormat("[SCENE PRESENCE]: No cached user info found for {0} {1} on leaving region", Name, UUID); + } + + bool crossingSuccessful = + m_scene.InformNeighbourOfCrossing(neighbourHandle, m_controllingClient.AgentId, newpos, + m_physicsActor.Flying); + if (crossingSuccessful) + { + // Next, let's close the child agent connections that are too far away. + CloseChildAgents(neighbourx, neighboury); + + //AgentCircuitData circuitdata = m_controllingClient.RequestClientInfo(); + m_controllingClient.RequestClientInfo(); + + //Console.WriteLine("BEFORE CROSS"); + //Scene.DumpChildrenSeeds(UUID); + //DumpKnownRegions(); + string agentcaps; + if (!m_knownChildRegions.TryGetValue(neighbourRegion.RegionHandle, out agentcaps)) + { + m_log.ErrorFormat("[SCENE PRESENCE]: No CAPS information for region handle {0}, exiting CrossToNewRegion.", + neighbourRegion.RegionHandle); + return; + } + // TODO Should construct this behind a method + string capsPath = + "http://" + neighbourRegion.ExternalHostName + ":" + neighbourRegion.HttpPort + + "/CAPS/" + agentcaps /*circuitdata.CapsPath*/ + "0000/"; + + m_log.DebugFormat("[CAPS]: Sending new CAPS seed url {0} to client {1}", capsPath, m_uuid); + + IEventQueue eq = m_scene.RequestModuleInterface(); + if (eq != null) + { + eq.CrossRegion(neighbourHandle, newpos, vel, neighbourRegion.ExternalEndPoint, + capsPath, UUID, ControllingClient.SessionId); + } + else + { + m_controllingClient.CrossRegion(neighbourHandle, newpos, vel, neighbourRegion.ExternalEndPoint, + capsPath); + } + + MakeChildAgent(); + // now we have a child agent in this region. Request all interesting data about other (root) agents + SendInitialFullUpdateToAllClients(); + + CrossAttachmentsIntoNewRegion(neighbourHandle, true); + + // m_scene.SendKillObject(m_localId); + + m_scene.NotifyMyCoarseLocationChange(); + // the user may change their profile information in other region, + // so the userinfo in UserProfileCache is not reliable any more, delete it + if (m_scene.NeedSceneCacheClear(UUID)) + { + m_scene.CommsManager.UserProfileCacheService.RemoveUser(UUID); + m_log.DebugFormat( + "[SCENE PRESENCE]: User {0} is going to another region, profile cache removed", UUID); + } + } + else + { + // Restore the user structures that we needed to delete before asking the receiving region to complete the crossing + m_scene.CommsManager.UserProfileCacheService.RequestInventoryForUser(UUID); + m_scene.CapsModule.AddCapsHandler(UUID); + } + } + + //Console.WriteLine("AFTER CROSS"); + //Scene.DumpChildrenSeeds(UUID); + //DumpKnownRegions(); + } + + /// + /// Computes which child agents to close when the scene presence moves to another region. + /// Removes those regions from m_knownRegions. + /// + /// The new region's x on the map + /// The new region's y on the map + /// + public void CloseChildAgents(uint newRegionX, uint newRegionY) + { + List byebyeRegions = new List(); + m_log.DebugFormat( + "[SCENE PRESENCE]: Closing child agents. Checking {0} regions in {1}", + m_knownChildRegions.Keys.Count, Scene.RegionInfo.RegionName); + //DumpKnownRegions(); + + lock (m_knownChildRegions) + { + foreach (ulong handle in m_knownChildRegions.Keys) + { + // Don't close the agent on this region yet + if (handle != Scene.RegionInfo.RegionHandle) + { + uint x, y; + Utils.LongToUInts(handle, out x, out y); + x = x / Constants.RegionSize; + y = y / Constants.RegionSize; + + //Console.WriteLine("---> x: " + x + "; newx:" + newRegionX + "; Abs:" + (int)Math.Abs((int)(x - newRegionX))); + //Console.WriteLine("---> y: " + y + "; newy:" + newRegionY + "; Abs:" + (int)Math.Abs((int)(y - newRegionY))); + if (Util.IsOutsideView(x, newRegionX, y, newRegionY)) + { + byebyeRegions.Add(handle); + } + } + } + } + + if (byebyeRegions.Count > 0) + { + m_log.Debug("[SCENE PRESENCE]: Closing " + byebyeRegions.Count + " child agents"); + m_scene.SceneGridService.SendCloseChildAgentConnections(m_controllingClient.AgentId, byebyeRegions); + } + + foreach (ulong handle in byebyeRegions) + { + RemoveNeighbourRegion(handle); + } + + } + + #endregion + + /// + /// This allows the Sim owner the abiility to kick users from their sim currently. + /// It tells the client that the agent has permission to do so. + /// + public void GrantGodlikePowers(UUID agentID, UUID sessionID, UUID token, bool godStatus) + { + if (godStatus) + { + // For now, assign god level 200 to anyone + // who is granted god powers, but has no god level set. + // + CachedUserInfo profile = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(agentID); + if (profile.UserProfile.GodLevel > 0) + m_godlevel = profile.UserProfile.GodLevel; + else + m_godlevel = 200; + } + else + { + m_godlevel = 0; + } + + ControllingClient.SendAdminResponse(token, (uint)m_godlevel); + } + + #region Child Agent Updates + + public void ChildAgentDataUpdate(AgentData cAgentData) + { + //Console.WriteLine(" >>> ChildAgentDataUpdate <<< " + Scene.RegionInfo.RegionName); + if (!IsChildAgent) + return; + + CopyFrom(cAgentData); + } + + /// + /// This updates important decision making data about a child agent + /// The main purpose is to figure out what objects to send to a child agent that's in a neighboring region + /// + public void ChildAgentDataUpdate(AgentPosition cAgentData, uint tRegionX, uint tRegionY, uint rRegionX, uint rRegionY) + { + if (!IsChildAgent) + return; + + //Console.WriteLine(" >>> ChildAgentPositionUpdate <<< " + rRegionX + "-" + rRegionY); + int shiftx = ((int)rRegionX - (int)tRegionX) * (int)Constants.RegionSize; + int shifty = ((int)rRegionY - (int)tRegionY) * (int)Constants.RegionSize; + + m_DrawDistance = cAgentData.Far; + if (cAgentData.Position != new Vector3(-1, -1, -1)) // UGH!! + m_pos = new Vector3(cAgentData.Position.X + shiftx, cAgentData.Position.Y + shifty, cAgentData.Position.Z); + + // It's hard to say here.. We can't really tell where the camera position is unless it's in world cordinates from the sending region + m_CameraCenter = cAgentData.Center; + + m_avHeight = cAgentData.Size.Z; + //SetHeight(cAgentData.AVHeight); + + if ((cAgentData.Throttles != null) && cAgentData.Throttles.Length > 0) + ControllingClient.SetChildAgentThrottle(cAgentData.Throttles); + + // Sends out the objects in the user's draw distance if m_sendTasksToChild is true. + if (m_scene.m_seeIntoRegionFromNeighbor) + m_pendingObjects = null; + + //cAgentData.AVHeight; + //cAgentData.regionHandle; + //m_velocity = cAgentData.Velocity; + } + + public void CopyTo(AgentData cAgent) + { + cAgent.AgentID = UUID; + cAgent.RegionHandle = m_scene.RegionInfo.RegionHandle; + + cAgent.Position = m_pos; + cAgent.Velocity = m_velocity; + cAgent.Center = m_CameraCenter; + cAgent.Size = new Vector3(0, 0, m_avHeight); + cAgent.AtAxis = m_CameraAtAxis; + cAgent.LeftAxis = m_CameraLeftAxis; + cAgent.UpAxis = m_CameraUpAxis; + + cAgent.Far = m_DrawDistance; + + // Throttles + float multiplier = 1; + int innacurateNeighbors = m_scene.GetInaccurateNeighborCount(); + if (innacurateNeighbors != 0) + { + multiplier = 1f / (float)innacurateNeighbors; + } + if (multiplier <= 0f) + { + multiplier = 0.25f; + } + //m_log.Info("[NeighborThrottle]: " + m_scene.GetInaccurateNeighborCount().ToString() + " - m: " + multiplier.ToString()); + cAgent.Throttles = ControllingClient.GetThrottlesPacked(multiplier); + + cAgent.HeadRotation = m_headrotation; + cAgent.BodyRotation = m_bodyRot; + cAgent.ControlFlags = m_AgentControlFlags; + if ((m_physicsActor != null) && (m_physicsActor.Flying)) + { + cAgent.ControlFlags |= (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY; + } + + if (m_scene.Permissions.IsGod(new UUID(cAgent.AgentID))) + cAgent.GodLevel = (byte)m_godlevel; + else + cAgent.GodLevel = (byte) 0; + + cAgent.AlwaysRun = m_setAlwaysRun; + + //cAgent.AgentTextures = ??? + //cAgent.GroupID = ?? + // Groups??? + + // Animations??? + + cAgent.VisualParams = m_appearance.VisualParams; + } + + public void CopyFrom(AgentData cAgent) + { + m_rootRegionHandle= cAgent.RegionHandle; + m_callbackURI = cAgent.CallbackURI; + + m_pos = cAgent.Position; + m_velocity = cAgent.Velocity; + m_CameraCenter = cAgent.Center; + m_avHeight = cAgent.Size.Z; + m_CameraAtAxis = cAgent.AtAxis; + m_CameraLeftAxis = cAgent.LeftAxis; + m_CameraUpAxis = cAgent.UpAxis; + + m_DrawDistance = cAgent.Far; + + if ((cAgent.Throttles != null) && cAgent.Throttles.Length > 0) + ControllingClient.SetChildAgentThrottle(cAgent.Throttles); + + m_headrotation = cAgent.HeadRotation; + m_bodyRot = cAgent.BodyRotation; + m_AgentControlFlags = cAgent.ControlFlags; // We need more flags! + if (m_physicsActor != null) + { + m_physicsActor.Flying = ((m_AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0); + } + if (m_scene.Permissions.IsGod(new UUID(cAgent.AgentID))) + m_godlevel = cAgent.GodLevel; + m_setAlwaysRun = cAgent.AlwaysRun; + + //cAgent.AgentTextures = ??? + + //cAgent.GroupID = ?? + //Groups??? + + // Animations??? + + m_appearance.VisualParams = cAgent.VisualParams; + } + + #endregion Child Agent Updates + + /// + /// Handles part of the PID controller function for moving an avatar. + /// + public override void UpdateMovement() + { + m_newForce = false; + lock (m_forcesList) + { + if (m_forcesList.Count > 0) + { + for (int i = 0; i < m_forcesList.Count; i++) + { + NewForce force = m_forcesList[i]; + + m_updateflag = true; + try + { + movementvector.X = force.X; + movementvector.Y = force.Y; + movementvector.Z = force.Z; + Velocity = movementvector; + } + catch (NullReferenceException) + { + // Under extreme load, this returns a NullReference Exception that we can ignore. + // Ignoring this causes no movement to be sent to the physics engine... + // which when the scene is moving at 1 frame every 10 seconds, it doesn't really matter! + } + m_newForce = true; + } + for (int i = 0; i < m_forcesList.Count; i++) + { + m_forcesList.RemoveAt(0); + } + } + } + } + + static ScenePresence() + { + Primitive.TextureEntry textu = AvatarAppearance.GetDefaultTexture(); + DefaultTexture = textu.ToBytes(); + } + + [Serializable] + public class NewForce + { + public float X; + public float Y; + public float Z; + + public NewForce() + { + } + } + + [Serializable] + public class ScenePartUpdate : ISerializable + { + public UUID FullID; + public uint LastFullUpdateTime; + public uint LastTerseUpdateTime; + + public ScenePartUpdate() + { + FullID = UUID.Zero; + LastFullUpdateTime = 0; + LastTerseUpdateTime = 0; + } + + protected ScenePartUpdate(SerializationInfo info, StreamingContext context) + { + //System.Console.WriteLine("ScenePartUpdate Deserialize BGN"); + + if (info == null) + { + throw new ArgumentNullException("info"); + } + + FullID = new UUID((Guid)info.GetValue("FullID", typeof(Guid))); + LastFullUpdateTime = (uint)info.GetValue("LastFullUpdateTime", typeof(uint)); + LastTerseUpdateTime = (uint)info.GetValue("LastTerseUpdateTime", typeof(uint)); + + //System.Console.WriteLine("ScenePartUpdate Deserialize END"); + } + + [SecurityPermission(SecurityAction.LinkDemand, + Flags = SecurityPermissionFlag.SerializationFormatter)] + public virtual void GetObjectData( + SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException("info"); + } + + info.AddValue("FullID", FullID.Guid); + info.AddValue("LastFullUpdateTime", LastFullUpdateTime); + info.AddValue("LastTerseUpdateTime", LastTerseUpdateTime); + } + } + + public override void SetText(string text, Vector3 color, double alpha) + { + throw new Exception("Can't set Text on avatar."); + } + + /// + /// Adds a physical representation of the avatar to the Physics plugin + /// + public void AddToPhysicalScene() + { + PhysicsScene scene = m_scene.PhysicsScene; + + PhysicsVector pVec = + new PhysicsVector(AbsolutePosition.X, AbsolutePosition.Y, + AbsolutePosition.Z); + + if (m_avHeight == 127.0f) + { + m_physicsActor = scene.AddAvatar(Firstname + "." + Lastname, pVec, new PhysicsVector(0, 0, 1.56f)); + } + else + { + m_physicsActor = scene.AddAvatar(Firstname + "." + Lastname, pVec, new PhysicsVector(0, 0, m_avHeight)); + } + + //m_physicsActor.OnRequestTerseUpdate += SendTerseUpdateToAllClients; + m_physicsActor.OnCollisionUpdate += PhysicsCollisionUpdate; + m_physicsActor.SubscribeEvents(1000); + m_physicsActor.LocalID = LocalId; + } + + // Event called by the physics plugin to tell the avatar about a collision. + private void PhysicsCollisionUpdate(EventArgs e) + { + if (e == null) + return; + CollisionEventUpdate collisionData = (CollisionEventUpdate)e; + Dictionary coldata = collisionData.m_objCollisionList; + float starthealth = Health; + uint killerObj = 0; + foreach (uint localid in coldata.Keys) + { + if (coldata[localid] <= 0.10f || m_invulnerable) + continue; + //if (localid == 0) + //continue; + + Health -= coldata[localid] * 5; + + if (Health <= 0) + { + if (localid != 0) + killerObj = localid; + } + //m_log.Debug("[AVATAR]: Collision with localid: " + localid.ToString() + " at depth: " + coldata[localid].ToString()); + } + //Health = 100; + if (!m_invulnerable) + { + if (starthealth != Health) + { + ControllingClient.SendHealth(Health); + } + if (m_health <= 0) + m_scene.EventManager.TriggerAvatarKill(killerObj, this); + } + + if (Velocity.X > 0 || Velocity.Y > 0) + UpdateMovementAnimations(); + } + + public void setHealthWithUpdate(float health) + { + Health = health; + ControllingClient.SendHealth(Health); + } + + public void Close() + { + lock (m_attachments) + { + // Delete attachments from scene + // Don't try to save, as this thread won't live long + // enough to complete the save. This would cause no copy + // attachments to poof! + // + foreach (SceneObjectGroup grp in m_attachments) + { + m_scene.DeleteSceneObject(grp, false); + } + m_attachments.Clear(); + } + lock (m_knownChildRegions) + { + m_knownChildRegions.Clear(); + } + lock (m_updateTimes) + { + m_updateTimes.Clear(); + } + lock (m_partsUpdateQueue) + { + m_partsUpdateQueue.Clear(); + } + + RemoveFromPhysicalScene(); + GC.Collect(); + } + + public ScenePresence() + { +/* JB + if (Animations == null) + { + Animations = new AvatarAnimations(); + Animations.LoadAnims(); + } +*/ + if (DefaultTexture == null) + { + Primitive.TextureEntry textu = AvatarAppearance.GetDefaultTexture(); + DefaultTexture = textu.ToBytes(); + } + } + + public void AddAttachment(SceneObjectGroup gobj) + { + lock (m_attachments) + { + m_attachments.Add(gobj); + } + } + + public bool HasAttachments() + { + return m_attachments.Count > 0; + } + + public bool HasScriptedAttachments() + { + lock (m_attachments) + { + foreach (SceneObjectGroup gobj in m_attachments) + { + if (gobj != null) + { + if (gobj.RootPart.Inventory.ContainsScripts()) + return true; + } + } + } + return false; + } + + public void RemoveAttachment(SceneObjectGroup gobj) + { + lock (m_attachments) + { + if (m_attachments.Contains(gobj)) + { + m_attachments.Remove(gobj); + } + } + } + + public bool ValidateAttachments() + { + lock (m_attachments) + { + // Validate + foreach (SceneObjectGroup gobj in m_attachments) + { + if (gobj == null) + return false; + + if (gobj.IsDeleted) + return false; + } + } + return true; + } + + public bool CrossAttachmentsIntoNewRegion(ulong regionHandle, bool silent) + { + lock (m_attachments) + { + // Validate + foreach (SceneObjectGroup gobj in m_attachments) + { + if (gobj == null || gobj.IsDeleted) + return false; + } + + foreach (SceneObjectGroup gobj in m_attachments) + { + // If the prim group is null then something must have happened to it! + if (gobj != null && gobj.RootPart != null) + { + // Set the parent localID to 0 so it transfers over properly. + gobj.RootPart.SetParentLocalId(0); + gobj.RootPart.IsAttachment = false; + gobj.AbsolutePosition = gobj.RootPart.AttachedPos; + gobj.RootPart.LastOwnerID = gobj.GetFromAssetID(); + m_log.DebugFormat("[ATTACHMENT]: Sending attachment {0} to region {1}", gobj.UUID, regionHandle); + m_scene.CrossPrimGroupIntoNewRegion(regionHandle, gobj, silent); + } + } + m_attachments.Clear(); + + return true; + } + } + + public void initializeScenePresence(IClientAPI client, RegionInfo region, Scene scene) + { + m_controllingClient = client; + m_regionInfo = region; + m_scene = scene; + + RegisterToEvents(); + + /* + AbsolutePosition = client.StartPos; + + Animations = new AvatarAnimations(); + Animations.LoadAnims(); + + m_animations = new List(); + m_animations.Add(Animations.AnimsUUID["STAND"]); + m_animationSeqs.Add(m_controllingClient.NextAnimationSequenceNumber); + + SetDirectionVectors(); + */ + } + + protected ScenePresence(SerializationInfo info, StreamingContext context) + : base (info, context) + { + //System.Console.WriteLine("ScenePresence Deserialize BGN"); + + if (info == null) + { + throw new ArgumentNullException("info"); + } +/* JB + if (Animations == null) + { + Animations = new AvatarAnimations(); + Animations.LoadAnims(); + } +*/ + if (DefaultTexture == null) + { + Primitive.TextureEntry textu = AvatarAppearance.GetDefaultTexture(); + DefaultTexture = textu.ToBytes(); + } + + m_animations = (AnimationSet)info.GetValue("m_animations", typeof(AnimationSet)); + m_updateflag = (bool)info.GetValue("m_updateflag", typeof(bool)); + m_movementflag = (byte)info.GetValue("m_movementflag", typeof(byte)); + m_forcesList = (List)info.GetValue("m_forcesList", typeof(List)); + m_updateCount = (short)info.GetValue("m_updateCount", typeof(short)); + m_requestedSitTargetID = (uint)info.GetValue("m_requestedSitTargetID", typeof(uint)); + + m_requestedSitOffset + = new Vector3( + (float)info.GetValue("m_requestedSitOffset.X", typeof(float)), + (float)info.GetValue("m_requestedSitOffset.Y", typeof(float)), + (float)info.GetValue("m_requestedSitOffset.Z", typeof(float))); + + m_sitAvatarHeight = (float)info.GetValue("m_sitAvatarHeight", typeof(float)); + m_godlevel = (float)info.GetValue("m_godlevel", typeof(float)); + m_setAlwaysRun = (bool)info.GetValue("m_setAlwaysRun", typeof(bool)); + + m_bodyRot + = new Quaternion( + (float)info.GetValue("m_bodyRot.X", typeof(float)), + (float)info.GetValue("m_bodyRot.Y", typeof(float)), + (float)info.GetValue("m_bodyRot.Z", typeof(float)), + (float)info.GetValue("m_bodyRot.W", typeof(float))); + + IsRestrictedToRegion = (bool)info.GetValue("IsRestrictedToRegion", typeof(bool)); + m_newForce = (bool)info.GetValue("m_newForce", typeof(bool)); + //m_newAvatar = (bool)info.GetValue("m_newAvatar", typeof(bool)); + m_newCoarseLocations = (bool)info.GetValue("m_newCoarseLocations", typeof(bool)); + m_avHeight = (float)info.GetValue("m_avHeight", typeof(float)); + crossingFromRegion = (ulong)info.GetValue("crossingFromRegion", typeof(ulong)); + + List Dir_Vectors_work = (List)info.GetValue("Dir_Vectors", typeof(List)); + List Dir_Vectors_work2 = new List(); + + foreach (float[] f3 in Dir_Vectors_work) + { + Dir_Vectors_work2.Add(new Vector3(f3[0], f3[1], f3[2])); + } + + Dir_Vectors = Dir_Vectors_work2.ToArray(); + + lastPhysPos + = new Vector3( + (float)info.GetValue("lastPhysPos.X", typeof(float)), + (float)info.GetValue("lastPhysPos.Y", typeof(float)), + (float)info.GetValue("lastPhysPos.Z", typeof(float))); + + // Possibly we should store lastPhysRot. But there may well be not much point since rotation changes + // wouldn't carry us across borders anyway + + m_CameraCenter + = new Vector3( + (float)info.GetValue("m_CameraCenter.X", typeof(float)), + (float)info.GetValue("m_CameraCenter.Y", typeof(float)), + (float)info.GetValue("m_CameraCenter.Z", typeof(float))); + + m_CameraAtAxis + = new Vector3( + (float)info.GetValue("m_CameraAtAxis.X", typeof(float)), + (float)info.GetValue("m_CameraAtAxis.Y", typeof(float)), + (float)info.GetValue("m_CameraAtAxis.Z", typeof(float))); + + m_CameraLeftAxis + = new Vector3( + (float)info.GetValue("m_CameraLeftAxis.X", typeof(float)), + (float)info.GetValue("m_CameraLeftAxis.Y", typeof(float)), + (float)info.GetValue("m_CameraLeftAxis.Z", typeof(float))); + + m_CameraUpAxis + = new Vector3( + (float)info.GetValue("m_CameraUpAxis.X", typeof(float)), + (float)info.GetValue("m_CameraUpAxis.Y", typeof(float)), + (float)info.GetValue("m_CameraUpAxis.Z", typeof(float))); + + m_DrawDistance = (float)info.GetValue("m_DrawDistance", typeof(float)); + m_appearance = (AvatarAppearance)info.GetValue("m_appearance", typeof(AvatarAppearance)); + + m_knownChildRegions = (Dictionary)info.GetValue("m_knownChildRegions", typeof(Dictionary)); + + posLastSignificantMove + = new Vector3( + (float)info.GetValue("posLastSignificantMove.X", typeof(float)), + (float)info.GetValue("posLastSignificantMove.Y", typeof(float)), + (float)info.GetValue("posLastSignificantMove.Z", typeof(float))); + + // m_partsUpdateQueue = (UpdateQueue)info.GetValue("m_partsUpdateQueue", typeof(UpdateQueue)); + + /* + Dictionary updateTimes_work + = (Dictionary)info.GetValue("m_updateTimes", typeof(Dictionary)); + + foreach (Guid id in updateTimes_work.Keys) + { + m_updateTimes.Add(new UUID(id), updateTimes_work[id]); + } + */ + m_regionHandle = (ulong)info.GetValue("m_regionHandle", typeof(ulong)); + m_firstname = (string)info.GetValue("m_firstname", typeof(string)); + m_lastname = (string)info.GetValue("m_lastname", typeof(string)); + m_allowMovement = (bool)info.GetValue("m_allowMovement", typeof(bool)); + m_parentPosition = new Vector3((float)info.GetValue("m_parentPosition.X", typeof(float)), + (float)info.GetValue("m_parentPosition.Y", typeof(float)), + (float)info.GetValue("m_parentPosition.Z", typeof(float))); + + m_isChildAgent = (bool)info.GetValue("m_isChildAgent", typeof(bool)); + m_parentID = (uint)info.GetValue("m_parentID", typeof(uint)); + +// for OpenSim_v0.5 + currentParcelUUID = new UUID((Guid)info.GetValue("currentParcelUUID", typeof(Guid))); + + lastKnownAllowedPosition + = new Vector3( + (float)info.GetValue("lastKnownAllowedPosition.X", typeof(float)), + (float)info.GetValue("lastKnownAllowedPosition.Y", typeof(float)), + (float)info.GetValue("lastKnownAllowedPosition.Z", typeof(float))); + + sentMessageAboutRestrictedParcelFlyingDown = (bool)info.GetValue("sentMessageAboutRestrictedParcelFlyingDown", typeof(bool)); + + m_LastChildAgentUpdatePosition + = new Vector3( + (float)info.GetValue("m_LastChildAgentUpdatePosition.X", typeof(float)), + (float)info.GetValue("m_LastChildAgentUpdatePosition.Y", typeof(float)), + (float)info.GetValue("m_LastChildAgentUpdatePosition.Z", typeof(float))); + + m_perfMonMS = (int)info.GetValue("m_perfMonMS", typeof(int)); + m_AgentControlFlags = (uint)info.GetValue("m_AgentControlFlags", typeof(uint)); + + m_headrotation + = new Quaternion( + (float)info.GetValue("m_headrotation.X", typeof(float)), + (float)info.GetValue("m_headrotation.Y", typeof(float)), + (float)info.GetValue("m_headrotation.Z", typeof(float)), + (float)info.GetValue("m_headrotation.W", typeof(float))); + + m_state = (byte)info.GetValue("m_state", typeof(byte)); + + //System.Console.WriteLine("ScenePresence Deserialize END"); + } + + [SecurityPermission(SecurityAction.LinkDemand, + Flags = SecurityPermissionFlag.SerializationFormatter)] + public override void GetObjectData( + SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException("info"); + } + + base.GetObjectData(info, context); + + info.AddValue("m_animations", m_animations); + info.AddValue("m_updateflag", m_updateflag); + info.AddValue("m_movementflag", m_movementflag); + info.AddValue("m_forcesList", m_forcesList); + info.AddValue("m_updateCount", m_updateCount); + info.AddValue("m_requestedSitTargetID", m_requestedSitTargetID); + + // Vector3 + info.AddValue("m_requestedSitOffset.X", m_requestedSitOffset.X); + info.AddValue("m_requestedSitOffset.Y", m_requestedSitOffset.Y); + info.AddValue("m_requestedSitOffset.Z", m_requestedSitOffset.Z); + + info.AddValue("m_sitAvatarHeight", m_sitAvatarHeight); + info.AddValue("m_godlevel", m_godlevel); + info.AddValue("m_setAlwaysRun", m_setAlwaysRun); + + // Quaternion + info.AddValue("m_bodyRot.X", m_bodyRot.X); + info.AddValue("m_bodyRot.Y", m_bodyRot.Y); + info.AddValue("m_bodyRot.Z", m_bodyRot.Z); + info.AddValue("m_bodyRot.W", m_bodyRot.W); + + info.AddValue("IsRestrictedToRegion", IsRestrictedToRegion); + info.AddValue("m_newForce", m_newForce); + //info.AddValue("m_newAvatar", m_newAvatar); + info.AddValue("m_newCoarseLocations", m_newCoarseLocations); + info.AddValue("m_gotAPrimitivesInScene", false); + info.AddValue("m_avHeight", m_avHeight); + + // info.AddValue("m_regionInfo", m_regionInfo); + + info.AddValue("crossingFromRegion", crossingFromRegion); + + List Dir_Vectors_work = new List(); + + foreach (Vector3 v3 in Dir_Vectors) + { + Dir_Vectors_work.Add(new float[] { v3.X, v3.Y, v3.Z }); + } + + info.AddValue("Dir_Vectors", Dir_Vectors_work); + + // Vector3 + info.AddValue("lastPhysPos.X", lastPhysPos.X); + info.AddValue("lastPhysPos.Y", lastPhysPos.Y); + info.AddValue("lastPhysPos.Z", lastPhysPos.Z); + + // Possibly we should retrieve lastPhysRot. But there may well be not much point since rotation changes + // wouldn't carry us across borders anyway + + // Vector3 + info.AddValue("m_CameraCenter.X", m_CameraCenter.X); + info.AddValue("m_CameraCenter.Y", m_CameraCenter.Y); + info.AddValue("m_CameraCenter.Z", m_CameraCenter.Z); + + // Vector3 + info.AddValue("m_CameraAtAxis.X", m_CameraAtAxis.X); + info.AddValue("m_CameraAtAxis.Y", m_CameraAtAxis.Y); + info.AddValue("m_CameraAtAxis.Z", m_CameraAtAxis.Z); + + // Vector3 + info.AddValue("m_CameraLeftAxis.X", m_CameraLeftAxis.X); + info.AddValue("m_CameraLeftAxis.Y", m_CameraLeftAxis.Y); + info.AddValue("m_CameraLeftAxis.Z", m_CameraLeftAxis.Z); + + // Vector3 + info.AddValue("m_CameraUpAxis.X", m_CameraUpAxis.X); + info.AddValue("m_CameraUpAxis.Y", m_CameraUpAxis.Y); + info.AddValue("m_CameraUpAxis.Z", m_CameraUpAxis.Z); + + info.AddValue("m_DrawDistance", m_DrawDistance); + info.AddValue("m_appearance", m_appearance); + info.AddValue("m_knownChildRegions", m_knownChildRegions); + + // Vector3 + info.AddValue("posLastSignificantMove.X", posLastSignificantMove.X); + info.AddValue("posLastSignificantMove.Y", posLastSignificantMove.Y); + info.AddValue("posLastSignificantMove.Z", posLastSignificantMove.Z); + + //info.AddValue("m_partsUpdateQueue", m_partsUpdateQueue); + + /* + Dictionary updateTimes_work = new Dictionary(); + + foreach (UUID id in m_updateTimes.Keys) + { + updateTimes_work.Add(id.UUID, m_updateTimes[id]); + } + + info.AddValue("m_updateTimes", updateTimes_work); + */ + + info.AddValue("m_regionHandle", m_regionHandle); + info.AddValue("m_firstname", m_firstname); + info.AddValue("m_lastname", m_lastname); + info.AddValue("m_allowMovement", m_allowMovement); + //info.AddValue("m_physicsActor", m_physicsActor); + info.AddValue("m_parentPosition.X", m_parentPosition.X); + info.AddValue("m_parentPosition.Y", m_parentPosition.Y); + info.AddValue("m_parentPosition.Z", m_parentPosition.Z); + info.AddValue("m_isChildAgent", m_isChildAgent); + info.AddValue("m_parentID", m_parentID); + +// for OpenSim_v0.5 + info.AddValue("currentParcelUUID", currentParcelUUID.Guid); + + info.AddValue("lastKnownAllowedPosition.X", lastKnownAllowedPosition.X); + info.AddValue("lastKnownAllowedPosition.Y", lastKnownAllowedPosition.Y); + info.AddValue("lastKnownAllowedPosition.Z", lastKnownAllowedPosition.Z); + + info.AddValue("sentMessageAboutRestrictedParcelFlyingDown", sentMessageAboutRestrictedParcelFlyingDown); + + info.AddValue("m_LastChildAgentUpdatePosition.X", m_LastChildAgentUpdatePosition.X); + info.AddValue("m_LastChildAgentUpdatePosition.Y", m_LastChildAgentUpdatePosition.Y); + info.AddValue("m_LastChildAgentUpdatePosition.Z", m_LastChildAgentUpdatePosition.Z); + + info.AddValue("m_perfMonMS", m_perfMonMS); + info.AddValue("m_AgentControlFlags", m_AgentControlFlags); + + info.AddValue("m_headrotation.W", m_headrotation.W); + info.AddValue("m_headrotation.X", m_headrotation.X); + info.AddValue("m_headrotation.Y", m_headrotation.Y); + info.AddValue("m_headrotation.Z", m_headrotation.Z); + + info.AddValue("m_state", m_state); + + List knownPrimUUID_work = new List(); + + info.AddValue("m_knownPrimUUID", knownPrimUUID_work); + } + + internal void PushForce(PhysicsVector impulse) + { + if (PhysicsActor != null) + { + PhysicsActor.AddForce(impulse,true); + } + } + + public void RegisterControlEventsToScript(int controls, int accept, int pass_on, uint Obj_localID, UUID Script_item_UUID) + { + ScriptControllers obj = new ScriptControllers(); + obj.ignoreControls = ScriptControlled.CONTROL_ZERO; + obj.eventControls = ScriptControlled.CONTROL_ZERO; + + obj.itemID = Script_item_UUID; + obj.objID = Obj_localID; + if (pass_on == 0 && accept == 0) + { + IgnoredControls |= (ScriptControlled)controls; + obj.ignoreControls = (ScriptControlled)controls; + } + + if (pass_on == 0 && accept == 1) + { + IgnoredControls |= (ScriptControlled)controls; + obj.ignoreControls = (ScriptControlled)controls; + obj.eventControls = (ScriptControlled)controls; + } + if (pass_on == 1 && accept == 1) + { + IgnoredControls = ScriptControlled.CONTROL_ZERO; + obj.eventControls = (ScriptControlled)controls; + obj.ignoreControls = ScriptControlled.CONTROL_ZERO; + } + + lock (scriptedcontrols) + { + if (pass_on == 1 && accept == 0) + { + IgnoredControls &= ~(ScriptControlled)controls; + if (scriptedcontrols.ContainsKey(Script_item_UUID)) + scriptedcontrols.Remove(Script_item_UUID); + + } + else + { + + if (scriptedcontrols.ContainsKey(Script_item_UUID)) + { + scriptedcontrols[Script_item_UUID] = obj; + } + else + { + scriptedcontrols.Add(Script_item_UUID, obj); + } + } + } + ControllingClient.SendTakeControls(controls, pass_on == 1 ? true : false, true); + } + + public void HandleForceReleaseControls(IClientAPI remoteClient, UUID agentID) + { + IgnoredControls = ScriptControlled.CONTROL_ZERO; + lock (scriptedcontrols) + { + scriptedcontrols.Clear(); + } + ControllingClient.SendTakeControls(int.MaxValue, false, false); + } + + public void UnRegisterControlEventsToScript(uint Obj_localID, UUID Script_item_UUID) + { + lock (scriptedcontrols) + { + if (scriptedcontrols.ContainsKey(Script_item_UUID)) + { + ScriptControllers takecontrolls = scriptedcontrols[Script_item_UUID]; + ScriptControlled sctc = takecontrolls.eventControls; + ControllingClient.SendTakeControls((int)sctc, false, false); + ControllingClient.SendTakeControls((int)sctc, true, false); + + scriptedcontrols.Remove(Script_item_UUID); + IgnoredControls = ScriptControlled.CONTROL_ZERO; + foreach (ScriptControllers scData in scriptedcontrols.Values) + { + IgnoredControls |= scData.ignoreControls; + } + } + + } + } + + internal void SendControlToScripts(uint flags) + { + + ScriptControlled allflags = ScriptControlled.CONTROL_ZERO; + + if (MouseDown) + { + allflags = LastCommands & (ScriptControlled.CONTROL_ML_LBUTTON | ScriptControlled.CONTROL_LBUTTON); + if ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_UP) != 0 || (flags & unchecked((uint)AgentManager.ControlFlags.AGENT_CONTROL_ML_LBUTTON_UP)) != 0) + { + allflags = ScriptControlled.CONTROL_ZERO; + MouseDown = true; + } + } + + if ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_ML_LBUTTON_DOWN) != 0) + { + allflags |= ScriptControlled.CONTROL_ML_LBUTTON; + MouseDown = true; + } + if ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_DOWN) != 0) + { + allflags |= ScriptControlled.CONTROL_LBUTTON; + MouseDown = true; + } + + // find all activated controls, whether the scripts are interested in them or not + if ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) != 0 || (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_POS) != 0) + { + allflags |= ScriptControlled.CONTROL_FWD; + } + if ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) != 0 || (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_NEG) != 0) + { + allflags |= ScriptControlled.CONTROL_BACK; + } + if ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) != 0 || (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_POS) != 0) + { + allflags |= ScriptControlled.CONTROL_UP; + } + if ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0 || (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG) != 0) + { + allflags |= ScriptControlled.CONTROL_DOWN; + } + if ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) != 0 || (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_POS) != 0) + { + allflags |= ScriptControlled.CONTROL_LEFT; + } + if ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) != 0 || (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_NEG) != 0) + { + allflags |= ScriptControlled.CONTROL_RIGHT; + } + if ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_YAW_NEG) != 0) + { + allflags |= ScriptControlled.CONTROL_ROT_RIGHT; + } + if ((flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_YAW_POS) != 0) + { + allflags |= ScriptControlled.CONTROL_ROT_LEFT; + } + // optimization; we have to check per script, but if nothing is pressed and nothing changed, we can skip that + if (allflags != ScriptControlled.CONTROL_ZERO || allflags != LastCommands) + { + lock (scriptedcontrols) + { + foreach (UUID scriptUUID in scriptedcontrols.Keys) + { + ScriptControllers scriptControlData = scriptedcontrols[scriptUUID]; + ScriptControlled localHeld = allflags & scriptControlData.eventControls; // the flags interesting for us + ScriptControlled localLast = LastCommands & scriptControlData.eventControls; // the activated controls in the last cycle + ScriptControlled localChange = localHeld ^ localLast; // the changed bits + if (localHeld != ScriptControlled.CONTROL_ZERO || localChange != ScriptControlled.CONTROL_ZERO) + { + // only send if still pressed or just changed + m_scene.EventManager.TriggerControlEvent(scriptControlData.objID, scriptUUID, UUID, (uint)localHeld, (uint)localChange); + } + } + } + } + + LastCommands = allflags; + } + + internal uint RemoveIgnoredControls(uint flags, ScriptControlled Ignored) + { + if (Ignored == ScriptControlled.CONTROL_ZERO) + return flags; + if ((Ignored & ScriptControlled.CONTROL_BACK) != 0) + flags &= ~((uint)AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG | (uint)AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_NEG); + if ((Ignored & ScriptControlled.CONTROL_FWD) != 0) + flags &= ~((uint)AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_POS | (uint)AgentManager.ControlFlags.AGENT_CONTROL_AT_POS); + if ((Ignored & ScriptControlled.CONTROL_DOWN) != 0) + flags &= ~((uint)AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG | (uint)AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG); + if ((Ignored & ScriptControlled.CONTROL_UP) != 0) + flags &= ~((uint)AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_POS | (uint)AgentManager.ControlFlags.AGENT_CONTROL_UP_POS); + if ((Ignored & ScriptControlled.CONTROL_LEFT) != 0) + flags &= ~((uint)AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS | (uint)AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_POS); + if ((Ignored & ScriptControlled.CONTROL_RIGHT) != 0) + flags &= ~((uint)AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_NEG | (uint)AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG); + if ((Ignored & ScriptControlled.CONTROL_ROT_LEFT) != 0) + flags &= ~((uint)AgentManager.ControlFlags.AGENT_CONTROL_YAW_NEG); + if ((Ignored & ScriptControlled.CONTROL_ROT_RIGHT) != 0) + flags &= ~((uint)AgentManager.ControlFlags.AGENT_CONTROL_YAW_POS); + if ((Ignored & ScriptControlled.CONTROL_ML_LBUTTON) != 0) + flags &= ~((uint)AgentManager.ControlFlags.AGENT_CONTROL_ML_LBUTTON_DOWN); + if ((Ignored & ScriptControlled.CONTROL_LBUTTON) != 0) + flags &= ~((uint)AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_UP | (uint)AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_DOWN); + //DIR_CONTROL_FLAG_FORWARD = AgentManager.ControlFlags.AGENT_CONTROL_AT_POS, + //DIR_CONTROL_FLAG_BACK = AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG, + //DIR_CONTROL_FLAG_LEFT = AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS, + //DIR_CONTROL_FLAG_RIGHT = AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG, + //DIR_CONTROL_FLAG_UP = AgentManager.ControlFlags.AGENT_CONTROL_UP_POS, + //DIR_CONTROL_FLAG_DOWN = AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG, + //DIR_CONTROL_FLAG_DOWN_NUDGE = AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG + return flags; + } + + private void ItemReceived(UUID itemID) + { + if (IsChildAgent) + return; + + if (null == m_appearance) + { + m_log.Warn("[ATTACHMENT] Appearance has not been initialized"); + return; + } + + int attachpoint = m_appearance.GetAttachpoint(itemID); + if (attachpoint == 0) + return; + + UUID asset = m_appearance.GetAttachedAsset(attachpoint); + if (UUID.Zero == asset) // We have just logged in + { + try + { + // Rez from inventory + asset = m_scene.RezSingleAttachment(ControllingClient, + itemID, (uint)attachpoint); + // Corner case: We are not yet a Scene Entity + // Setting attachment info in RezSingleAttachment will fail + // Set it here + // + m_appearance.SetAttachment((int)attachpoint, itemID, + asset); + m_log.InfoFormat("[ATTACHMENT] Rezzed attachment {0}, inworld asset {1}", + itemID.ToString(), asset); + + } + catch (Exception e) + { + m_log.ErrorFormat("[ATTACHMENT] Unable to rez attachment: {0}", e.ToString()); + } + + return; + } + + SceneObjectPart att = m_scene.GetSceneObjectPart(asset); + + + // If this is null, then the asset has not yet appeared in world + // so we revisit this when it does + // + if (att != null && att.UUID != asset) // Yes. It's really needed + { + m_log.DebugFormat("[ATTACHMENT]: Attach from in world: ItemID {0}, Asset ID {1}, Attachment inworld: {2}", itemID.ToString(), asset.ToString(), att.UUID.ToString()); + + // This will throw if crossing katty-korner + // So catch it here to avoid the noid + // + try + { + // Attach from world, if not already attached + if (att.ParentGroup != null && !att.IsAttachment) + m_scene.AttachObject(ControllingClient, att.ParentGroup.LocalId, (uint)0, Quaternion.Identity, att.ParentGroup.AbsolutePosition, false); + } + catch (NullReferenceException) + { + } + } + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/SceneXmlLoader.cs b/OpenSim/Region/Framework/Scenes/SceneXmlLoader.cs new file mode 100644 index 0000000..7d9d247 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/SceneXmlLoader.cs @@ -0,0 +1,289 @@ +/* + * 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 OpenSim 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 OpenMetaverse; +using log4net; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; + +namespace OpenSim.Region.Framework.Scenes +{ + /// + /// Static methods to serialize and deserialize scene objects to and from XML + /// + public class SceneXmlLoader + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static void LoadPrimsFromXml(Scene scene, string fileName, bool newIDS, Vector3 loadOffset) + { + XmlDocument doc = new XmlDocument(); + XmlNode rootNode; + + if (fileName.StartsWith("http:") || File.Exists(fileName)) + { + XmlTextReader reader = new XmlTextReader(fileName); + reader.WhitespaceHandling = WhitespaceHandling.None; + doc.Load(reader); + reader.Close(); + rootNode = doc.FirstChild; + foreach (XmlNode aPrimNode in rootNode.ChildNodes) + { + SceneObjectGroup obj = new SceneObjectGroup(aPrimNode.OuterXml, true); + + if (newIDS) + { + obj.ResetIDs(); + } + //if we want this to be a import method then we need new uuids for the object to avoid any clashes + //obj.RegenerateFullIDs(); + + scene.AddNewSceneObject(obj, true); + } + } + else + { + throw new Exception("Could not open file " + fileName + " for reading"); + } + } + + public static void SavePrimsToXml(Scene scene, string fileName) + { + FileStream file = new FileStream(fileName, FileMode.Create); + StreamWriter stream = new StreamWriter(file); + int primCount = 0; + stream.WriteLine("\n"); + + List EntityList = scene.GetEntities(); + + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + stream.WriteLine(((SceneObjectGroup) ent).ToXmlString()); + primCount++; + } + } + stream.WriteLine("\n"); + stream.Close(); + file.Close(); + } + + public static string SaveGroupToXml2(SceneObjectGroup grp) + { + return grp.ToXmlString2(); + } + + public static SceneObjectGroup DeserializeGroupFromXml2(string xmlString) + { + XmlDocument doc = new XmlDocument(); + XmlNode rootNode; + + XmlTextReader reader = new XmlTextReader(new StringReader(xmlString)); + reader.WhitespaceHandling = WhitespaceHandling.None; + doc.Load(reader); + reader.Close(); + rootNode = doc.FirstChild; + + // This is to deal with neighbouring regions that are still surrounding the group xml with the + // tag. It should be possible to remove the first part of this if statement once we go past 0.5.9 (or + // when some other changes forces all regions to upgrade). + // This might seem rather pointless since prim crossing from this revision to an earlier revision remains + // broken. But it isn't much work to accomodate the old format here. + if (rootNode.LocalName.Equals("scene")) + { + foreach (XmlNode aPrimNode in rootNode.ChildNodes) + { + // There is only ever one prim. This oddity should be removeable post 0.5.9 + return new SceneObjectGroup(aPrimNode.OuterXml); + } + + return null; + } + else + { + return new SceneObjectGroup(rootNode.OuterXml); + } + } + + /// + /// Load prims from the xml2 format + /// + /// + /// + public static void LoadPrimsFromXml2(Scene scene, string fileName) + { + LoadPrimsFromXml2(scene, new XmlTextReader(fileName), false); + } + + /// + /// Load prims from the xml2 format + /// + /// + /// + /// + public static void LoadPrimsFromXml2(Scene scene, TextReader reader, bool startScripts) + { + LoadPrimsFromXml2(scene, new XmlTextReader(reader), startScripts); + } + + /// + /// Load prims from the xml2 format. This method will close the reader + /// + /// + /// + /// + protected static void LoadPrimsFromXml2(Scene scene, XmlTextReader reader, bool startScripts) + { + XmlDocument doc = new XmlDocument(); + reader.WhitespaceHandling = WhitespaceHandling.None; + doc.Load(reader); + reader.Close(); + XmlNode rootNode = doc.FirstChild; + + ICollection sceneObjects = new List(); + foreach (XmlNode aPrimNode in rootNode.ChildNodes) + { + SceneObjectGroup obj = CreatePrimFromXml2(scene, aPrimNode.OuterXml); + if (obj != null && startScripts) + sceneObjects.Add(obj); + } + + foreach (SceneObjectGroup sceneObject in sceneObjects) + { + sceneObject.CreateScriptInstances(0, true, scene.DefaultScriptEngine, 0); + } + } + + /// + /// Create a prim from the xml2 representation. + /// + /// + /// + /// The scene object created. null if the scene object already existed + protected static SceneObjectGroup CreatePrimFromXml2(Scene scene, string xmlData) + { + SceneObjectGroup obj = new SceneObjectGroup(xmlData); + + if (scene.AddRestoredSceneObject(obj, true, false)) + return obj; + else + return null; + } + + public static void SavePrimsToXml2(Scene scene, string fileName) + { + List EntityList = scene.GetEntities(); + + SavePrimListToXml2(EntityList, fileName); + } + + public static void SavePrimsToXml2(Scene scene, TextWriter stream, Vector3 min, Vector3 max) + { + List EntityList = scene.GetEntities(); + + SavePrimListToXml2(EntityList, stream, min, max); + } + + public static void SaveNamedPrimsToXml2(Scene scene, string primName, string fileName) + { + m_log.InfoFormat( + "[SERIALISER]: Saving prims with name {0} in xml2 format for region {1} to {2}", + primName, scene.RegionInfo.RegionName, fileName); + + List entityList = scene.GetEntities(); + List primList = new List(); + + foreach (EntityBase ent in entityList) + { + if (ent is SceneObjectGroup) + { + if (ent.Name == primName) + { + primList.Add(ent); + } + } + } + + SavePrimListToXml2(primList, fileName); + } + + public static void SavePrimListToXml2(List entityList, string fileName) + { + FileStream file = new FileStream(fileName, FileMode.Create); + try + { + StreamWriter stream = new StreamWriter(file); + try + { + SavePrimListToXml2(entityList, stream, Vector3.Zero, Vector3.Zero); + } + finally + { + stream.Close(); + } + } + finally + { + file.Close(); + } + } + + public static void SavePrimListToXml2(List entityList, TextWriter stream, Vector3 min, Vector3 max) + { + int primCount = 0; + stream.WriteLine("\n"); + + foreach (EntityBase ent in entityList) + { + if (ent is SceneObjectGroup) + { + SceneObjectGroup g = (SceneObjectGroup)ent; + if (!min.Equals(Vector3.Zero) || !max.Equals(Vector3.Zero)) + { + Vector3 pos = g.RootPart.GetWorldPosition(); + if (min.X > pos.X || min.Y > pos.Y || min.Z > pos.Z) + continue; + if (max.X < pos.X || max.Y < pos.Y || max.Z < pos.Z) + continue; + } + + stream.WriteLine(g.ToXmlString2()); + primCount++; + } + } + stream.WriteLine("\n"); + stream.Flush(); + } + + } +} diff --git a/OpenSim/Region/Framework/Scenes/Scripting/IScriptHost.cs b/OpenSim/Region/Framework/Scenes/Scripting/IScriptHost.cs new file mode 100644 index 0000000..440820b --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Scripting/IScriptHost.cs @@ -0,0 +1,46 @@ +/* + * 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 OpenSim 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 OpenMetaverse; + +namespace OpenSim.Region.Framework.Scenes.Scripting +{ + public interface IScriptHost + { + string Name { get; set; } + string Description { get; set; } + + UUID UUID { get; } + UUID ObjectOwner { get; } + UUID ObjectCreator { get; } + Vector3 AbsolutePosition { get; } + + string SitName { get; set; } + string TouchName { get; set; } + void SetText(string text, Vector3 color, double alpha); + } +} diff --git a/OpenSim/Region/Framework/Scenes/Scripting/NullScriptHost.cs b/OpenSim/Region/Framework/Scenes/Scripting/NullScriptHost.cs new file mode 100644 index 0000000..fd7ff96 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Scripting/NullScriptHost.cs @@ -0,0 +1,86 @@ +/* + * 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 OpenSim 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 OpenMetaverse; + +namespace OpenSim.Region.Framework.Scenes.Scripting +{ + public class NullScriptHost : IScriptHost + { + private Vector3 m_pos = new Vector3(128, 128, 30); + + public string Name + { + get { return "Object"; } + set { } + } + + public string SitName + { + get { return String.Empty; } + set { } + } + + public string TouchName + { + get { return String.Empty; } + set { } + } + + public string Description + { + get { return String.Empty; } + set { } + } + + public UUID UUID + { + get { return UUID.Zero; } + } + + public UUID ObjectOwner + { + get { return UUID.Zero; } + } + + public UUID ObjectCreator + { + get { return UUID.Zero; } + } + + public Vector3 AbsolutePosition + { + get { return m_pos; } + } + + public void SetText(string text, Vector3 color, double alpha) + { + Console.WriteLine("Tried to SetText [{0}] on NullScriptHost", text); + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/Scripting/ScriptEngineInterface.cs b/OpenSim/Region/Framework/Scenes/Scripting/ScriptEngineInterface.cs new file mode 100644 index 0000000..d358187 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Scripting/ScriptEngineInterface.cs @@ -0,0 +1,38 @@ +/* + * 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 OpenSim 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. + */ + +//TODO: WHERE TO PLACE THIS? + +namespace OpenSim.Region.Framework.Scenes.Scripting +{ + public interface ScriptEngineInterface + { + void InitializeEngine(Scene Sceneworld); + void Shutdown(); +// void StartScript(string ScriptID, IScriptHost ObjectID); + } +} diff --git a/OpenSim/Region/Framework/Scenes/Scripting/ScriptEngineLoader.cs b/OpenSim/Region/Framework/Scenes/Scripting/ScriptEngineLoader.cs new file mode 100644 index 0000000..58228cc --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Scripting/ScriptEngineLoader.cs @@ -0,0 +1,119 @@ +/* + * 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 OpenSim 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. + */ + +/* Original code: Tedd Hansen */ +using System; +using System.IO; +using System.Reflection; +using log4net; + +namespace OpenSim.Region.Framework.Scenes.Scripting +{ + public class ScriptEngineLoader + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public ScriptEngineInterface LoadScriptEngine(string EngineName) + { + ScriptEngineInterface ret = null; + try + { + ret = + LoadAndInitAssembly( + Path.Combine("ScriptEngines", "OpenSim.Region.ScriptEngine." + EngineName + ".dll"), + "OpenSim.Region.ScriptEngine." + EngineName + ".ScriptEngine"); + } + catch (Exception e) + { + m_log.Error("[ScriptEngine]: " + + "Error loading assembly \"" + EngineName + "\": " + e.Message + ", " + + e.StackTrace.ToString()); + } + return ret; + } + + /// + /// Does actual loading and initialization of script Assembly + /// + /// AppDomain to load script into + /// FileName of script assembly (.dll) + /// + private ScriptEngineInterface LoadAndInitAssembly(string FileName, string NameSpace) + { + //Common.SendToDebug("Loading ScriptEngine Assembly " + FileName); + // Load .Net Assembly (.dll) + // Initialize and return it + + // TODO: Add error handling + + Assembly a; + //try + //{ + + + // Load to default appdomain (temporary) + a = Assembly.LoadFrom(FileName); + // Load to specified appdomain + // TODO: Insert security + //a = FreeAppDomain.Load(FileName); + //} + //catch (Exception e) + //{ + // m_log.Error("[ScriptEngine]: Error loading assembly \String.Empty + FileName + "\": " + e.ToString()); + //} + + + //Console.WriteLine("Loading: " + FileName); + //foreach (Type _t in a.GetTypes()) + //{ + // Console.WriteLine("Type: " + _t.ToString()); + //} + + Type t; + //try + //{ + t = a.GetType(NameSpace, true); + //} + //catch (Exception e) + //{ + // m_log.Error("[ScriptEngine]: Error initializing type \String.Empty + NameSpace + "\" from \String.Empty + FileName + "\": " + e.ToString()); + //} + + ScriptEngineInterface ret; + //try + //{ + ret = (ScriptEngineInterface) Activator.CreateInstance(t); + //} + //catch (Exception e) + //{ + // m_log.Error("[ScriptEngine]: Error initializing type \String.Empty + NameSpace + "\" from \String.Empty + FileName + "\": " + e.ToString()); + //} + + return ret; + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs new file mode 100644 index 0000000..b2cea5d --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs @@ -0,0 +1,450 @@ +/* + * 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 OpenSim 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.Timers; +using OpenMetaverse.Packets; +using OpenSim.Framework; +using OpenSim.Framework.Statistics; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.Framework.Scenes +{ + public class SimStatsReporter + { + public delegate void SendStatResult(SimStats stats); + + public delegate void YourStatsAreWrong(); + + public event SendStatResult OnSendStatsResult; + + public event YourStatsAreWrong OnStatsIncorrect; + + private SendStatResult handlerSendStatResult = null; + + private YourStatsAreWrong handlerStatsIncorrect = null; + + private enum Stats : uint + { + TimeDilation = 0, + SimFPS = 1, + PhysicsFPS = 2, + AgentUpdates = 3, + FrameMS = 4, + NetMS = 5, + OtherMS = 6, + PhysicsMS = 7, + AgentMS = 8, + ImageMS = 9, + ScriptMS = 10, + TotalPrim = 11, + ActivePrim = 12, + Agents = 13, + ChildAgents = 14, + ActiveScripts = 15, + ScriptLinesPerSecond = 16, + InPacketsPerSecond = 17, + OutPacketsPerSecond = 18, + PendingDownloads = 19, + PendingUploads = 20, + UnAckedBytes = 24, + } + + // Sending a stats update every 3 seconds + private int statsUpdatesEveryMS = 3000; + private float statsUpdateFactor = 0; + private float m_timeDilation = 0; + private int m_fps = 0; + // saved last reported value so there is something available for llGetRegionFPS + private float lastReportedSimFPS = 0; + private float m_pfps = 0; + private int m_agentUpdates = 0; + + private int m_frameMS = 0; + private int m_netMS = 0; + private int m_agentMS = 0; + private int m_physicsMS = 0; + private int m_imageMS = 0; + private int m_otherMS = 0; + +//Ckrinke: (3-21-08) Comment out to remove a compiler warning. Bring back into play when needed. +//Ckrinke private int m_scriptMS = 0; + + private int m_rootAgents = 0; + private int m_childAgents = 0; + private int m_numPrim = 0; + private int m_inPacketsPerSecond = 0; + private int m_outPacketsPerSecond = 0; + private int m_activePrim = 0; + private int m_unAckedBytes = 0; + private int m_pendingDownloads = 0; + private int m_pendingUploads = 0; + private int m_activeScripts = 0; + private int m_scriptLinesPerSecond = 0; + + private int objectCapacity = 45000; + + private Scene m_scene; + + private RegionInfo ReportingRegion; + + private Timer m_report = new Timer(); + + + public SimStatsReporter(Scene scene) + { + statsUpdateFactor = (float)(statsUpdatesEveryMS / 1000); + m_scene = scene; + ReportingRegion = scene.RegionInfo; + + m_report.AutoReset = true; + m_report.Interval = statsUpdatesEveryMS; + m_report.Elapsed += new ElapsedEventHandler(statsHeartBeat); + m_report.Enabled = true; + + if (StatsManager.SimExtraStats != null) + OnSendStatsResult += StatsManager.SimExtraStats.ReceiveClassicSimStatsPacket; + } + + public void SetUpdateMS(int ms) + { + statsUpdatesEveryMS = ms; + statsUpdateFactor = (float)(statsUpdatesEveryMS / 1000); + m_report.Interval = statsUpdatesEveryMS; + } + + private void statsHeartBeat(object sender, EventArgs e) + { + SimStatsPacket.StatBlock[] sb = new SimStatsPacket.StatBlock[21]; + SimStatsPacket.RegionBlock rb = new SimStatsPacket.RegionBlock(); + + // Know what's not thread safe in Mono... modifying timers. + // System.Console.WriteLine("Firing Stats Heart Beat"); + lock (m_report) + { + uint regionFlags = 0; + + try + { + IEstateModule estateModule = m_scene.RequestModuleInterface(); + regionFlags = estateModule != null ? estateModule.GetRegionFlags() : (uint) 0; + } + catch (Exception) + { + // leave region flags at 0 + } + +#region various statistic googly moogly + + // Our FPS is actually 10fps, so multiplying by 5 to get the amount that people expect there + // 0-50 is pretty close to 0-45 + float simfps = (int) ((m_fps * 5)); + // save the reported value so there is something available for llGetRegionFPS + lastReportedSimFPS = (float)simfps / statsUpdateFactor; + + //if (simfps > 45) + //simfps = simfps - (simfps - 45); + //if (simfps < 0) + //simfps = 0; + + // + float physfps = ((m_pfps / 1000)); + + //if (physfps > 600) + //physfps = physfps - (physfps - 600); + + if (physfps < 0) + physfps = 0; + +#endregion + + //Our time dilation is 0.91 when we're running a full speed, + // therefore to make sure we get an appropriate range, + // we have to factor in our error. (0.10f * statsUpdateFactor) + // multiplies the fix for the error times the amount of times it'll occur a second + // / 10 divides the value by the number of times the sim heartbeat runs (10fps) + // Then we divide the whole amount by the amount of seconds pass in between stats updates. + + for (int i = 0; i<21;i++) + { + sb[i] = new SimStatsPacket.StatBlock(); + } + + sb[0].StatID = (uint) Stats.TimeDilation; + sb[0].StatValue = (Single.IsNaN(m_timeDilation)) ? 0.1f : m_timeDilation ; //((((m_timeDilation + (0.10f * statsUpdateFactor)) /10) / statsUpdateFactor)); + + sb[1].StatID = (uint) Stats.SimFPS; + sb[1].StatValue = simfps/statsUpdateFactor; + + sb[2].StatID = (uint) Stats.PhysicsFPS; + sb[2].StatValue = physfps / statsUpdateFactor; + + sb[3].StatID = (uint) Stats.AgentUpdates; + sb[3].StatValue = (m_agentUpdates / statsUpdateFactor); + + sb[4].StatID = (uint) Stats.Agents; + sb[4].StatValue = m_rootAgents; + + sb[5].StatID = (uint) Stats.ChildAgents; + sb[5].StatValue = m_childAgents; + + sb[6].StatID = (uint) Stats.TotalPrim; + sb[6].StatValue = m_numPrim; + + sb[7].StatID = (uint) Stats.ActivePrim; + sb[7].StatValue = m_activePrim; + + sb[8].StatID = (uint)Stats.FrameMS; + sb[8].StatValue = m_frameMS / statsUpdateFactor; + + sb[9].StatID = (uint)Stats.NetMS; + sb[9].StatValue = m_netMS / statsUpdateFactor; + + sb[10].StatID = (uint)Stats.PhysicsMS; + sb[10].StatValue = m_physicsMS / statsUpdateFactor; + + sb[11].StatID = (uint)Stats.ImageMS ; + sb[11].StatValue = m_imageMS / statsUpdateFactor; + + sb[12].StatID = (uint)Stats.OtherMS; + sb[12].StatValue = m_otherMS / statsUpdateFactor; + + sb[13].StatID = (uint)Stats.InPacketsPerSecond; + sb[13].StatValue = (m_inPacketsPerSecond); + + sb[14].StatID = (uint)Stats.OutPacketsPerSecond; + sb[14].StatValue = (m_outPacketsPerSecond / statsUpdateFactor); + + sb[15].StatID = (uint)Stats.UnAckedBytes; + sb[15].StatValue = m_unAckedBytes; + + sb[16].StatID = (uint)Stats.AgentMS; + sb[16].StatValue = m_agentMS / statsUpdateFactor; + + sb[17].StatID = (uint)Stats.PendingDownloads; + sb[17].StatValue = m_pendingDownloads; + + sb[18].StatID = (uint)Stats.PendingUploads; + sb[18].StatValue = m_pendingUploads; + + sb[19].StatID = (uint)Stats.ActiveScripts; + sb[19].StatValue = m_activeScripts; + + sb[20].StatID = (uint)Stats.ScriptLinesPerSecond; + sb[20].StatValue = m_scriptLinesPerSecond / statsUpdateFactor; + + SimStats simStats + = new SimStats( + ReportingRegion.RegionLocX, ReportingRegion.RegionLocY, regionFlags, (uint)objectCapacity, rb, sb, m_scene.RegionInfo.originRegionID); + + handlerSendStatResult = OnSendStatsResult; + if (handlerSendStatResult != null) + { + handlerSendStatResult(simStats); + } + resetvalues(); + } + } + + private void resetvalues() + { + m_timeDilation = 0; + m_fps = 0; + m_pfps = 0; + m_agentUpdates = 0; + m_inPacketsPerSecond = 0; + m_outPacketsPerSecond = 0; + m_unAckedBytes = 0; + m_scriptLinesPerSecond = 0; + + m_frameMS = 0; + m_agentMS = 0; + m_netMS = 0; + m_physicsMS = 0; + m_imageMS = 0; + m_otherMS = 0; + +//Ckrinke This variable is not used, so comment to remove compiler warning until it is used. +//Ckrinke m_scriptMS = 0; + } + + # region methods called from Scene + // The majority of these functions are additive + // so that you can easily change the amount of + // seconds in between sim stats updates + + public void AddTimeDilation(float td) + { + //float tdsetting = td; + //if (tdsetting > 1.0f) + //tdsetting = (tdsetting - (tdsetting - 0.91f)); + + //if (tdsetting < 0) + //tdsetting = 0.0f; + m_timeDilation = td; + } + + public void SetRootAgents(int rootAgents) + { + m_rootAgents = rootAgents; + CheckStatSanity(); + + } + + internal void CheckStatSanity() + { + if (m_rootAgents < 0 || m_childAgents < 0) + { + handlerStatsIncorrect = OnStatsIncorrect; + if (handlerStatsIncorrect != null) + { + handlerStatsIncorrect(); + } + } + if (m_rootAgents == 0 && m_childAgents == 0) + { + m_unAckedBytes = 0; + } + } + + public void SetChildAgents(int childAgents) + { + m_childAgents = childAgents; + CheckStatSanity(); + } + + public void SetObjects(int objects) + { + m_numPrim = objects; + } + + public void SetActiveObjects(int objects) + { + m_activePrim = objects; + } + + public void AddFPS(int frames) + { + m_fps += frames; + } + + public void AddPhysicsFPS(float frames) + { + m_pfps += frames; + } + + public void AddAgentUpdates(int numUpdates) + { + m_agentUpdates += numUpdates; + } + + public void AddInPackets(int numPackets) + { + m_inPacketsPerSecond += numPackets; + } + + public void AddOutPackets(int numPackets) + { + m_outPacketsPerSecond += numPackets; + } + + public void AddunAckedBytes(int numBytes) + { + m_unAckedBytes += numBytes; + if (m_unAckedBytes < 0) m_unAckedBytes = 0; + } + + public void addFrameMS(int ms) + { + m_frameMS += ms; + } + public void addNetMS(int ms) + { + m_netMS += ms; + } + public void addAgentMS(int ms) + { + m_agentMS += ms; + } + public void addPhysicsMS(int ms) + { + m_physicsMS += ms; + } + public void addImageMS(int ms) + { + m_imageMS += ms; + } + public void addOtherMS(int ms) + { + m_otherMS += ms; + } + +// private static readonly log4net.ILog m_log +// = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public void addPendingDownload(int count) + { + m_pendingDownloads += count; + if (m_pendingDownloads < 0) m_pendingDownloads = 0; + //m_log.InfoFormat("[stats]: Adding {0} to pending downloads to make {1}", count, m_pendingDownloads); + } + + public void addScriptLines(int count) + { + m_scriptLinesPerSecond += count; + } + + public void SetActiveScripts(int count) + { + m_activeScripts = count; + } + + public void SetObjectCapacity(int objects) + { + objectCapacity = objects; + } + + /// + /// This is for llGetRegionFPS + /// + public float getLastReportedSimFPS() + { + return lastReportedSimFPS; + } + + public void AddPacketsFromClientStats(int inPackets, int outPackets, int unAckedBytes) + { + AddInPackets(inPackets); + AddOutPackets(outPackets); + AddunAckedBytes(unAckedBytes); + + } + + #endregion + } +} diff --git a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs new file mode 100644 index 0000000..55c5181 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs @@ -0,0 +1,242 @@ +/* + * 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 OpenSim 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 OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using System; +using System.Text; +using System.Xml; +using System.IO; +using System.Xml.Serialization; + +namespace OpenSim.Region.Framework.Scenes +{ + /// + /// A new version of the old Channel class, simplified + /// + public class TerrainChannel : ITerrainChannel + { + private readonly bool[,] taint; + private double[,] map; + + public TerrainChannel() + { + map = new double[Constants.RegionSize, Constants.RegionSize]; + taint = new bool[Constants.RegionSize / 16,Constants.RegionSize / 16]; + + int x; + for (x = 0; x < Constants.RegionSize; x++) + { + int y; + for (y = 0; y < Constants.RegionSize; y++) + { + map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10; + double spherFacA = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 50) * 0.01; + double spherFacB = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 100) * 0.001; + if (map[x, y] < spherFacA) + map[x, y] = spherFacA; + if (map[x, y] < spherFacB) + map[x, y] = spherFacB; + } + } + } + + public TerrainChannel(double[,] import) + { + map = import; + taint = new bool[import.GetLength(0),import.GetLength(1)]; + } + + public TerrainChannel(bool createMap) + { + if (createMap) + { + map = new double[Constants.RegionSize,Constants.RegionSize]; + taint = new bool[Constants.RegionSize / 16,Constants.RegionSize / 16]; + } + } + + public TerrainChannel(int w, int h) + { + map = new double[w,h]; + taint = new bool[w / 16,h / 16]; + } + + #region ITerrainChannel Members + + public int Width + { + get { return map.GetLength(0); } + } + + public int Height + { + get { return map.GetLength(1); } + } + + public ITerrainChannel MakeCopy() + { + TerrainChannel copy = new TerrainChannel(false); + copy.map = (double[,]) map.Clone(); + + return copy; + } + + public float[] GetFloatsSerialised() + { + // Move the member variables into local variables, calling + // member variables 256*256 times gets expensive + int w = Width; + int h = Height; + float[] heights = new float[w * h]; + + int i, j; // map coordinates + int idx = 0; // index into serialized array + for (i = 0; i < h; i++) + { + for (j = 0; j < w; j++) + { + heights[idx++] = (float)map[j, i]; + } + } + + return heights; + } + + public double[,] GetDoubles() + { + return map; + } + + public double this[int x, int y] + { + get { return map[x, y]; } + set + { + // Will "fix" terrain hole problems. Although not fantastically. + if (Double.IsNaN(value) || Double.IsInfinity(value)) + return; + + if (map[x, y] != value) + { + taint[x / 16, y / 16] = true; + map[x, y] = value; + } + } + } + + public bool Tainted(int x, int y) + { + if (taint[x / 16, y / 16]) + { + taint[x / 16, y / 16] = false; + return true; + } + return false; + } + + #endregion + + public TerrainChannel Copy() + { + TerrainChannel copy = new TerrainChannel(false); + copy.map = (double[,]) map.Clone(); + + return copy; + } + + public string SaveToXmlString() + { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + using (StringWriter sw = new StringWriter()) + { + using (XmlWriter writer = XmlWriter.Create(sw, settings)) + { + WriteXml(writer); + } + string output = sw.ToString(); + return output; + } + } + + private void WriteXml(XmlWriter writer) + { + writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty); + ToXml(writer); + writer.WriteEndElement(); + } + + public void LoadFromXmlString(string data) + { + StringReader sr = new StringReader(data); + XmlTextReader reader = new XmlTextReader(sr); + reader.Read(); + + ReadXml(reader); + reader.Close(); + sr.Close(); + } + + private void ReadXml(XmlReader reader) + { + reader.ReadStartElement("TerrainMap"); + FromXml(reader); + } + + private void ToXml(XmlWriter xmlWriter) + { + float[] mapData = GetFloatsSerialised(); + byte[] buffer = new byte[mapData.Length * 4]; + for (int i = 0; i < mapData.Length; i++) + { + byte[] value = BitConverter.GetBytes(mapData[i]); + Array.Copy(value, 0, buffer, (i * 4), 4); + } + XmlSerializer serializer = new XmlSerializer(typeof(byte[])); + serializer.Serialize(xmlWriter, buffer); + } + + private void FromXml(XmlReader xmlReader) + { + XmlSerializer serializer = new XmlSerializer(typeof(byte[])); + byte[] dataArray = (byte[])serializer.Deserialize(xmlReader); + int index = 0; + + for (int y = 0; y < Height; y++) + { + for (int x = 0; x < Width; x++) + { + float value; + value = BitConverter.ToSingle(dataArray, index); + index += 4; + this[x, y] = (double)value; + } + } + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/TerrainUtil.cs b/OpenSim/Region/Framework/Scenes/TerrainUtil.cs new file mode 100644 index 0000000..318a682 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/TerrainUtil.cs @@ -0,0 +1,132 @@ +/* + * 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 OpenSim 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 OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.Framework.Scenes +{ + public static class TerrainUtil + { + public static double MetersToSphericalStrength(double size) + { + //return Math.Pow(2, size); + return (size + 1) * 1.35; // MCP: a more useful brush size range + } + + public static double SphericalFactor(double x, double y, double rx, double ry, double size) + { + return size * size - ((x - rx) * (x - rx) + (y - ry) * (y - ry)); + } + + public static double GetBilinearInterpolate(double x, double y, ITerrainChannel map) + { + int w = map.Width; + int h = map.Height; + + if (x > w - 2.0) + x = w - 2.0; + if (y > h - 2.0) + y = h - 2.0; + if (x < 0.0) + x = 0.0; + if (y < 0.0) + y = 0.0; + + const int stepSize = 1; + double h00 = map[(int) x, (int) y]; + double h10 = map[(int) x + stepSize, (int) y]; + double h01 = map[(int) x, (int) y + stepSize]; + double h11 = map[(int) x + stepSize, (int) y + stepSize]; + double h1 = h00; + double h2 = h10; + double h3 = h01; + double h4 = h11; + double a00 = h1; + double a10 = h2 - h1; + double a01 = h3 - h1; + double a11 = h1 - h2 - h3 + h4; + double partialx = x - (int) x; + double partialz = y - (int) y; + double hi = a00 + (a10 * partialx) + (a01 * partialz) + (a11 * partialx * partialz); + return hi; + } + + private static double Noise(double x, double y) + { + int n = (int) x + (int) (y * 749); + n = (n << 13) ^ n; + return (1.0 - ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0); + } + + private static double SmoothedNoise1(double x, double y) + { + double corners = (Noise(x - 1, y - 1) + Noise(x + 1, y - 1) + Noise(x - 1, y + 1) + Noise(x + 1, y + 1)) / 16; + double sides = (Noise(x - 1, y) + Noise(x + 1, y) + Noise(x, y - 1) + Noise(x, y + 1)) / 8; + double center = Noise(x, y) / 4; + return corners + sides + center; + } + + private static double Interpolate(double x, double y, double z) + { + return (x * (1.0 - z)) + (y * z); + } + + public static double InterpolatedNoise(double x, double y) + { + int integer_X = (int) (x); + double fractional_X = x - integer_X; + + int integer_Y = (int) y; + double fractional_Y = y - integer_Y; + + double v1 = SmoothedNoise1(integer_X, integer_Y); + double v2 = SmoothedNoise1(integer_X + 1, integer_Y); + double v3 = SmoothedNoise1(integer_X, integer_Y + 1); + double v4 = SmoothedNoise1(integer_X + 1, integer_Y + 1); + + double i1 = Interpolate(v1, v2, fractional_X); + double i2 = Interpolate(v3, v4, fractional_X); + + return Interpolate(i1, i2, fractional_Y); + } + + public static double PerlinNoise2D(double x, double y, int octaves, double persistence) + { + double total = 0.0; + + for (int i = 0; i < octaves; i++) + { + double frequency = Math.Pow(2, i); + double amplitude = Math.Pow(persistence, i); + + total += InterpolatedNoise(x * frequency, y * frequency) * amplitude; + } + return total; + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/Tests/EntityManagerTests.cs b/OpenSim/Region/Framework/Scenes/Tests/EntityManagerTests.cs new file mode 100644 index 0000000..2c6ccc8 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Tests/EntityManagerTests.cs @@ -0,0 +1,176 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSim 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.Threading; +using System.Text; +using System.Collections.Generic; +using Nini.Config; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Tests.Common.Setup; + +namespace OpenSim.Region.Framework.Scenes.Tests +{ + [TestFixture] + public class EntityManagerTests + { + static public Random random; + SceneObjectGroup found; + Scene scene = SceneSetupHelpers.SetupScene(); + + [Test] + public void T010_AddObjects() + { + random = new Random(); + SceneObjectGroup found; + EntityManager entman = new EntityManager(); + SceneObjectGroup sog = NewSOG(); + UUID obj1 = sog.UUID; + uint li1 = sog.LocalId; + entman.Add(sog); + sog = NewSOG(); + UUID obj2 = sog.UUID; + uint li2 = sog.LocalId; + entman.Add(sog); + + found = (SceneObjectGroup)entman[obj1]; + Assert.That(found.UUID ,Is.EqualTo(obj1) ); + found = (SceneObjectGroup)entman[li1]; + Assert.That(found.UUID ,Is.EqualTo(obj1) ); + found = (SceneObjectGroup)entman[obj2]; + Assert.That(found.UUID ,Is.EqualTo(obj2) ); + found = (SceneObjectGroup)entman[li2]; + Assert.That(found.UUID ,Is.EqualTo(obj2) ); + + entman.Remove(obj1); + entman.Remove(li2); + + Assert.That(entman.ContainsKey(obj1), Is.False); + Assert.That(entman.ContainsKey(li1), Is.False); + Assert.That(entman.ContainsKey(obj2), Is.False); + Assert.That(entman.ContainsKey(li2), Is.False); + } + + [Test] + public void T011_ThreadAddRemoveTest() + { + // This test adds and removes with mutiple threads, attempting to break the + // uuid and localid dictionary coherence. + EntityManager entman = new EntityManager(); + SceneObjectGroup sog = NewSOG(); + for (int j=0; j<20; j++) + { + List trdlist = new List(); + + for (int i=0; i<4; i++) + { + // Adds scene object + NewTestThreads test = new NewTestThreads(entman,sog); + Thread start = new Thread(new ThreadStart(test.TestAddSceneObject)); + start.Start(); + trdlist.Add(start); + + // Removes it + test = new NewTestThreads(entman,sog); + start = new Thread(new ThreadStart(test.TestRemoveSceneObject)); + start.Start(); + trdlist.Add(start); + } + foreach (Thread thread in trdlist) + { + thread.Join(); + } + if (entman.ContainsKey(sog.UUID) || entman.ContainsKey(sog.LocalId)) { + found = (SceneObjectGroup)entman[sog.UUID]; + Assert.That(found.UUID,Is.EqualTo(sog.UUID)); + found = (SceneObjectGroup)entman[sog.LocalId]; + Assert.That(found.UUID,Is.EqualTo(sog.UUID)); + } + } + } + + private SceneObjectGroup NewSOG() + { + SceneObjectGroup sog = new SceneObjectGroup(); + SceneObjectPart sop = new SceneObjectPart(UUID.Random(), PrimitiveBaseShape.Default, Vector3.Zero, Quaternion.Identity, Vector3.Zero); + sop.Name = RandomName(); + sop.Description = sop.Name; + sop.Text = RandomName(); + sop.SitName = RandomName(); + sop.TouchName = RandomName(); + sop.ObjectFlags |= (uint)PrimFlags.Phantom; + + sog.SetRootPart(sop); + + scene.AddNewSceneObject(sog, false); + + return sog; + } + + private static string RandomName() + { + StringBuilder name = new StringBuilder(); + int size = random.Next(40,80); + char ch ; + for (int i=0; i + /// Basic scene object tests (create, read and delete but not update). + /// + [TestFixture] + public class SceneObjectTests + { + /// + /// Test adding an object to a scene. + /// + [Test] + public void TestAddSceneObject() + { + Scene scene = SceneSetupHelpers.SetupScene(); + SceneObjectPart part = SceneSetupHelpers.AddSceneObject(scene); + SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId); + + //System.Console.WriteLine("retrievedPart : {0}", retrievedPart); + // If the parts have the same UUID then we will consider them as one and the same + Assert.That(retrievedPart.UUID, Is.EqualTo(part.UUID)); + } + + /// + /// Test deleting an object from a scene. + /// + [Test] + public void TestDeleteSceneObject() + { + TestScene scene = SceneSetupHelpers.SetupScene(); + SceneObjectPart part = SceneSetupHelpers.AddSceneObject(scene); + scene.DeleteSceneObject(part.ParentGroup, false); + + SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId); + Assert.That(retrievedPart, Is.Null); + } + + /// + /// Test deleting an object asynchronously + /// + [Test] + public void TestDeleteSceneObjectAsync() + { + UUID agentId = UUID.Parse("00000000-0000-0000-0000-000000000001"); + + TestScene scene = SceneSetupHelpers.SetupScene(); + + // Turn off the timer on the async sog deleter - we'll crank it by hand for this test. + AsyncSceneObjectGroupDeleter sogd = scene.SceneObjectGroupDeleter; + sogd.Enabled = false; + + SceneObjectPart part = SceneSetupHelpers.AddSceneObject(scene); + + IClientAPI client = SceneSetupHelpers.AddRootAgent(scene, agentId); + scene.DeRezObject(client, part.LocalId, UUID.Zero, DeRezAction.Delete, UUID.Zero); + + SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId); + Assert.That(retrievedPart, Is.Not.Null); + + sogd.InventoryDeQueueAndDelete(); + SceneObjectPart retrievedPart2 = scene.GetSceneObjectPart(part.LocalId); + Assert.That(retrievedPart2, Is.Null); + } + + /// + /// Test deleting an object asynchronously to user inventory. + /// + [Test] + public void TestDeleteSceneObjectAsyncToUserInventory() + { + //log4net.Config.XmlConfigurator.Configure(); + + UUID agentId = UUID.Parse("00000000-0000-0000-0000-000000000001"); + string myObjectName = "Fred"; + + TestScene scene = SceneSetupHelpers.SetupScene(); + SceneObjectPart part = SceneSetupHelpers.AddSceneObject(scene, myObjectName); + + Assert.That( + scene.CommsManager.UserAdminService.AddUser( + "Bob", "Hoskins", "test", "test@test.com", 1000, 1000, agentId), + Is.EqualTo(agentId)); + + IClientAPI client = SceneSetupHelpers.AddRootAgent(scene, agentId); + + CachedUserInfo userInfo = scene.CommsManager.UserProfileCacheService.GetUserDetails(agentId); + Assert.That(userInfo, Is.Not.Null); + Assert.That(userInfo.RootFolder, Is.Not.Null); + + SceneSetupHelpers.DeleteSceneObjectAsync(scene, part, DeRezAction.Take, userInfo.RootFolder.ID, client); + + // Check that we now have the taken part in our inventory + Assert.That(myObjectName, Is.EqualTo(userInfo.RootFolder.FindItemByPath(myObjectName).Name)); + + // Check that the taken part has actually disappeared + SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId); + Assert.That(retrievedPart, Is.Null); + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectLinkingTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectLinkingTests.cs new file mode 100644 index 0000000..30b0987 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectLinkingTests.cs @@ -0,0 +1,249 @@ +/* + * 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 OpenSim 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 NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Region.Communications.Local; +using OpenSim.Region.Environment.Scenes; +using OpenSim.Tests.Common.Mock; +using OpenSim.Tests.Common.Setup; + +namespace OpenSim.Region.Environment.Scenes.Tests +{ + /// + /// Linking tests + /// + [TestFixture] + public class SceneObjectLinkingTests + { + [Test] + public void TestLinkDelink2SceneObjects() + { + bool debugtest = false; + + Scene scene = SceneSetupHelpers.SetupScene(); + SceneObjectPart part1 = SceneSetupHelpers.AddSceneObject(scene); + SceneObjectGroup grp1 = part1.ParentGroup; + SceneObjectPart part2 = SceneSetupHelpers.AddSceneObject(scene); + SceneObjectGroup grp2 = part2.ParentGroup; + + grp1.AbsolutePosition = new Vector3(10, 10, 10); + grp2.AbsolutePosition = Vector3.Zero; + + // <90,0,0> + grp1.Rotation = (Quaternion.CreateFromEulers(90 * Utils.DEG_TO_RAD, 0, 0)); + + // <180,0,0> + grp2.UpdateGroupRotation(Quaternion.CreateFromEulers(180 * Utils.DEG_TO_RAD, 0, 0)); + + // Required for linking + grp1.RootPart.UpdateFlag = 0; + grp2.RootPart.UpdateFlag = 0; + + // Link grp2 to grp1. part2 becomes child prim to grp1. grp2 is eliminated. + grp1.LinkToGroup(grp2); + + // FIXME: Can't do this test yet since group 2 still has its root part! We can't yet null this since + // it might cause SOG.ProcessBackup() to fail due to the race condition. This really needs to be fixed. + Assert.That(grp2.IsDeleted, "SOG 2 was not registered as deleted after link."); + Assert.That(grp2.Children.Count, Is.EqualTo(0), "Group 2 still contained children after delink."); + Assert.That(grp1.Children.Count == 2); + + if (debugtest) + { + System.Console.WriteLine("parts: {0}", grp1.Children.Count); + System.Console.WriteLine("Group1: Pos:{0}, Rot:{1}", grp1.AbsolutePosition, grp1.Rotation); + System.Console.WriteLine("Group1: Prim1: OffsetPosition:{0}, OffsetRotation:{1}", part1.OffsetPosition, part1.RotationOffset); + System.Console.WriteLine("Group1: Prim2: OffsetPosition:{0}, OffsetRotation:{1}", part2.OffsetPosition, part2.RotationOffset); + } + + // root part should have no offset position or rotation + Assert.That(part1.OffsetPosition == Vector3.Zero && part1.RotationOffset == Quaternion.Identity); + + // offset position should be root part position - part2.absolute position. + Assert.That(part2.OffsetPosition == new Vector3(-10, -10, -10)); + + float roll = 0; + float pitch = 0; + float yaw = 0; + + // There's a euler anomoly at 180, 0, 0 so expect 180 to turn into -180. + part1.RotationOffset.GetEulerAngles(out roll, out pitch, out yaw); + Vector3 rotEuler1 = new Vector3(roll * Utils.RAD_TO_DEG, pitch * Utils.RAD_TO_DEG, yaw * Utils.RAD_TO_DEG); + + if (debugtest) + System.Console.WriteLine(rotEuler1); + + part2.RotationOffset.GetEulerAngles(out roll, out pitch, out yaw); + Vector3 rotEuler2 = new Vector3(roll * Utils.RAD_TO_DEG, pitch * Utils.RAD_TO_DEG, yaw * Utils.RAD_TO_DEG); + + if (debugtest) + System.Console.WriteLine(rotEuler2); + + Assert.That(rotEuler2.ApproxEquals(new Vector3(-180, 0, 0), 0.001f) || rotEuler2.ApproxEquals(new Vector3(180, 0, 0), 0.001f)); + + // Delink part 2 + grp1.DelinkFromGroup(part2.LocalId); + + if (debugtest) + System.Console.WriteLine("Group2: Prim2: OffsetPosition:{0}, OffsetRotation:{1}", part2.AbsolutePosition, part2.RotationOffset); + + Assert.That(grp1.Children.Count, Is.EqualTo(1), "Group 1 still contained part2 after delink."); + Assert.That(part2.AbsolutePosition == Vector3.Zero); + } + + [Test] + public void TestLinkDelink2groups4SceneObjects() + { + bool debugtest = false; + + Scene scene = SceneSetupHelpers.SetupScene(); + SceneObjectPart part1 = SceneSetupHelpers.AddSceneObject(scene); + SceneObjectGroup grp1 = part1.ParentGroup; + SceneObjectPart part2 = SceneSetupHelpers.AddSceneObject(scene); + SceneObjectGroup grp2 = part2.ParentGroup; + SceneObjectPart part3 = SceneSetupHelpers.AddSceneObject(scene); + SceneObjectGroup grp3 = part3.ParentGroup; + SceneObjectPart part4 = SceneSetupHelpers.AddSceneObject(scene); + SceneObjectGroup grp4 = part4.ParentGroup; + + grp1.AbsolutePosition = new Vector3(10, 10, 10); + grp2.AbsolutePosition = Vector3.Zero; + grp3.AbsolutePosition = new Vector3(20, 20, 20); + grp4.AbsolutePosition = new Vector3(40, 40, 40); + + // <90,0,0> + grp1.Rotation = (Quaternion.CreateFromEulers(90 * Utils.DEG_TO_RAD, 0, 0)); + + // <180,0,0> + grp2.UpdateGroupRotation(Quaternion.CreateFromEulers(180 * Utils.DEG_TO_RAD, 0, 0)); + + // <270,0,0> + grp3.Rotation = (Quaternion.CreateFromEulers(270 * Utils.DEG_TO_RAD, 0, 0)); + + // <0,90,0> + grp4.UpdateGroupRotation(Quaternion.CreateFromEulers(0, 90 * Utils.DEG_TO_RAD, 0)); + + // Required for linking + grp1.RootPart.UpdateFlag = 0; + grp2.RootPart.UpdateFlag = 0; + grp3.RootPart.UpdateFlag = 0; + grp4.RootPart.UpdateFlag = 0; + + // Link grp2 to grp1. part2 becomes child prim to grp1. grp2 is eliminated. + grp1.LinkToGroup(grp2); + + // Link grp4 to grp3. + grp3.LinkToGroup(grp4); + + // At this point we should have 4 parts total in two groups. + Assert.That(grp1.Children.Count == 2); + Assert.That(grp2.IsDeleted, "Group 2 was not registered as deleted after link."); + Assert.That(grp2.Children.Count, Is.EqualTo(0), "Group 2 still contained parts after delink."); + Assert.That(grp3.Children.Count == 2); + Assert.That(grp4.IsDeleted, "Group 4 was not registered as deleted after link."); + Assert.That(grp4.Children.Count, Is.EqualTo(0), "Group 4 still contained parts after delink."); + + if (debugtest) + { + System.Console.WriteLine("--------After Link-------"); + System.Console.WriteLine("Group1: parts: {0}", grp1.Children.Count); + System.Console.WriteLine("Group1: Pos:{0}, Rot:{1}", grp1.AbsolutePosition, grp1.Rotation); + System.Console.WriteLine("Group1: Prim1: OffsetPosition:{0}, OffsetRotation:{1}", part1.OffsetPosition, part1.RotationOffset); + System.Console.WriteLine("Group1: Prim2: OffsetPosition:{0}, OffsetRotation:{1}", part2.OffsetPosition, part2.RotationOffset); + + System.Console.WriteLine("Group3: parts: {0}", grp3.Children.Count); + System.Console.WriteLine("Group3: Pos:{0}, Rot:{1}", grp3.AbsolutePosition, grp3.Rotation); + System.Console.WriteLine("Group3: Prim1: OffsetPosition:{0}, OffsetRotation:{1}", part3.OffsetPosition, part3.RotationOffset); + System.Console.WriteLine("Group3: Prim2: OffsetPosition:{0}, OffsetRotation:{1}", part4.OffsetPosition, part4.RotationOffset); + } + + // Required for linking + grp1.RootPart.UpdateFlag = 0; + grp3.RootPart.UpdateFlag = 0; + + // root part should have no offset position or rotation + Assert.That(part1.OffsetPosition == Vector3.Zero && part1.RotationOffset == Quaternion.Identity); + + // offset position should be root part position - part2.absolute position. + Assert.That(part2.OffsetPosition == new Vector3(-10, -10, -10)); + + float roll = 0; + float pitch = 0; + float yaw = 0; + + // There's a euler anomoly at 180, 0, 0 so expect 180 to turn into -180. + part1.RotationOffset.GetEulerAngles(out roll, out pitch, out yaw); + Vector3 rotEuler1 = new Vector3(roll * Utils.RAD_TO_DEG, pitch * Utils.RAD_TO_DEG, yaw * Utils.RAD_TO_DEG); + + if (debugtest) + System.Console.WriteLine(rotEuler1); + + part2.RotationOffset.GetEulerAngles(out roll, out pitch, out yaw); + Vector3 rotEuler2 = new Vector3(roll * Utils.RAD_TO_DEG, pitch * Utils.RAD_TO_DEG, yaw * Utils.RAD_TO_DEG); + + if (debugtest) + System.Console.WriteLine(rotEuler2); + + Assert.That(rotEuler2.ApproxEquals(new Vector3(-180, 0, 0), 0.001f) || rotEuler2.ApproxEquals(new Vector3(180, 0, 0), 0.001f)); + + // Now we're linking the first group to the third group. This will make the first group child parts of the third one. + grp3.LinkToGroup(grp1); + + // Delink parts 2 and 3 + grp3.DelinkFromGroup(part2.LocalId); + grp3.DelinkFromGroup(part3.LocalId); + + if (debugtest) + { + System.Console.WriteLine("--------After De-Link-------"); + System.Console.WriteLine("Group1: parts: {0}", grp1.Children.Count); + System.Console.WriteLine("Group1: Pos:{0}, Rot:{1}", grp1.AbsolutePosition, grp1.Rotation); + System.Console.WriteLine("Group1: Prim1: OffsetPosition:{0}, OffsetRotation:{1}", part1.OffsetPosition, part1.RotationOffset); + System.Console.WriteLine("NoGroup: Prim2: AbsolutePosition:{0}, OffsetRotation:{1}", part2.AbsolutePosition, part2.RotationOffset); + + System.Console.WriteLine("Group3: parts: {0}", grp3.Children.Count); + System.Console.WriteLine("Group3: Pos:{0}, Rot:{1}", grp3.AbsolutePosition, grp3.Rotation); + System.Console.WriteLine("Group3: Prim1: OffsetPosition:{0}, OffsetRotation:{1}", part3.OffsetPosition, part3.RotationOffset); + System.Console.WriteLine("Group3: Prim2: OffsetPosition:{0}, OffsetRotation:{1}", part4.OffsetPosition, part4.RotationOffset); + } + + Assert.That(part2.AbsolutePosition == Vector3.Zero); + Assert.That(part4.OffsetPosition == new Vector3(20, 20, 20)); + Quaternion compareQuaternion = new Quaternion(0, 0.7071068f, 0, 0.7071068f); + Assert.That((part4.RotationOffset.X - compareQuaternion.X < 0.00003) + && (part4.RotationOffset.Y - compareQuaternion.Y < 0.00003) + && (part4.RotationOffset.Z - compareQuaternion.Z < 0.00003) + && (part4.RotationOffset.W - compareQuaternion.W < 0.00003)); + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs new file mode 100644 index 0000000..dc8df32 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs @@ -0,0 +1,292 @@ +/* + * 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 OpenSim 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 Nini.Config; +using System; +using System.Collections.Generic; +using System.Text; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Environment.Modules.Communications.Local; +using OpenSim.Region.Environment.Modules.World.Serialiser; +using OpenSim.Tests.Common.Mock; +using OpenSim.Tests.Common.Setup; + +namespace OpenSim.Region.Framework.Scenes.Tests +{ + /// + /// Scene presence tests + /// + [TestFixture] + public class ScenePresenceTests + { + public Scene scene, scene2, scene3; + public UUID agent1, agent2, agent3; + public static Random random; + public ulong region1,region2,region3; + public CommunicationsManager cm; + public AgentCircuitData acd1; + public SceneObjectGroup sog1, sog2, sog3; + public TestClient testclient; + + [TestFixtureSetUp] + public void Init() + { + cm = new TestCommunicationsManager(); + scene = SceneSetupHelpers.SetupScene("Neighbour x", UUID.Random(), 1000, 1000, cm); + scene2 = SceneSetupHelpers.SetupScene("Neighbour x+1", UUID.Random(), 1001, 1000, cm); + scene3 = SceneSetupHelpers.SetupScene("Neighbour x-1", UUID.Random(), 999, 1000, cm); + + agent1 = UUID.Random(); + agent2 = UUID.Random(); + agent3 = UUID.Random(); + random = new Random(); + sog1 = NewSOG(UUID.Random(), scene, agent1); + sog2 = NewSOG(UUID.Random(), scene, agent1); + sog3 = NewSOG(UUID.Random(), scene, agent1); + + //ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize)); + region1 = scene.RegionInfo.RegionHandle; + region2 = scene2.RegionInfo.RegionHandle; + region3 = scene3.RegionInfo.RegionHandle; + } + + /// + /// Test adding a root agent to a scene. Doesn't yet actually complete crossing the agent into the scene. + /// + [Test] + public void T010_TestAddRootAgent() + { + string firstName = "testfirstname"; + + AgentCircuitData agent = new AgentCircuitData(); + agent.AgentID = agent1; + agent.firstname = firstName; + agent.lastname = "testlastname"; + agent.SessionID = UUID.Zero; + agent.SecureSessionID = UUID.Zero; + agent.circuitcode = 123; + agent.BaseFolder = UUID.Zero; + agent.InventoryFolder = UUID.Zero; + agent.startpos = Vector3.Zero; + agent.CapsPath = GetRandomCapsObjectPath(); + + scene.NewUserConnection(agent); + testclient = new TestClient(agent, scene); + scene.AddNewClient(testclient); + + ScenePresence presence = scene.GetScenePresence(agent1); + + Assert.That(presence, Is.Not.Null, "presence is null"); + Assert.That(presence.Firstname, Is.EqualTo(firstName), "First name not same"); + acd1 = agent; + } + + /// + /// Test removing an uncrossed root agent from a scene. + /// + [Test] + public void T011_TestRemoveRootAgent() + { + scene.RemoveClient(agent1); + + ScenePresence presence = scene.GetScenePresence(agent1); + + Assert.That(presence, Is.Null, "presence is not null"); + } + + [Test] + public void T012_TestAddNeighbourRegion() + { + SceneSetupHelpers.AddRootAgent(scene,agent1); + + ScenePresence presence = scene.GetScenePresence(agent1); + + string cap = presence.ControllingClient.RequestClientInfo().CapsPath; + + presence.AddNeighbourRegion(region2, cap); + presence.AddNeighbourRegion(region3, cap); + + List neighbours = presence.GetKnownRegionList(); + + Assert.That(neighbours.Count, Is.EqualTo(2)); + } + + [Test] + public void T013_TestRemoveNeighbourRegion() + { + ScenePresence presence = scene.GetScenePresence(agent1); + presence.RemoveNeighbourRegion(region3); + + List neighbours = presence.GetKnownRegionList(); + Assert.That(neighbours.Count,Is.EqualTo(1)); + /* + presence.MakeChildAgent; + presence.MakeRootAgent; + CompleteAvatarMovement + */ + } + + [Test] + public void T020_TestMakeRootAgent() + { + ScenePresence presence = scene.GetScenePresence(agent1); + Assert.That(presence.IsChildAgent, Is.False, "Starts out as a root agent"); + + presence.MakeChildAgent(); + Assert.That(presence.IsChildAgent, Is.True, "Did not change to child agent after MakeChildAgent"); + + // Accepts 0 but rejects Constants.RegionSize + Vector3 pos = new Vector3(0,Constants.RegionSize-1,0); + presence.MakeRootAgent(pos,true); + Assert.That(presence.IsChildAgent, Is.False, "Did not go back to root agent"); + Assert.That(presence.AbsolutePosition, Is.EqualTo(pos), "Position is not the same one entered"); + } + + [Test] + public void T021_TestCrossToNewRegion() + { + // Adding child agent to region 1001 + scene2.NewUserConnection(acd1); + scene2.AddNewClient(testclient); + + ScenePresence presence = scene.GetScenePresence(agent1); + ScenePresence presence2 = scene2.GetScenePresence(agent1); + + // Adding neighbour region caps info to presence2 + string cap = presence.ControllingClient.RequestClientInfo().CapsPath; + presence2.AddNeighbourRegion(region1, cap); + + Assert.That(presence.IsChildAgent, Is.False, "Did not start root in origin region."); + Assert.That(presence2.IsChildAgent, Is.True, "Is not a child on destination region."); + + // Cross to x+1 + presence.AbsolutePosition = new Vector3(Constants.RegionSize+1,3,100); + scene.RegisterRegionWithGrid(); + scene2.RegisterRegionWithGrid(); + presence.Update(); + + Assert.That(presence.IsChildAgent, Is.True, "Did not complete region cross as expected."); + Assert.That(presence2.IsChildAgent, Is.False, "Did not receive root status after receiving agent."); + + // Cross Back + presence2.AbsolutePosition = new Vector3(-1, 3, 100); + presence2.Update(); + + Assert.That(presence2.IsChildAgent, Is.True, "Did not return from region as expected."); + Assert.That(presence.IsChildAgent, Is.False, "Presence was not made root in old region again."); + } + + [Test] + public void T030_TestAddAttachments() + { + ScenePresence presence = scene.GetScenePresence(agent1); + + presence.AddAttachment(sog1); + presence.AddAttachment(sog2); + presence.AddAttachment(sog3); + + Assert.That(presence.HasAttachments(), Is.True); + Assert.That(presence.ValidateAttachments(), Is.True); + } + + [Test] + public void T031_RemoveAttachments() + { + ScenePresence presence = scene.GetScenePresence(agent1); + presence.RemoveAttachment(sog1); + presence.RemoveAttachment(sog2); + presence.RemoveAttachment(sog3); + Assert.That(presence.HasAttachments(), Is.False); + } + + [Test] + public void T032_CrossAttachments() + { + ScenePresence presence = scene.GetScenePresence(agent1); + ScenePresence presence2 = scene2.GetScenePresence(agent1); + presence2.AddAttachment(sog1); + presence2.AddAttachment(sog2); + + IRegionModule serialiser = new SerialiserModule(); + SceneSetupHelpers.SetupSceneModules(scene, new IniConfigSource(), serialiser); + SceneSetupHelpers.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"); + } + + public static string GetRandomCapsObjectPath() + { + UUID caps = UUID.Random(); + string capsPath = caps.ToString(); + capsPath = capsPath.Remove(capsPath.Length - 4, 4); + return capsPath; + } + + private SceneObjectGroup NewSOG(UUID uuid, Scene scene, UUID agent) + { + SceneObjectPart sop = new SceneObjectPart(); + sop.Name = RandomName(); + sop.Description = RandomName(); + sop.Text = RandomName(); + sop.SitName = RandomName(); + sop.TouchName = RandomName(); + sop.UUID = uuid; + sop.Shape = PrimitiveBaseShape.Default; + sop.Shape.State = 1; + sop.OwnerID = agent; + + SceneObjectGroup sog = new SceneObjectGroup(); + sog.SetScene(scene); + sog.SetRootPart(sop); + + return sog; + } + + private static string RandomName() + { + StringBuilder name = new StringBuilder(); + int size = random.Next(5,12); + char ch ; + for (int i=0; i + /// Teleport tests in a standalone OpenSim + /// + [TestFixture] + public class StandaloneTeleportTests + { + /// + /// Test a teleport between two regions that are not neighbours and do not share any neighbours in common. + /// + /// Does not yet do what is says on the tin. + [Test] + public void TestSimpleNotNeighboursTeleport() + { + //log4net.Config.XmlConfigurator.Configure(); + + UUID sceneAId = UUID.Parse("00000000-0000-0000-0000-000000000100"); + UUID sceneBId = UUID.Parse("00000000-0000-0000-0000-000000000200"); + CommunicationsManager cm = new TestCommunicationsManager(); + + // shared module + IRegionModule interregionComms = new RESTInterregionComms(); + + Scene sceneA = SceneSetupHelpers.SetupScene("sceneA", sceneAId, 1000, 1000, cm); + SceneSetupHelpers.SetupSceneModules(sceneA, new IniConfigSource(), interregionComms); + sceneA.RegisterRegionWithGrid(); + + Scene sceneB = SceneSetupHelpers.SetupScene("sceneB", sceneBId, 1010, 1010, cm); + SceneSetupHelpers.SetupSceneModules(sceneB, new IniConfigSource(), interregionComms); + sceneB.RegisterRegionWithGrid(); + + UUID agentId = UUID.Parse("00000000-0000-0000-0000-000000000041"); + TestClient client = SceneSetupHelpers.AddRootAgent(sceneA, agentId); + + ICapabilitiesModule sceneACapsModule = sceneA.RequestModuleInterface(); + + Assert.That( + sceneACapsModule.GetCapsPath(agentId), + Is.EqualTo(client.CapsSeedUrl), + "Incorrect caps object path set up in sceneA"); + + // FIXME: This is a hack to get the test working - really the normal OpenSim mechanisms should be used. + client.TeleportTargetScene = sceneB; + client.Teleport(sceneB.RegionInfo.RegionHandle, new Vector3(100, 100, 100), new Vector3(40, 40, 40)); + + Assert.That(sceneB.GetScenePresence(agentId), Is.Not.Null, "Client does not have an agent in sceneB"); + Assert.That(sceneA.GetScenePresence(agentId), Is.Null, "Client still had an agent in sceneA"); + + ICapabilitiesModule sceneBCapsModule = sceneB.RequestModuleInterface(); + + // Temporary assertion - caps url construction should at least be doable through a method. + Assert.That( + "http://" + sceneB.RegionInfo.ExternalHostName + ":" + sceneB.RegionInfo.HttpPort + "/CAPS/" + sceneBCapsModule.GetCapsPath(agentId) + "0000/", + Is.EqualTo(client.CapsSeedUrl), + "Incorrect caps object path set up in sceneB"); + + // This assertion will currently fail since we don't remove the caps paths when no longer needed + //Assert.That(sceneACapsModule.GetCapsPath(agentId), Is.Null, "sceneA still had a caps object path"); + + // TODO: Check that more of everything is as it should be + + // TODO: test what happens if we try to teleport to a region that doesn't exist + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/Types/BasicQuadTreeNode.cs b/OpenSim/Region/Framework/Scenes/Types/BasicQuadTreeNode.cs new file mode 100644 index 0000000..9fbbb15 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Types/BasicQuadTreeNode.cs @@ -0,0 +1,269 @@ +/* + * 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 OpenSim 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 OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Scenes.Types +{ + public class BasicQuadTreeNode + { + private List m_objects = new List(); + private BasicQuadTreeNode[] m_childNodes = null; + private BasicQuadTreeNode m_parent = null; + + private short m_leftX; + private short m_leftY; + private short m_width; + private short m_height; + //private int m_quadNumber; + private string m_quadID; + + public BasicQuadTreeNode(BasicQuadTreeNode parent, string quadID, short leftX, short leftY, short width, + short height) + { + m_parent = parent; + m_quadID = quadID; + m_leftX = leftX; + m_leftY = leftY; + m_width = width; + m_height = height; + // Console.WriteLine("creating quadtree node " + m_quadID); + } + + public void AddObject(SceneObjectGroup obj) + { + if (m_childNodes == null) + { + if (!m_objects.Contains(obj)) + { + m_objects.Add(obj); + } + } + else + { + if (obj.AbsolutePosition.X < (m_leftX + (m_width/2))) + { + if (obj.AbsolutePosition.Y < (m_leftY + (m_height/2))) + { + m_childNodes[0].AddObject(obj); + } + else + { + m_childNodes[2].AddObject(obj); + } + } + else + { + if (obj.AbsolutePosition.Y < (m_leftY + (m_height/2))) + { + m_childNodes[1].AddObject(obj); + } + else + { + m_childNodes[3].AddObject(obj); + } + } + } + } + + public void Subdivide() + { + if (m_childNodes == null) + { + m_childNodes = new BasicQuadTreeNode[4]; + m_childNodes[0] = + new BasicQuadTreeNode(this, m_quadID + "1/", m_leftX, m_leftY, (short) (m_width/2), + (short) (m_height/2)); + m_childNodes[1] = + new BasicQuadTreeNode(this, m_quadID + "2/", (short) (m_leftX + (m_width/2)), m_leftY, + (short) (m_width/2), (short) (m_height/2)); + m_childNodes[2] = + new BasicQuadTreeNode(this, m_quadID + "3/", m_leftX, (short) (m_leftY + (m_height/2)), + (short) (m_width/2), (short) (m_height/2)); + m_childNodes[3] = + new BasicQuadTreeNode(this, m_quadID + "4/", (short) (m_leftX + (m_width/2)), + (short) (m_height + (m_height/2)), (short) (m_width/2), (short) (m_height/2)); + } + else + { + for (int i = 0; i < m_childNodes.Length; i++) + { + m_childNodes[i].Subdivide(); + } + } + } + + public List GetObjectsFrom(float x, float y) + { + if (m_childNodes == null) + { + return new List(m_objects); + } + else + { + if (x < m_leftX + (m_width/2)) + { + if (y < m_leftY + (m_height/2)) + { + return m_childNodes[0].GetObjectsFrom(x, y); + } + else + { + return m_childNodes[2].GetObjectsFrom(x, y); + } + } + else + { + if (y < m_leftY + (m_height/2)) + { + return m_childNodes[1].GetObjectsFrom(x, y); + } + else + { + return m_childNodes[3].GetObjectsFrom(x, y); + } + } + } + } + + public List GetObjectsFrom(string nodeName) + { + if (nodeName == m_quadID) + { + return new List(m_objects); + } + else if (m_childNodes != null) + { + for (int i = 0; i < 4; i++) + { + List retVal; + retVal = m_childNodes[i].GetObjectsFrom(nodeName); + if (retVal != null) + { + return retVal; + } + } + } + return null; + } + + public string GetNodeID(float x, float y) + { + if (m_childNodes == null) + { + return m_quadID; + } + else + { + if (x < m_leftX + (m_width/2)) + { + if (y < m_leftY + (m_height/2)) + { + return m_childNodes[0].GetNodeID(x, y); + } + else + { + return m_childNodes[2].GetNodeID(x, y); + } + } + else + { + if (y < m_leftY + (m_height/2)) + { + return m_childNodes[1].GetNodeID(x, y); + } + else + { + return m_childNodes[3].GetNodeID(x, y); + } + } + } + } + + public void Update() + { + if (m_childNodes != null) + { + for (int i = 0; i < 4; i++) + { + m_childNodes[i].Update(); + } + } + else + { + List outBounds = new List(); + foreach (SceneObjectGroup group in m_objects) + { + if (((group.AbsolutePosition.X > m_leftX) && (group.AbsolutePosition.X < (m_leftX + m_width))) && + ((group.AbsolutePosition.Y > m_leftY) && (group.AbsolutePosition.Y < (m_leftY + m_height)))) + { + //still in bounds + } + else + { + outBounds.Add(group); + } + } + + foreach (SceneObjectGroup removee in outBounds) + { + m_objects.Remove(removee); + if (m_parent != null) + { + m_parent.PassUp(removee); + } + } + outBounds.Clear(); + } + } + + public void PassUp(SceneObjectGroup group) + { + if (((group.AbsolutePosition.X > m_leftX) && (group.AbsolutePosition.X < (m_leftX + m_width))) && + ((group.AbsolutePosition.Y > m_leftY) && (group.AbsolutePosition.Y < (m_leftY + m_height)))) + { + AddObject(group); + } + else + { + if (m_parent != null) + { + m_parent.PassUp(group); + } + } + } + + public string[] GetNeighbours(string nodeName) + { + string[] retVal = new string[1]; + retVal[0] = String.Empty; + return retVal; + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/Types/UpdateQueue.cs b/OpenSim/Region/Framework/Scenes/Types/UpdateQueue.cs new file mode 100644 index 0000000..452ada9 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Types/UpdateQueue.cs @@ -0,0 +1,134 @@ +/* + * 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 OpenSim 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.Runtime.Serialization; +using System.Security.Permissions; +using OpenMetaverse; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Scenes.Types +{ + [Serializable] + public class UpdateQueue : ISerializable + { + private Queue m_queue; + + private List m_ids; + + private object m_syncObject = new object(); + + public int Count + { + get { return m_queue.Count; } + } + + public UpdateQueue() + { + m_queue = new Queue(); + m_ids = new List(); + } + + public void Clear() + { + lock (m_syncObject) + { + m_ids.Clear(); + m_queue.Clear(); + } + } + + public void Enqueue(SceneObjectPart part) + { + lock (m_syncObject) + { + if (!m_ids.Contains(part.UUID)) + { + m_ids.Add(part.UUID); + m_queue.Enqueue(part); + } + } + } + + public SceneObjectPart Dequeue() + { + SceneObjectPart part = null; + lock (m_syncObject) + { + if (m_queue.Count > 0) + { + part = m_queue.Dequeue(); + m_ids.Remove(part.UUID); + } + } + + return part; + } + + protected UpdateQueue(SerializationInfo info, StreamingContext context) + { + //System.Console.WriteLine("UpdateQueue Deserialize BGN"); + + if (info == null) + { + throw new ArgumentNullException("info"); + } + + m_queue = (Queue)info.GetValue("m_queue", typeof(Queue)); + List ids_work = (List)info.GetValue("m_ids", typeof(List)); + + foreach (Guid guid in ids_work) + { + m_ids.Add(new UUID(guid)); + } + + //System.Console.WriteLine("UpdateQueue Deserialize END"); + } + + [SecurityPermission(SecurityAction.LinkDemand, + Flags = SecurityPermissionFlag.SerializationFormatter)] + public virtual void GetObjectData( + SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException("info"); + } + + List ids_work = new List(); + + foreach (UUID uuid in m_ids) + { + ids_work.Add(uuid.Guid); + } + + info.AddValue("m_queue", m_queue); + info.AddValue("m_ids", ids_work); + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/UndoState.cs b/OpenSim/Region/Framework/Scenes/UndoState.cs new file mode 100644 index 0000000..3d48229 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/UndoState.cs @@ -0,0 +1,116 @@ +/* + * 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 OpenSim 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 OpenMetaverse; + +namespace OpenSim.Region.Framework.Scenes +{ + public class UndoState + { + public Vector3 Position = Vector3.Zero; + public Vector3 Scale = Vector3.Zero; + public Quaternion Rotation = Quaternion.Identity; + + public UndoState(Vector3 pos, Quaternion rot, Vector3 scale) + { + Position = pos; + Rotation = rot; + Scale = scale; + } + + public UndoState(SceneObjectPart part) + { + if (part != null) + { + if (part.ParentID == 0) + { + Position = part.AbsolutePosition; + Rotation = part.RotationOffset; + + } + else + { + Position = part.GroupPosition; + Rotation = part.RotationOffset; + Scale = part.Shape.Scale; + + } + } + } + + public bool Compare(SceneObjectPart part) + { + if (part != null) + { + if (part.ParentID == 0) + { + if (Position == part.AbsolutePosition && Rotation == part.RotationOffset) + return true; + else + return false; + } + else + { + if (Position == part.GroupPosition && Rotation == part.RotationOffset && Scale == part.Shape.Scale) + return true; + else + return false; + + } + } + return false; + } + + public void PlaybackState(SceneObjectPart part) + { + if (part != null) + { + part.Undoing = true; + + if (part.ParentID == 0) + { + part.ParentGroup.AbsolutePosition = Position; + part.UpdateRotation(Rotation); + part.ParentGroup.ScheduleGroupForTerseUpdate(); + } + else + { + part.OffsetPosition = Position; + part.UpdateRotation(Rotation); + part.Resize(Scale); + part.ScheduleTerseUpdate(); + } + part.Undoing = false; + + } + } + + public UndoState() + { + } + } +} diff --git a/OpenSim/Region/Framework/StorageManager.cs b/OpenSim/Region/Framework/StorageManager.cs new file mode 100644 index 0000000..db9359f --- /dev/null +++ b/OpenSim/Region/Framework/StorageManager.cs @@ -0,0 +1,96 @@ +/* + * 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 OpenSim 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 log4net; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.Framework +{ + public class StorageManager + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IRegionDataStore m_dataStore; + + public IRegionDataStore DataStore + { + get { return m_dataStore; } + } + + private IEstateDataStore m_estateDataStore; + + public IEstateDataStore EstateDataStore + { + get { return m_estateDataStore; } + } + + public StorageManager(IRegionDataStore storage) + { + m_dataStore = storage; + } + + public StorageManager(string dllName, string connectionstring, string estateconnectionstring) + { + m_log.Info("[DATASTORE]: Attempting to load " + dllName); + Assembly pluginAssembly = Assembly.LoadFrom(dllName); + + foreach (Type pluginType in pluginAssembly.GetTypes()) + { + if (pluginType.IsPublic) + { + Type typeInterface = pluginType.GetInterface("IRegionDataStore", true); + + if (typeInterface != null) + { + IRegionDataStore plug = + (IRegionDataStore) Activator.CreateInstance(pluginAssembly.GetType(pluginType.ToString())); + plug.Initialise(connectionstring); + + m_dataStore = plug; + + m_log.Info("[DATASTORE]: Added IRegionDataStore Interface"); + } + + typeInterface = pluginType.GetInterface("IEstateDataStore", true); + + if (typeInterface != null) + { + IEstateDataStore estPlug = + (IEstateDataStore) Activator.CreateInstance(pluginAssembly.GetType(pluginType.ToString())); + estPlug.Initialise(estateconnectionstring); + + m_estateDataStore = estPlug; + } + } + } + + //TODO: Add checking and warning to make sure it initialised. + } + } +} -- cgit v1.1