diff options
Diffstat (limited to 'OpenSim/Region')
16 files changed, 435 insertions, 251 deletions
diff --git a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs index da1ff2e..1e14f45 100644 --- a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs +++ b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs | |||
@@ -33,6 +33,7 @@ using OpenSim.Framework; | |||
33 | 33 | ||
34 | using OpenSim.Region.Framework.Scenes; | 34 | using OpenSim.Region.Framework.Scenes; |
35 | using OpenSim.Services.Interfaces; | 35 | using OpenSim.Services.Interfaces; |
36 | using OpenSim.Region.Framework.Interfaces; | ||
36 | 37 | ||
37 | namespace OpenSim.Region.CoreModules.Agent.AssetTransaction | 38 | namespace OpenSim.Region.CoreModules.Agent.AssetTransaction |
38 | { | 39 | { |
@@ -119,6 +120,14 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction | |||
119 | } | 120 | } |
120 | else | 121 | else |
121 | { | 122 | { |
123 | // Check if the xfer is a terrain xfer | ||
124 | IEstateModule estateModule = m_Scene.RequestModuleInterface<IEstateModule>(); | ||
125 | if (estateModule != null) | ||
126 | { | ||
127 | if (estateModule.IsTerrainXfer(xferID)) | ||
128 | return; | ||
129 | } | ||
130 | |||
122 | m_log.ErrorFormat( | 131 | m_log.ErrorFormat( |
123 | "[AGENT ASSET TRANSACTIONS]: Could not find uploader for xfer id {0}, packet id {1}, data length {2}", | 132 | "[AGENT ASSET TRANSACTIONS]: Could not find uploader for xfer id {0}, packet id {1}, data length {2}", |
124 | xferID, packetID, data.Length); | 133 | xferID, packetID, data.Length); |
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs index 2116605..ff87ece 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs | |||
@@ -42,14 +42,22 @@ using log4net; | |||
42 | 42 | ||
43 | namespace OpenSim.Region.CoreModules.Avatar.Friends | 43 | namespace OpenSim.Region.CoreModules.Avatar.Friends |
44 | { | 44 | { |
45 | public class FriendsRequestHandler : BaseStreamHandler | 45 | public class FriendsRequestHandler : BaseStreamHandlerBasicDOSProtector |
46 | { | 46 | { |
47 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 47 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
48 | 48 | ||
49 | private FriendsModule m_FriendsModule; | 49 | private FriendsModule m_FriendsModule; |
50 | 50 | ||
51 | public FriendsRequestHandler(FriendsModule fmodule) | 51 | public FriendsRequestHandler(FriendsModule fmodule) |
52 | : base("POST", "/friends") | 52 | : base("POST", "/friends", new BasicDosProtectorOptions() |
53 | { | ||
54 | AllowXForwardedFor = true, | ||
55 | ForgetTimeSpan = TimeSpan.FromMinutes(2), | ||
56 | MaxRequestsInTimeframe = 20, | ||
57 | ReportingName = "FRIENDSDOSPROTECTOR", | ||
58 | RequestTimeSpan = TimeSpan.FromSeconds(5), | ||
59 | ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod | ||
60 | }) | ||
53 | { | 61 | { |
54 | m_FriendsModule = fmodule; | 62 | m_FriendsModule = fmodule; |
55 | } | 63 | } |
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index ac4203f..6251266 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs | |||
@@ -789,83 +789,29 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
789 | 789 | ||
790 | SceneObjectGroup group = null; | 790 | SceneObjectGroup group = null; |
791 | 791 | ||
792 | string xmlData = Utils.BytesToString(rezAsset.Data); | 792 | List<SceneObjectGroup> objlist; |
793 | List<SceneObjectGroup> objlist = new List<SceneObjectGroup>(); | 793 | List<Vector3> veclist; |
794 | List<Vector3> veclist = new List<Vector3>(); | 794 | Vector3 bbox; |
795 | float offsetHeight; | ||
795 | byte bRayEndIsIntersection = (byte)(RayEndIsIntersection ? 1 : 0); | 796 | byte bRayEndIsIntersection = (byte)(RayEndIsIntersection ? 1 : 0); |
796 | Vector3 pos; | 797 | Vector3 pos; |
797 | 798 | ||
798 | XmlDocument doc = new XmlDocument(); | 799 | bool single = m_Scene.GetObjectsToRez(rezAsset.Data, attachment, out objlist, out veclist, out bbox, out offsetHeight); |
799 | doc.LoadXml(xmlData); | ||
800 | XmlElement e = (XmlElement)doc.SelectSingleNode("/CoalescedObject"); | ||
801 | Vector3 rez_pos; | ||
802 | if (e == null || attachment) // Single | ||
803 | { | ||
804 | SceneObjectGroup g = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); | ||
805 | if (!attachment) | ||
806 | { | ||
807 | g.RootPart.AttachPoint = g.RootPart.Shape.State; | ||
808 | g.RootPart.AttachOffset = g.AbsolutePosition; | ||
809 | g.RootPart.AttachRotation = g.GroupRotation; | ||
810 | if (g.RootPart.Shape.PCode != (byte)PCode.NewTree && | ||
811 | g.RootPart.Shape.PCode != (byte)PCode.Tree) | ||
812 | g.RootPart.Shape.State = 0; | ||
813 | } | ||
814 | |||
815 | objlist.Add(g); | ||
816 | veclist.Add(Vector3.Zero); | ||
817 | 800 | ||
818 | float offsetHeight = 0; | 801 | if (single) |
802 | { | ||
819 | pos = m_Scene.GetNewRezLocation( | 803 | pos = m_Scene.GetNewRezLocation( |
820 | RayStart, RayEnd, RayTargetID, Quaternion.Identity, | 804 | RayStart, RayEnd, RayTargetID, Quaternion.Identity, |
821 | BypassRayCast, bRayEndIsIntersection, true, g.GetAxisAlignedBoundingBox(out offsetHeight), false); | 805 | BypassRayCast, bRayEndIsIntersection, true, bbox, false); |
822 | pos.Z += offsetHeight; | 806 | pos.Z += offsetHeight; |
823 | rez_pos = pos; | ||
824 | } | 807 | } |
825 | else | 808 | else |
826 | { | 809 | { |
827 | XmlElement coll = (XmlElement)e; | ||
828 | float bx = Convert.ToSingle(coll.GetAttribute("x")); | ||
829 | float by = Convert.ToSingle(coll.GetAttribute("y")); | ||
830 | float bz = Convert.ToSingle(coll.GetAttribute("z")); | ||
831 | Vector3 bbox = new Vector3(bx, by, bz); | ||
832 | |||
833 | pos = m_Scene.GetNewRezLocation(RayStart, RayEnd, | 810 | pos = m_Scene.GetNewRezLocation(RayStart, RayEnd, |
834 | RayTargetID, Quaternion.Identity, | 811 | RayTargetID, Quaternion.Identity, |
835 | BypassRayCast, bRayEndIsIntersection, true, | 812 | BypassRayCast, bRayEndIsIntersection, true, |
836 | bbox, false); | 813 | bbox, false); |
837 | |||
838 | rez_pos = pos; | ||
839 | |||
840 | pos -= bbox / 2; | 814 | pos -= bbox / 2; |
841 | |||
842 | XmlNodeList groups = e.SelectNodes("SceneObjectGroup"); | ||
843 | foreach (XmlNode n in groups) | ||
844 | { | ||
845 | SceneObjectGroup g = SceneObjectSerializer.FromOriginalXmlFormat(n.OuterXml); | ||
846 | g.RootPart.AttachPoint = g.RootPart.Shape.State; | ||
847 | g.RootPart.AttachOffset = g.AbsolutePosition; | ||
848 | g.RootPart.AttachRotation = g.GroupRotation; | ||
849 | if (g.RootPart.Shape.PCode != (byte)PCode.NewTree && | ||
850 | g.RootPart.Shape.PCode != (byte)PCode.Tree) | ||
851 | g.RootPart.Shape.State = 0; | ||
852 | |||
853 | objlist.Add(g); | ||
854 | XmlElement el = (XmlElement)n; | ||
855 | |||
856 | string rawX = el.GetAttribute("offsetx"); | ||
857 | string rawY = el.GetAttribute("offsety"); | ||
858 | string rawZ = el.GetAttribute("offsetz"); | ||
859 | // | ||
860 | // m_log.DebugFormat( | ||
861 | // "[INVENTORY ACCESS MODULE]: Converting coalesced object {0} offset <{1}, {2}, {3}>", | ||
862 | // g.Name, rawX, rawY, rawZ); | ||
863 | |||
864 | float x = Convert.ToSingle(rawX); | ||
865 | float y = Convert.ToSingle(rawY); | ||
866 | float z = Convert.ToSingle(rawZ); | ||
867 | veclist.Add(new Vector3(x, y, z)); | ||
868 | } | ||
869 | } | 815 | } |
870 | 816 | ||
871 | int primcount = 0; | 817 | int primcount = 0; |
@@ -873,7 +819,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
873 | primcount += g.PrimCount; | 819 | primcount += g.PrimCount; |
874 | 820 | ||
875 | if (!m_Scene.Permissions.CanRezObject( | 821 | if (!m_Scene.Permissions.CanRezObject( |
876 | primcount, remoteClient.AgentId, rez_pos) | 822 | primcount, remoteClient.AgentId, pos) |
877 | && !attachment) | 823 | && !attachment) |
878 | { | 824 | { |
879 | // The client operates in no fail mode. It will | 825 | // The client operates in no fail mode. It will |
@@ -890,7 +836,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess | |||
890 | return null; | 836 | return null; |
891 | } | 837 | } |
892 | 838 | ||
893 | if (item != null && !DoPreRezWhenFromItem(remoteClient, item, objlist, rez_pos, veclist, attachment)) | 839 | if (item != null && !DoPreRezWhenFromItem(remoteClient, item, objlist, pos, veclist, attachment)) |
894 | return null; | 840 | return null; |
895 | 841 | ||
896 | for (int i = 0; i < objlist.Count; i++) | 842 | for (int i = 0; i < objlist.Count; i++) |
diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index 487aa09..4750b46 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs | |||
@@ -839,26 +839,23 @@ namespace OpenSim.Region.CoreModules.World.Estate | |||
839 | 839 | ||
840 | private void AbortTerrainXferHandler(IClientAPI remoteClient, ulong XferID) | 840 | private void AbortTerrainXferHandler(IClientAPI remoteClient, ulong XferID) |
841 | { | 841 | { |
842 | if (TerrainUploader != null) | 842 | lock (this) |
843 | { | 843 | { |
844 | lock (TerrainUploader) | 844 | if ((TerrainUploader != null) && (XferID == TerrainUploader.XferID)) |
845 | { | 845 | { |
846 | if (XferID == TerrainUploader.XferID) | 846 | remoteClient.OnXferReceive -= TerrainUploader.XferReceive; |
847 | { | 847 | remoteClient.OnAbortXfer -= AbortTerrainXferHandler; |
848 | remoteClient.OnXferReceive -= TerrainUploader.XferReceive; | 848 | TerrainUploader.TerrainUploadDone -= HandleTerrainApplication; |
849 | remoteClient.OnAbortXfer -= AbortTerrainXferHandler; | ||
850 | TerrainUploader.TerrainUploadDone -= HandleTerrainApplication; | ||
851 | 849 | ||
852 | TerrainUploader = null; | 850 | TerrainUploader = null; |
853 | remoteClient.SendAlertMessage("Terrain Upload aborted by the client"); | 851 | remoteClient.SendAlertMessage("Terrain Upload aborted by the client"); |
854 | } | ||
855 | } | 852 | } |
856 | } | 853 | } |
857 | |||
858 | } | 854 | } |
855 | |||
859 | private void HandleTerrainApplication(string filename, byte[] terrainData, IClientAPI remoteClient) | 856 | private void HandleTerrainApplication(string filename, byte[] terrainData, IClientAPI remoteClient) |
860 | { | 857 | { |
861 | lock (TerrainUploader) | 858 | lock (this) |
862 | { | 859 | { |
863 | remoteClient.OnXferReceive -= TerrainUploader.XferReceive; | 860 | remoteClient.OnXferReceive -= TerrainUploader.XferReceive; |
864 | remoteClient.OnAbortXfer -= AbortTerrainXferHandler; | 861 | remoteClient.OnAbortXfer -= AbortTerrainXferHandler; |
@@ -917,22 +914,32 @@ namespace OpenSim.Region.CoreModules.World.Estate | |||
917 | 914 | ||
918 | private void handleUploadTerrain(IClientAPI remote_client, string clientFileName) | 915 | private void handleUploadTerrain(IClientAPI remote_client, string clientFileName) |
919 | { | 916 | { |
920 | if (TerrainUploader == null) | 917 | lock (this) |
921 | { | 918 | { |
922 | 919 | if (TerrainUploader == null) | |
923 | TerrainUploader = new EstateTerrainXferHandler(remote_client, clientFileName); | ||
924 | lock (TerrainUploader) | ||
925 | { | 920 | { |
921 | m_log.DebugFormat("Starting to receive uploaded terrain"); | ||
922 | TerrainUploader = new EstateTerrainXferHandler(remote_client, clientFileName); | ||
926 | remote_client.OnXferReceive += TerrainUploader.XferReceive; | 923 | remote_client.OnXferReceive += TerrainUploader.XferReceive; |
927 | remote_client.OnAbortXfer += AbortTerrainXferHandler; | 924 | remote_client.OnAbortXfer += AbortTerrainXferHandler; |
928 | TerrainUploader.TerrainUploadDone += HandleTerrainApplication; | 925 | TerrainUploader.TerrainUploadDone += HandleTerrainApplication; |
926 | TerrainUploader.RequestStartXfer(remote_client); | ||
927 | } | ||
928 | else | ||
929 | { | ||
930 | remote_client.SendAlertMessage("Another Terrain Upload is in progress. Please wait your turn!"); | ||
929 | } | 931 | } |
930 | TerrainUploader.RequestStartXfer(remote_client); | ||
931 | |||
932 | } | 932 | } |
933 | else | 933 | } |
934 | |||
935 | public bool IsTerrainXfer(ulong xferID) | ||
936 | { | ||
937 | lock (this) | ||
934 | { | 938 | { |
935 | remote_client.SendAlertMessage("Another Terrain Upload is in progress. Please wait your turn!"); | 939 | if (TerrainUploader == null) |
940 | return false; | ||
941 | else | ||
942 | return TerrainUploader.XferID == xferID; | ||
936 | } | 943 | } |
937 | } | 944 | } |
938 | 945 | ||
diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateTerrainXferHandler.cs b/OpenSim/Region/CoreModules/World/Estate/EstateTerrainXferHandler.cs index b8d8b10..2d74eaf 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateTerrainXferHandler.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateTerrainXferHandler.cs | |||
@@ -78,7 +78,10 @@ namespace OpenSim.Region.CoreModules.World.Estate | |||
78 | /// <param name="data"></param> | 78 | /// <param name="data"></param> |
79 | public void XferReceive(IClientAPI remoteClient, ulong xferID, uint packetID, byte[] data) | 79 | public void XferReceive(IClientAPI remoteClient, ulong xferID, uint packetID, byte[] data) |
80 | { | 80 | { |
81 | if (mXferID == xferID) | 81 | if (mXferID != xferID) |
82 | return; | ||
83 | |||
84 | lock (this) | ||
82 | { | 85 | { |
83 | if (m_asset.Data.Length > 1) | 86 | if (m_asset.Data.Length > 1) |
84 | { | 87 | { |
@@ -99,7 +102,6 @@ namespace OpenSim.Region.CoreModules.World.Estate | |||
99 | if ((packetID & 0x80000000) != 0) | 102 | if ((packetID & 0x80000000) != 0) |
100 | { | 103 | { |
101 | SendCompleteMessage(remoteClient); | 104 | SendCompleteMessage(remoteClient); |
102 | |||
103 | } | 105 | } |
104 | } | 106 | } |
105 | } | 107 | } |
diff --git a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs index bf18616..98fa763 100644 --- a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs +++ b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs | |||
@@ -162,7 +162,16 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
162 | regionimage = regionimage.Replace("-", ""); | 162 | regionimage = regionimage.Replace("-", ""); |
163 | m_log.Info("[WORLD MAP]: JPEG Map location: " + m_scene.RegionInfo.ServerURI + "index.php?method=" + regionimage); | 163 | m_log.Info("[WORLD MAP]: JPEG Map location: " + m_scene.RegionInfo.ServerURI + "index.php?method=" + regionimage); |
164 | 164 | ||
165 | MainServer.Instance.AddHTTPHandler(regionimage, OnHTTPGetMapImage); | 165 | MainServer.Instance.AddHTTPHandler(regionimage, |
166 | new GenericHTTPDOSProtector(OnHTTPGetMapImage, OnHTTPThrottled, new BasicDosProtectorOptions() | ||
167 | { | ||
168 | AllowXForwardedFor = false, | ||
169 | ForgetTimeSpan = TimeSpan.FromMinutes(2), | ||
170 | MaxRequestsInTimeframe = 4, | ||
171 | ReportingName = "MAPDOSPROTECTOR", | ||
172 | RequestTimeSpan = TimeSpan.FromSeconds(10), | ||
173 | ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod | ||
174 | }).Process); | ||
166 | MainServer.Instance.AddLLSDHandler( | 175 | MainServer.Instance.AddLLSDHandler( |
167 | "/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(), HandleRemoteMapItemRequest); | 176 | "/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(), HandleRemoteMapItemRequest); |
168 | 177 | ||
@@ -1131,6 +1140,16 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1131 | block.Y = (ushort)(r.RegionLocY / Constants.RegionSize); | 1140 | block.Y = (ushort)(r.RegionLocY / Constants.RegionSize); |
1132 | } | 1141 | } |
1133 | 1142 | ||
1143 | public Hashtable OnHTTPThrottled(Hashtable keysvals) | ||
1144 | { | ||
1145 | Hashtable reply = new Hashtable(); | ||
1146 | int statuscode = 500; | ||
1147 | reply["str_response_string"] = ""; | ||
1148 | reply["int_response_code"] = statuscode; | ||
1149 | reply["content_type"] = "text/plain"; | ||
1150 | return reply; | ||
1151 | } | ||
1152 | |||
1134 | public Hashtable OnHTTPGetMapImage(Hashtable keysvals) | 1153 | public Hashtable OnHTTPGetMapImage(Hashtable keysvals) |
1135 | { | 1154 | { |
1136 | m_log.Debug("[WORLD MAP]: Sending map image jpeg"); | 1155 | m_log.Debug("[WORLD MAP]: Sending map image jpeg"); |
diff --git a/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs b/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs index 8028d87..eba881f 100644 --- a/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs +++ b/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs | |||
@@ -236,15 +236,17 @@ namespace OpenSim.Region.Framework.Interfaces | |||
236 | List<TaskInventoryItem> GetInventoryItems(InventoryType type); | 236 | List<TaskInventoryItem> GetInventoryItems(InventoryType type); |
237 | 237 | ||
238 | /// <summary> | 238 | /// <summary> |
239 | /// Get the scene object referenced by an inventory item. | 239 | /// Get the scene object(s) referenced by an inventory item. |
240 | /// </summary> | 240 | /// </summary> |
241 | /// | 241 | /// |
242 | /// This is returned in a 'rez ready' state. That is, name, description, permissions and other details have | 242 | /// This is returned in a 'rez ready' state. That is, name, description, permissions and other details have |
243 | /// been adjusted to reflect the part and item from which it originates. | 243 | /// been adjusted to reflect the part and item from which it originates. |
244 | /// | 244 | /// |
245 | /// <param name="item"></param> | 245 | /// <param name="item">Inventory item</param> |
246 | /// <returns>The scene object. Null if the scene object asset couldn't be found</returns> | 246 | /// <param name="objlist">The scene objects</param> |
247 | SceneObjectGroup GetRezReadySceneObject(TaskInventoryItem item); | 247 | /// <param name="veclist">Relative offsets for each object</param> |
248 | /// <returns>true = success, false = the scene object asset couldn't be found</returns> | ||
249 | bool GetRezReadySceneObjects(TaskInventoryItem item, out List<SceneObjectGroup> objlist, out List<Vector3> veclist); | ||
248 | 250 | ||
249 | /// <summary> | 251 | /// <summary> |
250 | /// Update an existing inventory item. | 252 | /// Update an existing inventory item. |
diff --git a/OpenSim/Region/Framework/Interfaces/IEstateModule.cs b/OpenSim/Region/Framework/Interfaces/IEstateModule.cs index d49b24e..944c66b 100644 --- a/OpenSim/Region/Framework/Interfaces/IEstateModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IEstateModule.cs | |||
@@ -54,5 +54,10 @@ namespace OpenSim.Region.Framework.Interfaces | |||
54 | 54 | ||
55 | void setEstateTerrainBaseTexture(int level, UUID texture); | 55 | void setEstateTerrainBaseTexture(int level, UUID texture); |
56 | void setEstateTerrainTextureHeights(int corner, float lowValue, float highValue); | 56 | void setEstateTerrainTextureHeights(int corner, float lowValue, float highValue); |
57 | |||
58 | /// <summary> | ||
59 | /// Returns whether the transfer ID is being used for a terrain transfer. | ||
60 | /// </summary> | ||
61 | bool IsTerrainXfer(ulong xferID); | ||
57 | } | 62 | } |
58 | } | 63 | } |
diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 659bde9..f384462 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs | |||
@@ -31,6 +31,7 @@ using System.Collections; | |||
31 | using System.Reflection; | 31 | using System.Reflection; |
32 | using System.Text; | 32 | using System.Text; |
33 | using System.Timers; | 33 | using System.Timers; |
34 | using System.Xml; | ||
34 | using OpenMetaverse; | 35 | using OpenMetaverse; |
35 | using OpenMetaverse.Packets; | 36 | using OpenMetaverse.Packets; |
36 | using log4net; | 37 | using log4net; |
@@ -2301,6 +2302,85 @@ namespace OpenSim.Region.Framework.Scenes | |||
2301 | } | 2302 | } |
2302 | 2303 | ||
2303 | /// <summary> | 2304 | /// <summary> |
2305 | /// Returns the list of Scene Objects in an asset. | ||
2306 | /// </summary> | ||
2307 | /// <remarks> | ||
2308 | /// Returns one object if the asset is a regular object, and multiple objects for a coalesced object. | ||
2309 | /// </remarks> | ||
2310 | /// <param name="assetData">Asset data</param> | ||
2311 | /// <param name="attachment">Whether the item is an attachment</param> | ||
2312 | /// <param name="objlist">The objects included in the asset</param> | ||
2313 | /// <param name="veclist">Relative positions of the objects</param> | ||
2314 | /// <param name="bbox">Bounding box of all the objects</param> | ||
2315 | /// <param name="offsetHeight">Offset in the Z axis from the centre of the bounding box | ||
2316 | /// to the centre of the root prim (relevant only when returning a single object)</param> | ||
2317 | /// <returns>true = returning a single object; false = multiple objects</returns> | ||
2318 | public bool GetObjectsToRez(byte[] assetData, bool attachment, out List<SceneObjectGroup> objlist, out List<Vector3> veclist, | ||
2319 | out Vector3 bbox, out float offsetHeight) | ||
2320 | { | ||
2321 | objlist = new List<SceneObjectGroup>(); | ||
2322 | veclist = new List<Vector3>(); | ||
2323 | |||
2324 | XmlDocument doc = new XmlDocument(); | ||
2325 | string xmlData = Utils.BytesToString(assetData); | ||
2326 | doc.LoadXml(xmlData); | ||
2327 | XmlElement e = (XmlElement)doc.SelectSingleNode("/CoalescedObject"); | ||
2328 | |||
2329 | if (e == null || attachment) // Single | ||
2330 | { | ||
2331 | SceneObjectGroup g = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); | ||
2332 | |||
2333 | g.RootPart.AttachPoint = g.RootPart.Shape.State; | ||
2334 | g.RootPart.AttachOffset = g.AbsolutePosition; | ||
2335 | g.RootPart.AttachRotation = g.GroupRotation; | ||
2336 | if (g.RootPart.Shape.PCode != (byte)PCode.NewTree && | ||
2337 | g.RootPart.Shape.PCode != (byte)PCode.Tree) | ||
2338 | g.RootPart.Shape.State = 0; | ||
2339 | |||
2340 | objlist.Add(g); | ||
2341 | veclist.Add(new Vector3(0, 0, 0)); | ||
2342 | bbox = g.GetAxisAlignedBoundingBox(out offsetHeight); | ||
2343 | return true; | ||
2344 | } | ||
2345 | else | ||
2346 | { | ||
2347 | XmlElement coll = (XmlElement)e; | ||
2348 | float bx = Convert.ToSingle(coll.GetAttribute("x")); | ||
2349 | float by = Convert.ToSingle(coll.GetAttribute("y")); | ||
2350 | float bz = Convert.ToSingle(coll.GetAttribute("z")); | ||
2351 | bbox = new Vector3(bx, by, bz); | ||
2352 | offsetHeight = 0; | ||
2353 | |||
2354 | XmlNodeList groups = e.SelectNodes("SceneObjectGroup"); | ||
2355 | foreach (XmlNode n in groups) | ||
2356 | { | ||
2357 | SceneObjectGroup g = SceneObjectSerializer.FromOriginalXmlFormat(n.OuterXml); | ||
2358 | |||
2359 | g.RootPart.AttachPoint = g.RootPart.Shape.State; | ||
2360 | g.RootPart.AttachOffset = g.AbsolutePosition; | ||
2361 | g.RootPart.AttachRotation = g.GroupRotation; | ||
2362 | if (g.RootPart.Shape.PCode != (byte)PCode.NewTree && | ||
2363 | g.RootPart.Shape.PCode != (byte)PCode.Tree) | ||
2364 | g.RootPart.Shape.State = 0; | ||
2365 | |||
2366 | objlist.Add(g); | ||
2367 | |||
2368 | XmlElement el = (XmlElement)n; | ||
2369 | string rawX = el.GetAttribute("offsetx"); | ||
2370 | string rawY = el.GetAttribute("offsety"); | ||
2371 | string rawZ = el.GetAttribute("offsetz"); | ||
2372 | |||
2373 | float x = Convert.ToSingle(rawX); | ||
2374 | float y = Convert.ToSingle(rawY); | ||
2375 | float z = Convert.ToSingle(rawZ); | ||
2376 | veclist.Add(new Vector3(x, y, z)); | ||
2377 | } | ||
2378 | } | ||
2379 | |||
2380 | return false; | ||
2381 | } | ||
2382 | |||
2383 | /// <summary> | ||
2304 | /// Event Handler Rez an object into a scene | 2384 | /// Event Handler Rez an object into a scene |
2305 | /// Calls the non-void event handler | 2385 | /// Calls the non-void event handler |
2306 | /// </summary> | 2386 | /// </summary> |
@@ -2375,19 +2455,25 @@ namespace OpenSim.Region.Framework.Scenes | |||
2375 | /// will be used if it exists.</param> | 2455 | /// will be used if it exists.</param> |
2376 | /// <param name="vel">The velocity of the rezzed object.</param> | 2456 | /// <param name="vel">The velocity of the rezzed object.</param> |
2377 | /// <param name="param"></param> | 2457 | /// <param name="param"></param> |
2378 | /// <returns>The SceneObjectGroup rezzed or null if rez was unsuccessful</returns> | 2458 | /// <returns>The SceneObjectGroup(s) rezzed, or null if rez was unsuccessful</returns> |
2379 | public virtual SceneObjectGroup RezObject( | 2459 | public virtual List<SceneObjectGroup> RezObject( |
2380 | SceneObjectPart sourcePart, TaskInventoryItem item, Vector3 pos, Quaternion? rot, Vector3 vel, int param) | 2460 | SceneObjectPart sourcePart, TaskInventoryItem item, Vector3 pos, Quaternion? rot, Vector3 vel, int param) |
2381 | { | 2461 | { |
2382 | if (null == item) | 2462 | if (null == item) |
2383 | return null; | 2463 | return null; |
2464 | |||
2465 | List<SceneObjectGroup> objlist; | ||
2466 | List<Vector3> veclist; | ||
2384 | 2467 | ||
2385 | SceneObjectGroup group = sourcePart.Inventory.GetRezReadySceneObject(item); | 2468 | bool success = sourcePart.Inventory.GetRezReadySceneObjects(item, out objlist, out veclist); |
2386 | 2469 | if (!success) | |
2387 | if (null == group) | ||
2388 | return null; | 2470 | return null; |
2389 | 2471 | ||
2390 | if (!Permissions.CanRezObject(group.PrimCount, item.OwnerID, pos)) | 2472 | int totalPrims = 0; |
2473 | foreach (SceneObjectGroup group in objlist) | ||
2474 | totalPrims += group.PrimCount; | ||
2475 | |||
2476 | if (!Permissions.CanRezObject(totalPrims, item.OwnerID, pos)) | ||
2391 | return null; | 2477 | return null; |
2392 | 2478 | ||
2393 | if (!Permissions.BypassPermissions()) | 2479 | if (!Permissions.BypassPermissions()) |
@@ -2396,23 +2482,28 @@ namespace OpenSim.Region.Framework.Scenes | |||
2396 | sourcePart.Inventory.RemoveInventoryItem(item.ItemID); | 2482 | sourcePart.Inventory.RemoveInventoryItem(item.ItemID); |
2397 | } | 2483 | } |
2398 | 2484 | ||
2399 | 2485 | for (int i = 0; i < objlist.Count; i++) | |
2400 | if (group.IsAttachment == false && group.RootPart.Shape.State != 0) | ||
2401 | { | 2486 | { |
2402 | group.RootPart.AttachedPos = group.AbsolutePosition; | 2487 | SceneObjectGroup group = objlist[i]; |
2403 | group.RootPart.Shape.LastAttachPoint = (byte)group.AttachmentPoint; | 2488 | Vector3 curpos = pos + veclist[i]; |
2489 | |||
2490 | if (group.IsAttachment == false && group.RootPart.Shape.State != 0) | ||
2491 | { | ||
2492 | group.RootPart.AttachedPos = group.AbsolutePosition; | ||
2493 | group.RootPart.Shape.LastAttachPoint = (byte)group.AttachmentPoint; | ||
2494 | } | ||
2495 | |||
2496 | group.FromPartID = sourcePart.UUID; | ||
2497 | AddNewSceneObject(group, true, curpos, rot, vel); | ||
2498 | |||
2499 | // We can only call this after adding the scene object, since the scene object references the scene | ||
2500 | // to find out if scripts should be activated at all. | ||
2501 | group.CreateScriptInstances(param, true, DefaultScriptEngine, 3); | ||
2502 | |||
2503 | group.ScheduleGroupForFullUpdate(); | ||
2404 | } | 2504 | } |
2405 | 2505 | ||
2406 | group.FromPartID = sourcePart.UUID; | 2506 | return objlist; |
2407 | AddNewSceneObject(group, true, pos, rot, vel); | ||
2408 | |||
2409 | // We can only call this after adding the scene object, since the scene object references the scene | ||
2410 | // to find out if scripts should be activated at all. | ||
2411 | group.CreateScriptInstances(param, true, DefaultScriptEngine, 3); | ||
2412 | |||
2413 | group.ScheduleGroupForFullUpdate(); | ||
2414 | |||
2415 | return group; | ||
2416 | } | 2507 | } |
2417 | 2508 | ||
2418 | public virtual bool returnObjects(SceneObjectGroup[] returnobjects, | 2509 | public virtual bool returnObjects(SceneObjectGroup[] returnobjects, |
diff --git a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs index 421cb08..46b2d2e 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs | |||
@@ -252,8 +252,6 @@ namespace OpenSim.Region.Framework.Scenes | |||
252 | if (part.ParentGroup.RootPart.LocalId != part.LocalId) | 252 | if (part.ParentGroup.RootPart.LocalId != part.LocalId) |
253 | return; | 253 | return; |
254 | 254 | ||
255 | bool isAttachment = false; | ||
256 | |||
257 | // This is wrong, wrong, wrong. Selection should not be | 255 | // This is wrong, wrong, wrong. Selection should not be |
258 | // handled by group, but by prim. Legacy cruft. | 256 | // handled by group, but by prim. Legacy cruft. |
259 | // TODO: Make selection flagging per prim! | 257 | // TODO: Make selection flagging per prim! |
@@ -262,17 +260,14 @@ namespace OpenSim.Region.Framework.Scenes | |||
262 | || Permissions.CanMoveObject(part.ParentGroup.UUID, remoteClient.AgentId)) | 260 | || Permissions.CanMoveObject(part.ParentGroup.UUID, remoteClient.AgentId)) |
263 | part.ParentGroup.IsSelected = false; | 261 | part.ParentGroup.IsSelected = false; |
264 | 262 | ||
265 | if (part.ParentGroup.IsAttachment) | 263 | part.ParentGroup.ScheduleGroupForFullUpdate(); |
266 | isAttachment = true; | ||
267 | else | ||
268 | part.ParentGroup.ScheduleGroupForFullUpdate(); | ||
269 | 264 | ||
270 | // If it's not an attachment, and we are allowed to move it, | 265 | // If it's not an attachment, and we are allowed to move it, |
271 | // then we might have done so. If we moved across a parcel | 266 | // then we might have done so. If we moved across a parcel |
272 | // boundary, we will need to recount prims on the parcels. | 267 | // boundary, we will need to recount prims on the parcels. |
273 | // For attachments, that makes no sense. | 268 | // For attachments, that makes no sense. |
274 | // | 269 | // |
275 | if (!isAttachment) | 270 | if (!part.ParentGroup.IsAttachment) |
276 | { | 271 | { |
277 | if (Permissions.CanEditObject( | 272 | if (Permissions.CanEditObject( |
278 | part.UUID, remoteClient.AgentId) | 273 | part.UUID, remoteClient.AgentId) |
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 66b42a1..f5cb84b 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | |||
@@ -3158,7 +3158,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
3158 | return; | 3158 | return; |
3159 | 3159 | ||
3160 | // This was pulled from SceneViewer. Attachments always receive full updates. | 3160 | // This was pulled from SceneViewer. Attachments always receive full updates. |
3161 | // I could not verify if this is a requirement but this maintains existing behavior | 3161 | // This is needed because otherwise if only the root prim changes position, then |
3162 | // it looks as if the entire object has moved (including the other prims). | ||
3162 | if (ParentGroup.IsAttachment) | 3163 | if (ParentGroup.IsAttachment) |
3163 | { | 3164 | { |
3164 | ScheduleFullUpdate(); | 3165 | ScheduleFullUpdate(); |
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs index 3be0623..b4fc472 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs | |||
@@ -881,8 +881,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
881 | 881 | ||
882 | return items; | 882 | return items; |
883 | } | 883 | } |
884 | 884 | ||
885 | public SceneObjectGroup GetRezReadySceneObject(TaskInventoryItem item) | 885 | public bool GetRezReadySceneObjects(TaskInventoryItem item, out List<SceneObjectGroup> objlist, out List<Vector3> veclist) |
886 | { | 886 | { |
887 | AssetBase rezAsset = m_part.ParentGroup.Scene.AssetService.Get(item.AssetID.ToString()); | 887 | AssetBase rezAsset = m_part.ParentGroup.Scene.AssetService.Get(item.AssetID.ToString()); |
888 | 888 | ||
@@ -891,70 +891,79 @@ namespace OpenSim.Region.Framework.Scenes | |||
891 | m_log.WarnFormat( | 891 | m_log.WarnFormat( |
892 | "[PRIM INVENTORY]: Could not find asset {0} for inventory item {1} in {2}", | 892 | "[PRIM INVENTORY]: Could not find asset {0} for inventory item {1} in {2}", |
893 | item.AssetID, item.Name, m_part.Name); | 893 | item.AssetID, item.Name, m_part.Name); |
894 | return null; | 894 | objlist = null; |
895 | veclist = null; | ||
896 | return false; | ||
895 | } | 897 | } |
896 | 898 | ||
897 | string xmlData = Utils.BytesToString(rezAsset.Data); | 899 | Vector3 bbox; |
898 | SceneObjectGroup group = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); | 900 | float offsetHeight; |
899 | 901 | ||
900 | group.RootPart.AttachPoint = group.RootPart.Shape.State; | 902 | bool single = m_part.ParentGroup.Scene.GetObjectsToRez(rezAsset.Data, false, out objlist, out veclist, out bbox, out offsetHeight); |
901 | group.RootPart.AttachOffset = group.AbsolutePosition; | ||
902 | group.RootPart.AttachRotation = group.GroupRotation; | ||
903 | 903 | ||
904 | group.ResetIDs(); | 904 | for (int i = 0; i < objlist.Count; i++) |
905 | { | ||
906 | SceneObjectGroup group = objlist[i]; | ||
905 | 907 | ||
906 | SceneObjectPart rootPart = group.GetPart(group.UUID); | 908 | group.RootPart.AttachPoint = group.RootPart.Shape.State; |
909 | group.RootPart.AttachOffset = group.AbsolutePosition; | ||
910 | group.RootPart.AttachRotation = group.GroupRotation; | ||
907 | 911 | ||
908 | // Since renaming the item in the inventory does not affect the name stored | 912 | group.ResetIDs(); |
909 | // in the serialization, transfer the correct name from the inventory to the | ||
910 | // object itself before we rez. | ||
911 | rootPart.Name = item.Name; | ||
912 | rootPart.Description = item.Description; | ||
913 | 913 | ||
914 | SceneObjectPart[] partList = group.Parts; | 914 | SceneObjectPart rootPart = group.GetPart(group.UUID); |
915 | 915 | ||
916 | group.SetGroup(m_part.GroupID, null); | 916 | // Since renaming the item in the inventory does not affect the name stored |
917 | // in the serialization, transfer the correct name from the inventory to the | ||
918 | // object itself before we rez. | ||
919 | rootPart.Name = item.Name; | ||
920 | rootPart.Description = item.Description; | ||
917 | 921 | ||
918 | // TODO: Remove magic number badness | 922 | SceneObjectPart[] partList = group.Parts; |
919 | if ((rootPart.OwnerID != item.OwnerID) || (item.CurrentPermissions & 16) != 0 || (item.Flags & (uint)InventoryItemFlags.ObjectSlamPerm) != 0) // Magic number | 923 | |
920 | { | 924 | group.SetGroup(m_part.GroupID, null); |
921 | if (m_part.ParentGroup.Scene.Permissions.PropagatePermissions()) | 925 | |
926 | // TODO: Remove magic number badness | ||
927 | if ((rootPart.OwnerID != item.OwnerID) || (item.CurrentPermissions & 16) != 0 || (item.Flags & (uint)InventoryItemFlags.ObjectSlamPerm) != 0) // Magic number | ||
922 | { | 928 | { |
923 | foreach (SceneObjectPart part in partList) | 929 | if (m_part.ParentGroup.Scene.Permissions.PropagatePermissions()) |
924 | { | 930 | { |
925 | if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteEveryone) != 0) | 931 | foreach (SceneObjectPart part in partList) |
926 | part.EveryoneMask = item.EveryonePermissions; | 932 | { |
927 | if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteNextOwner) != 0) | 933 | if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteEveryone) != 0) |
928 | part.NextOwnerMask = item.NextPermissions; | 934 | part.EveryoneMask = item.EveryonePermissions; |
929 | if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteGroup) != 0) | 935 | if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteNextOwner) != 0) |
930 | part.GroupMask = item.GroupPermissions; | 936 | part.NextOwnerMask = item.NextPermissions; |
937 | if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteGroup) != 0) | ||
938 | part.GroupMask = item.GroupPermissions; | ||
939 | } | ||
940 | |||
941 | group.ApplyNextOwnerPermissions(); | ||
931 | } | 942 | } |
932 | |||
933 | group.ApplyNextOwnerPermissions(); | ||
934 | } | 943 | } |
935 | } | ||
936 | 944 | ||
937 | foreach (SceneObjectPart part in partList) | 945 | foreach (SceneObjectPart part in partList) |
938 | { | ||
939 | // TODO: Remove magic number badness | ||
940 | if ((part.OwnerID != item.OwnerID) || (item.CurrentPermissions & 16) != 0 || (item.Flags & (uint)InventoryItemFlags.ObjectSlamPerm) != 0) // Magic number | ||
941 | { | 946 | { |
942 | part.LastOwnerID = part.OwnerID; | 947 | // TODO: Remove magic number badness |
943 | part.OwnerID = item.OwnerID; | 948 | if ((part.OwnerID != item.OwnerID) || (item.CurrentPermissions & 16) != 0 || (item.Flags & (uint)InventoryItemFlags.ObjectSlamPerm) != 0) // Magic number |
944 | part.Inventory.ChangeInventoryOwner(item.OwnerID); | 949 | { |
950 | part.LastOwnerID = part.OwnerID; | ||
951 | part.OwnerID = item.OwnerID; | ||
952 | part.Inventory.ChangeInventoryOwner(item.OwnerID); | ||
953 | } | ||
954 | |||
955 | if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteEveryone) != 0) | ||
956 | part.EveryoneMask = item.EveryonePermissions; | ||
957 | if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteNextOwner) != 0) | ||
958 | part.NextOwnerMask = item.NextPermissions; | ||
959 | if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteGroup) != 0) | ||
960 | part.GroupMask = item.GroupPermissions; | ||
945 | } | 961 | } |
946 | 962 | ||
947 | if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteEveryone) != 0) | 963 | rootPart.TrimPermissions(); |
948 | part.EveryoneMask = item.EveryonePermissions; | ||
949 | if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteNextOwner) != 0) | ||
950 | part.NextOwnerMask = item.NextPermissions; | ||
951 | if ((item.Flags & (uint)InventoryItemFlags.ObjectOverwriteGroup) != 0) | ||
952 | part.GroupMask = item.GroupPermissions; | ||
953 | } | 964 | } |
954 | 965 | ||
955 | rootPart.TrimPermissions(); | 966 | return true; |
956 | |||
957 | return group; | ||
958 | } | 967 | } |
959 | 968 | ||
960 | /// <summary> | 969 | /// <summary> |
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs index 8a734e1..d2a6828 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs | |||
@@ -39,6 +39,7 @@ using OpenSim.Framework.Communications; | |||
39 | using OpenSim.Region.Framework.Interfaces; | 39 | using OpenSim.Region.Framework.Interfaces; |
40 | using OpenSim.Region.Framework.Scenes; | 40 | using OpenSim.Region.Framework.Scenes; |
41 | using OpenSim.Services.Interfaces; | 41 | using OpenSim.Services.Interfaces; |
42 | using System.Text; | ||
42 | using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; | 43 | using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; |
43 | 44 | ||
44 | namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | 45 | namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups |
@@ -421,44 +422,75 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
421 | string Subject = im.message.Substring(0, im.message.IndexOf('|')); | 422 | string Subject = im.message.Substring(0, im.message.IndexOf('|')); |
422 | string Message = im.message.Substring(Subject.Length + 1); | 423 | string Message = im.message.Substring(Subject.Length + 1); |
423 | 424 | ||
425 | InventoryItemBase item = null; | ||
426 | bool hasAttachment = false; | ||
427 | UUID itemID = UUID.Zero; //Assignment to quiet compiler | ||
428 | UUID ownerID = UUID.Zero; //Assignment to quiet compiler | ||
424 | byte[] bucket; | 429 | byte[] bucket; |
425 | 430 | ||
426 | if ((im.binaryBucket.Length == 1) && (im.binaryBucket[0] == 0)) | 431 | if (im.binaryBucket.Length >= 1 && im.binaryBucket[0] > 0) |
427 | { | ||
428 | bucket = new byte[19]; | ||
429 | bucket[0] = 0; //dunno | ||
430 | bucket[1] = 0; //dunno | ||
431 | GroupID.ToBytes(bucket, 2); | ||
432 | bucket[18] = 0; //dunno | ||
433 | } | ||
434 | else | ||
435 | { | 432 | { |
436 | string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket); | 433 | string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket); |
437 | binBucket = binBucket.Remove(0, 14).Trim(); | 434 | binBucket = binBucket.Remove(0, 14).Trim(); |
438 | if (m_debugEnabled) | 435 | |
436 | OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket); | ||
437 | if (binBucketOSD is OSD) | ||
439 | { | 438 | { |
440 | m_log.WarnFormat("I don't understand a group notice binary bucket of: {0}", binBucket); | 439 | OSDMap binBucketMap = (OSDMap)binBucketOSD; |
440 | |||
441 | itemID = binBucketMap["item_id"].AsUUID(); | ||
442 | ownerID = binBucketMap["owner_id"].AsUUID(); | ||
441 | 443 | ||
442 | OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket); | 444 | //Attempt to get the details of the attached item. |
443 | 445 | //If sender doesn't own the attachment, the item | |
444 | foreach (string key in binBucketOSD.Keys) | 446 | //variable will be set to null and attachment will |
447 | //not be included with the group notice. | ||
448 | Scene scene = (Scene)remoteClient.Scene; | ||
449 | item = new InventoryItemBase(itemID, ownerID); | ||
450 | item = scene.InventoryService.GetItem(item); | ||
451 | |||
452 | if (item != null) | ||
445 | { | 453 | { |
446 | if (binBucketOSD.ContainsKey(key)) | 454 | //Got item details so include the attachment. |
447 | { | 455 | hasAttachment = true; |
448 | m_log.WarnFormat("{0}: {1}", key, binBucketOSD[key].ToString()); | ||
449 | } | ||
450 | } | 456 | } |
451 | } | 457 | } |
452 | 458 | else | |
453 | // treat as if no attachment | 459 | { |
460 | m_log.DebugFormat("[Groups]: Received OSD with unexpected type: {0}", binBucketOSD.GetType()); | ||
461 | } | ||
462 | } | ||
463 | |||
464 | if (hasAttachment) | ||
465 | { | ||
466 | //Bucket contains information about attachment. | ||
467 | // | ||
468 | //Byte offset and description of bucket data: | ||
469 | //0: 1 byte indicating if attachment is present | ||
470 | //1: 1 byte indicating the type of attachment | ||
471 | //2: 16 bytes - Group UUID | ||
472 | //18: 16 bytes - UUID of the attachment owner | ||
473 | //34: 16 bytes - UUID of the attachment | ||
474 | //50: variable - Name of the attachment | ||
475 | //??: NUL byte to terminate the attachment name | ||
476 | byte[] name = Encoding.UTF8.GetBytes(item.Name); | ||
477 | bucket = new byte[51 + name.Length];//3 bytes, 3 UUIDs, and name | ||
478 | bucket[0] = 1; //Has attachment flag | ||
479 | bucket[1] = (byte)item.InvType; //Type of Attachment | ||
480 | GroupID.ToBytes(bucket, 2); | ||
481 | ownerID.ToBytes(bucket, 18); | ||
482 | itemID.ToBytes(bucket, 34); | ||
483 | name.CopyTo(bucket, 50); | ||
484 | } | ||
485 | else | ||
486 | { | ||
454 | bucket = new byte[19]; | 487 | bucket = new byte[19]; |
455 | bucket[0] = 0; //dunno | 488 | bucket[0] = 0; //Has attachment flag |
456 | bucket[1] = 0; //dunno | 489 | bucket[1] = 0; //Type of attachment |
457 | GroupID.ToBytes(bucket, 2); | 490 | GroupID.ToBytes(bucket, 2); |
458 | bucket[18] = 0; //dunno | 491 | bucket[18] = 0; //NUL terminate name of attachment |
459 | } | 492 | } |
460 | 493 | ||
461 | |||
462 | m_groupData.AddGroupNotice(GetRequestingAgentID(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket); | 494 | m_groupData.AddGroupNotice(GetRequestingAgentID(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket); |
463 | if (OnNewGroupNotice != null) | 495 | if (OnNewGroupNotice != null) |
464 | { | 496 | { |
@@ -483,7 +515,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
483 | 515 | ||
484 | if (member.AcceptNotices) | 516 | if (member.AcceptNotices) |
485 | { | 517 | { |
486 | // Build notice IIM | 518 | // Build notice IM |
487 | GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice); | 519 | GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice); |
488 | 520 | ||
489 | msg.toAgentID = member.AgentID.Guid; | 521 | msg.toAgentID = member.AgentID.Guid; |
@@ -492,10 +524,40 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
492 | } | 524 | } |
493 | } | 525 | } |
494 | } | 526 | } |
495 | 527 | ||
528 | if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryAccepted) | ||
529 | { | ||
530 | //Is bucket large enough to hold UUID of the attachment? | ||
531 | if (im.binaryBucket.Length < 16) | ||
532 | return; | ||
533 | |||
534 | UUID noticeID = new UUID(im.imSessionID); | ||
535 | |||
536 | GroupNoticeInfo notice = m_groupData.GetGroupNotice(GetRequestingAgentID(remoteClient), noticeID); | ||
537 | if (notice != null) | ||
538 | { | ||
539 | UUID giver = new UUID(notice.BinaryBucket, 18); | ||
540 | UUID attachmentUUID = new UUID(notice.BinaryBucket, 34); | ||
541 | |||
542 | if (m_debugEnabled) | ||
543 | m_log.DebugFormat("[Groups]: Giving inventory from {0} to {1}", giver, remoteClient.AgentId); | ||
544 | |||
545 | InventoryItemBase itemCopy = ((Scene)(remoteClient.Scene)).GiveInventoryItem(remoteClient.AgentId, | ||
546 | giver, attachmentUUID); | ||
547 | |||
548 | if (itemCopy == null) | ||
549 | { | ||
550 | remoteClient.SendAgentAlertMessage("Can't find item to give. Nothing given.", false); | ||
551 | return; | ||
552 | } | ||
553 | |||
554 | remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0); | ||
555 | } | ||
556 | } | ||
557 | |||
496 | // Interop, received special 210 code for ejecting a group member | 558 | // Interop, received special 210 code for ejecting a group member |
497 | // this only works within the comms servers domain, and won't work hypergrid | 559 | // this only works within the comms servers domain, and won't work hypergrid |
498 | // TODO:FIXME: Use a presense server of some kind to find out where the | 560 | // TODO:FIXME: Use a presence server of some kind to find out where the |
499 | // client actually is, and try contacting that region directly to notify them, | 561 | // client actually is, and try contacting that region directly to notify them, |
500 | // or provide the notification via xmlrpc update queue | 562 | // or provide the notification via xmlrpc update queue |
501 | if ((im.dialog == 210)) | 563 | if ((im.dialog == 210)) |
@@ -873,26 +935,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
873 | 935 | ||
874 | if (data != null) | 936 | if (data != null) |
875 | { | 937 | { |
876 | GroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), data.GroupID, null); | 938 | GridInstantMessage msg = CreateGroupNoticeIM(remoteClient.AgentId, groupNoticeID, (byte)InstantMessageDialog.GroupNoticeRequested); |
877 | |||
878 | GridInstantMessage msg = new GridInstantMessage(); | ||
879 | msg.imSessionID = UUID.Zero.Guid; | ||
880 | msg.fromAgentID = data.GroupID.Guid; | ||
881 | msg.toAgentID = GetRequestingAgentID(remoteClient).Guid; | ||
882 | msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); | ||
883 | msg.fromAgentName = "Group Notice : " + groupInfo == null ? "Unknown" : groupInfo.GroupName; | ||
884 | msg.message = data.noticeData.Subject + "|" + data.Message; | ||
885 | msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNoticeRequested; | ||
886 | msg.fromGroup = true; | ||
887 | msg.offline = (byte)0; | ||
888 | msg.ParentEstateID = 0; | ||
889 | msg.Position = Vector3.Zero; | ||
890 | msg.RegionID = UUID.Zero.Guid; | ||
891 | msg.binaryBucket = data.BinaryBucket; | ||
892 | 939 | ||
893 | OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient)); | 940 | OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient)); |
894 | } | 941 | } |
895 | |||
896 | } | 942 | } |
897 | 943 | ||
898 | public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) | 944 | public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) |
@@ -900,10 +946,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
900 | if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); | 946 | if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); |
901 | 947 | ||
902 | GridInstantMessage msg = new GridInstantMessage(); | 948 | GridInstantMessage msg = new GridInstantMessage(); |
903 | msg.imSessionID = UUID.Zero.Guid; | 949 | byte[] bucket; |
950 | |||
951 | msg.imSessionID = groupNoticeID.Guid; | ||
904 | msg.toAgentID = agentID.Guid; | 952 | msg.toAgentID = agentID.Guid; |
905 | msg.dialog = dialog; | 953 | msg.dialog = dialog; |
906 | // msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNotice; | ||
907 | msg.fromGroup = true; | 954 | msg.fromGroup = true; |
908 | msg.offline = (byte)1; // Allow this message to be stored for offline use | 955 | msg.offline = (byte)1; // Allow this message to be stored for offline use |
909 | msg.ParentEstateID = 0; | 956 | msg.ParentEstateID = 0; |
@@ -917,13 +964,38 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
917 | msg.timestamp = info.noticeData.Timestamp; | 964 | msg.timestamp = info.noticeData.Timestamp; |
918 | msg.fromAgentName = info.noticeData.FromName; | 965 | msg.fromAgentName = info.noticeData.FromName; |
919 | msg.message = info.noticeData.Subject + "|" + info.Message; | 966 | msg.message = info.noticeData.Subject + "|" + info.Message; |
920 | msg.binaryBucket = info.BinaryBucket; | 967 | |
968 | if (info.BinaryBucket[0] > 0) | ||
969 | { | ||
970 | //32 is due to not needing space for two of the UUIDs. | ||
971 | //(Don't need UUID of attachment or its owner in IM) | ||
972 | //50 offset gets us to start of attachment name. | ||
973 | //We are skipping the attachment flag, type, and | ||
974 | //the three UUID fields at the start of the bucket. | ||
975 | bucket = new byte[info.BinaryBucket.Length-32]; | ||
976 | bucket[0] = 1; //Has attachment | ||
977 | bucket[1] = info.BinaryBucket[1]; | ||
978 | Array.Copy(info.BinaryBucket, 50, | ||
979 | bucket, 18, info.BinaryBucket.Length-50); | ||
980 | } | ||
981 | else | ||
982 | { | ||
983 | bucket = new byte[19]; | ||
984 | bucket[0] = 0; //No attachment | ||
985 | bucket[1] = 0; //Attachment type | ||
986 | bucket[18] = 0; //NUL terminate name | ||
987 | } | ||
988 | |||
989 | info.GroupID.ToBytes(bucket, 2); | ||
990 | msg.binaryBucket = bucket; | ||
921 | } | 991 | } |
922 | else | 992 | else |
923 | { | 993 | { |
924 | if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Group Notice {0} not found, composing empty message.", groupNoticeID); | 994 | if (m_debugEnabled) |
995 | m_log.DebugFormat("[GROUPS]: Group Notice {0} not found, composing empty message.", groupNoticeID); | ||
996 | |||
925 | msg.fromAgentID = UUID.Zero.Guid; | 997 | msg.fromAgentID = UUID.Zero.Guid; |
926 | msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); ; | 998 | msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); |
927 | msg.fromAgentName = string.Empty; | 999 | msg.fromAgentName = string.Empty; |
928 | msg.message = string.Empty; | 1000 | msg.message = string.Empty; |
929 | msg.binaryBucket = new byte[0]; | 1001 | msg.binaryBucket = new byte[0]; |
@@ -1047,7 +1119,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups | |||
1047 | // Message to ejector | 1119 | // Message to ejector |
1048 | // Interop, received special 210 code for ejecting a group member | 1120 | // Interop, received special 210 code for ejecting a group member |
1049 | // this only works within the comms servers domain, and won't work hypergrid | 1121 | // this only works within the comms servers domain, and won't work hypergrid |
1050 | // TODO:FIXME: Use a presense server of some kind to find out where the | 1122 | // TODO:FIXME: Use a presence server of some kind to find out where the |
1051 | // client actually is, and try contacting that region directly to notify them, | 1123 | // client actually is, and try contacting that region directly to notify them, |
1052 | // or provide the notification via xmlrpc update queue | 1124 | // or provide the notification via xmlrpc update queue |
1053 | 1125 | ||
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index f0d17d3..7b98f9d 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | |||
@@ -1125,7 +1125,19 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
1125 | { | 1125 | { |
1126 | // If body is already heigher, use its height as target height | 1126 | // If body is already heigher, use its height as target height |
1127 | if (VehiclePosition.Z > m_VhoverTargetHeight) | 1127 | if (VehiclePosition.Z > m_VhoverTargetHeight) |
1128 | { | ||
1128 | m_VhoverTargetHeight = VehiclePosition.Z; | 1129 | m_VhoverTargetHeight = VehiclePosition.Z; |
1130 | |||
1131 | // A 'misfeature' of this flag is that if the vehicle is above it's hover height, | ||
1132 | // the vehicle's buoyancy goes away. This is an SL bug that got used by so many | ||
1133 | // scripts that it could not be changed. | ||
1134 | // So, if above the height, reapply gravity if buoyancy had it turned off. | ||
1135 | if (m_VehicleBuoyancy != 0) | ||
1136 | { | ||
1137 | Vector3 appliedGravity = ControllingPrim.ComputeGravity(ControllingPrim.Buoyancy) * m_vehicleMass; | ||
1138 | VehicleAddForce(appliedGravity); | ||
1139 | } | ||
1140 | } | ||
1129 | } | 1141 | } |
1130 | 1142 | ||
1131 | if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0) | 1143 | if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0) |
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index c4807c4..c016eed 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs | |||
@@ -354,6 +354,8 @@ public sealed class BSTerrainManager : IDisposable | |||
354 | // Return a new position that is over known terrain if the position is outside our terrain. | 354 | // Return a new position that is over known terrain if the position is outside our terrain. |
355 | public Vector3 ClampPositionIntoKnownTerrain(Vector3 pPos) | 355 | public Vector3 ClampPositionIntoKnownTerrain(Vector3 pPos) |
356 | { | 356 | { |
357 | float edgeEpsilon = 0.1f; | ||
358 | |||
357 | Vector3 ret = pPos; | 359 | Vector3 ret = pPos; |
358 | 360 | ||
359 | // First, base addresses are never negative so correct for that possible problem. | 361 | // First, base addresses are never negative so correct for that possible problem. |
@@ -378,10 +380,19 @@ public sealed class BSTerrainManager : IDisposable | |||
378 | // NOTE that GetTerrainPhysicalAtXYZ will set 'terrainBaseXYZ' to the base of the unfound region. | 380 | // NOTE that GetTerrainPhysicalAtXYZ will set 'terrainBaseXYZ' to the base of the unfound region. |
379 | 381 | ||
380 | // Must be off the top of a region. Find an adjacent region to move into. | 382 | // Must be off the top of a region. Find an adjacent region to move into. |
383 | // The returned terrain is always 'lower'. That is, closer to <0,0>. | ||
381 | Vector3 adjacentTerrainBase = FindAdjacentTerrainBase(terrainBaseXYZ); | 384 | Vector3 adjacentTerrainBase = FindAdjacentTerrainBase(terrainBaseXYZ); |
382 | 385 | ||
383 | ret.X = Math.Min(ret.X, adjacentTerrainBase.X + (ret.X % DefaultRegionSize.X)); | 386 | if (adjacentTerrainBase.X < terrainBaseXYZ.X) |
384 | ret.Y = Math.Min(ret.Y, adjacentTerrainBase.Y + (ret.X % DefaultRegionSize.Y)); | 387 | { |
388 | // moving down into a new region in the X dimension. New position will be the max in the new base. | ||
389 | ret.X = adjacentTerrainBase.X + DefaultRegionSize.X - edgeEpsilon; | ||
390 | } | ||
391 | if (adjacentTerrainBase.Y < terrainBaseXYZ.Y) | ||
392 | { | ||
393 | // moving down into a new region in the X dimension. New position will be the max in the new base. | ||
394 | ret.Y = adjacentTerrainBase.Y + DefaultRegionSize.Y - edgeEpsilon; | ||
395 | } | ||
385 | DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,findingAdjacentRegion,adjacentRegBase={1},oldPos={2},newPos={3}", | 396 | DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,findingAdjacentRegion,adjacentRegBase={1},oldPos={2},newPos={3}", |
386 | BSScene.DetailLogZero, adjacentTerrainBase, pPos, ret); | 397 | BSScene.DetailLogZero, adjacentTerrainBase, pPos, ret); |
387 | 398 | ||
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 8892b69..89f4706 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs | |||
@@ -3247,46 +3247,41 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
3247 | // need the magnitude later | 3247 | // need the magnitude later |
3248 | // float velmag = (float)Util.GetMagnitude(llvel); | 3248 | // float velmag = (float)Util.GetMagnitude(llvel); |
3249 | 3249 | ||
3250 | SceneObjectGroup new_group = World.RezObject(m_host, item, pos, rot, vel, param); | 3250 | List<SceneObjectGroup> new_groups = World.RezObject(m_host, item, pos, rot, vel, param); |
3251 | 3251 | ||
3252 | // If either of these are null, then there was an unknown error. | 3252 | // If either of these are null, then there was an unknown error. |
3253 | if (new_group == null) | 3253 | if (new_groups == null) |
3254 | return; | 3254 | return; |
3255 | 3255 | ||
3256 | // objects rezzed with this method are die_at_edge by default. | 3256 | foreach (SceneObjectGroup group in new_groups) |
3257 | new_group.RootPart.SetDieAtEdge(true); | 3257 | { |
3258 | 3258 | // objects rezzed with this method are die_at_edge by default. | |
3259 | new_group.ResumeScripts(); | 3259 | group.RootPart.SetDieAtEdge(true); |
3260 | 3260 | ||
3261 | m_ScriptEngine.PostObjectEvent(m_host.LocalId, new EventParams( | 3261 | group.ResumeScripts(); |
3262 | "object_rez", new Object[] { | ||
3263 | new LSL_String( | ||
3264 | new_group.RootPart.UUID.ToString()) }, | ||
3265 | new DetectParams[0])); | ||
3266 | 3262 | ||
3267 | // do recoil | 3263 | m_ScriptEngine.PostObjectEvent(m_host.LocalId, new EventParams( |
3268 | SceneObjectGroup hostgrp = m_host.ParentGroup; | 3264 | "object_rez", new Object[] { |
3269 | if (hostgrp == null) | 3265 | new LSL_String( |
3270 | return; | 3266 | group.RootPart.UUID.ToString()) }, |
3267 | new DetectParams[0])); | ||
3271 | 3268 | ||
3272 | if (hostgrp.IsAttachment) // don't recoil avatars | 3269 | float groupmass = group.GetMass(); |
3273 | return; | ||
3274 | 3270 | ||
3275 | PhysicsActor pa = new_group.RootPart.PhysActor; | 3271 | PhysicsActor pa = group.RootPart.PhysActor; |
3276 | 3272 | ||
3277 | //Recoil. | 3273 | //Recoil. |
3278 | if (pa != null && pa.IsPhysical && (Vector3)vel != Vector3.Zero) | 3274 | if (pa != null && pa.IsPhysical && (Vector3)vel != Vector3.Zero) |
3279 | { | ||
3280 | float groupmass = new_group.GetMass(); | ||
3281 | Vector3 recoil = -vel * groupmass * m_recoilScaleFactor; | ||
3282 | if (recoil != Vector3.Zero) | ||
3283 | { | 3275 | { |
3284 | llApplyImpulse(recoil, 0); | 3276 | Vector3 recoil = -vel * groupmass * m_recoilScaleFactor; |
3277 | if (recoil != Vector3.Zero) | ||
3278 | { | ||
3279 | llApplyImpulse(recoil, 0); | ||
3280 | } | ||
3285 | } | 3281 | } |
3282 | // Variable script delay? (see (http://wiki.secondlife.com/wiki/LSL_Delay) | ||
3286 | } | 3283 | } |
3287 | // Variable script delay? (see (http://wiki.secondlife.com/wiki/LSL_Delay) | ||
3288 | return; | 3284 | return; |
3289 | |||
3290 | }); | 3285 | }); |
3291 | 3286 | ||
3292 | //ScriptSleep((int)((groupmass * velmag) / 10)); | 3287 | //ScriptSleep((int)((groupmass * velmag) / 10)); |