diff options
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/Scene.Inventory.cs')
-rw-r--r-- | OpenSim/Region/Framework/Scenes/Scene.Inventory.cs | 203 |
1 files changed, 168 insertions, 35 deletions
diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index d70aa45..746a5ce 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; |
@@ -139,7 +140,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
139 | { | 140 | { |
140 | userlevel = 1; | 141 | userlevel = 1; |
141 | } | 142 | } |
142 | EventManager.TriggerOnNewInventoryItemUploadComplete(item.Owner, item.AssetID, item.Name, userlevel); | 143 | EventManager.TriggerOnNewInventoryItemUploadComplete(item.Owner, (AssetType)item.AssetType, item.AssetID, item.Name, userlevel); |
143 | 144 | ||
144 | return true; | 145 | return true; |
145 | } | 146 | } |
@@ -178,7 +179,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
178 | { | 179 | { |
179 | userlevel = 1; | 180 | userlevel = 1; |
180 | } | 181 | } |
181 | EventManager.TriggerOnNewInventoryItemUploadComplete(item.Owner, item.AssetID, item.Name, userlevel); | 182 | EventManager.TriggerOnNewInventoryItemUploadComplete(item.Owner, (AssetType)item.AssetType, item.AssetID, item.Name, userlevel); |
182 | 183 | ||
183 | if (originalFolder != UUID.Zero) | 184 | if (originalFolder != UUID.Zero) |
184 | { | 185 | { |
@@ -411,19 +412,21 @@ namespace OpenSim.Region.Framework.Scenes | |||
411 | // itemUpd.NextPermissions, itemUpd.GroupPermissions, itemUpd.EveryOnePermissions, item.Flags, | 412 | // itemUpd.NextPermissions, itemUpd.GroupPermissions, itemUpd.EveryOnePermissions, item.Flags, |
412 | // item.NextPermissions, item.GroupPermissions, item.EveryOnePermissions, item.CurrentPermissions); | 413 | // item.NextPermissions, item.GroupPermissions, item.EveryOnePermissions, item.CurrentPermissions); |
413 | 414 | ||
415 | bool sendUpdate = false; | ||
416 | |||
414 | if (itemUpd.NextPermissions != 0) // Use this to determine validity. Can never be 0 if valid | 417 | if (itemUpd.NextPermissions != 0) // Use this to determine validity. Can never be 0 if valid |
415 | { | 418 | { |
416 | // Create a set of base permissions that will not include export if the user | 419 | // Create a set of base permissions that will not include export if the user |
417 | // is not allowed to change the export flag. | 420 | // is not allowed to change the export flag. |
418 | bool denyExportChange = false; | 421 | bool denyExportChange = false; |
419 | 422 | ||
420 | m_log.InfoFormat("[XXX]: B: {0} O: {1} E: {2}", itemUpd.BasePermissions, itemUpd.CurrentPermissions, itemUpd.EveryOnePermissions); | 423 | // m_log.DebugFormat("[XXX]: B: {0} O: {1} E: {2}", itemUpd.BasePermissions, itemUpd.CurrentPermissions, itemUpd.EveryOnePermissions); |
421 | 424 | ||
422 | // If the user is not the creator or doesn't have "E" in both "B" and "O", deny setting export | 425 | // If the user is not the creator or doesn't have "E" in both "B" and "O", deny setting export |
423 | if ((item.BasePermissions & (uint)(PermissionMask.All | PermissionMask.Export)) != (uint)(PermissionMask.All | PermissionMask.Export) || (item.CurrentPermissions & (uint)PermissionMask.Export) == 0 || item.CreatorIdAsUuid != item.Owner) | 426 | if ((item.BasePermissions & (uint)(PermissionMask.All | PermissionMask.Export)) != (uint)(PermissionMask.All | PermissionMask.Export) || (item.CurrentPermissions & (uint)PermissionMask.Export) == 0 || item.CreatorIdAsUuid != item.Owner) |
424 | denyExportChange = true; | 427 | denyExportChange = true; |
425 | 428 | ||
426 | m_log.InfoFormat("[XXX]: Deny Export Update {0}", denyExportChange); | 429 | // m_log.DebugFormat("[XXX]: Deny Export Update {0}", denyExportChange); |
427 | 430 | ||
428 | // If it is already set, force it set and also force full perm | 431 | // If it is already set, force it set and also force full perm |
429 | // else prevent setting it. It can and should never be set unless | 432 | // else prevent setting it. It can and should never be set unless |
@@ -447,7 +450,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
447 | // If the new state is exportable, force full perm | 450 | // If the new state is exportable, force full perm |
448 | if ((itemUpd.EveryOnePermissions & (uint)PermissionMask.Export) != 0) | 451 | if ((itemUpd.EveryOnePermissions & (uint)PermissionMask.Export) != 0) |
449 | { | 452 | { |
450 | m_log.InfoFormat("[XXX]: Force full perm"); | 453 | // m_log.DebugFormat("[XXX]: Force full perm"); |
451 | itemUpd.NextPermissions = (uint)(PermissionMask.All); | 454 | itemUpd.NextPermissions = (uint)(PermissionMask.All); |
452 | } | 455 | } |
453 | } | 456 | } |
@@ -484,8 +487,13 @@ namespace OpenSim.Region.Framework.Scenes | |||
484 | item.SalePrice = itemUpd.SalePrice; | 487 | item.SalePrice = itemUpd.SalePrice; |
485 | item.SaleType = itemUpd.SaleType; | 488 | item.SaleType = itemUpd.SaleType; |
486 | 489 | ||
490 | if (item.InvType == (int)InventoryType.Wearable && (item.Flags & 0xf) == 0 && (itemUpd.Flags & 0xf) != 0) | ||
491 | { | ||
492 | item.Flags = (uint)(item.Flags & 0xfffffff0) | (itemUpd.Flags & 0xf); | ||
493 | sendUpdate = true; | ||
494 | } | ||
495 | |||
487 | InventoryService.UpdateItem(item); | 496 | InventoryService.UpdateItem(item); |
488 | remoteClient.SendBulkUpdateInventory(item); | ||
489 | } | 497 | } |
490 | 498 | ||
491 | if (UUID.Zero != transactionID) | 499 | if (UUID.Zero != transactionID) |
@@ -495,6 +503,17 @@ namespace OpenSim.Region.Framework.Scenes | |||
495 | AgentTransactionsModule.HandleItemUpdateFromTransaction(remoteClient, transactionID, item); | 503 | AgentTransactionsModule.HandleItemUpdateFromTransaction(remoteClient, transactionID, item); |
496 | } | 504 | } |
497 | } | 505 | } |
506 | else | ||
507 | { | ||
508 | // This MAY be problematic, if it is, another solution | ||
509 | // needs to be found. If inventory item flags are updated | ||
510 | // the viewer's notion of the item needs to be refreshed. | ||
511 | // | ||
512 | // In other situations we cannot send out a bulk update here, since this will cause editing of clothing to start | ||
513 | // failing frequently. Possibly this is a race with a separate transaction that uploads the asset. | ||
514 | if (sendUpdate) | ||
515 | remoteClient.SendBulkUpdateInventory(item); | ||
516 | } | ||
498 | } | 517 | } |
499 | else | 518 | else |
500 | { | 519 | { |
@@ -548,6 +567,9 @@ namespace OpenSim.Region.Framework.Scenes | |||
548 | { | 567 | { |
549 | //Console.WriteLine("Scene.Inventory.cs: GiveInventoryItem"); | 568 | //Console.WriteLine("Scene.Inventory.cs: GiveInventoryItem"); |
550 | 569 | ||
570 | if (!Permissions.CanTransferUserInventory(itemId, senderId, recipient)) | ||
571 | return null; | ||
572 | |||
551 | InventoryItemBase item = new InventoryItemBase(itemId, senderId); | 573 | InventoryItemBase item = new InventoryItemBase(itemId, senderId); |
552 | item = InventoryService.GetItem(item); | 574 | item = InventoryService.GetItem(item); |
553 | 575 | ||
@@ -642,17 +664,13 @@ namespace OpenSim.Region.Framework.Scenes | |||
642 | // a mask | 664 | // a mask |
643 | if (item.InvType == (int)InventoryType.Object) | 665 | if (item.InvType == (int)InventoryType.Object) |
644 | { | 666 | { |
645 | // Create a safe mask for the current perms | ||
646 | uint foldedPerms = (item.CurrentPermissions & 7) << 13; | ||
647 | foldedPerms |= permsMask; | ||
648 | |||
649 | bool isRootMod = (item.CurrentPermissions & | 667 | bool isRootMod = (item.CurrentPermissions & |
650 | (uint)PermissionMask.Modify) != 0 ? | 668 | (uint)PermissionMask.Modify) != 0 ? |
651 | true : false; | 669 | true : false; |
652 | 670 | ||
653 | // Mask the owner perms to the folded perms | 671 | // Mask the owner perms to the folded perms |
654 | ownerPerms &= foldedPerms; | 672 | PermissionsUtil.ApplyFoldedPermissions(item.CurrentPermissions, ref ownerPerms); |
655 | basePerms &= foldedPerms; | 673 | PermissionsUtil.ApplyFoldedPermissions(item.CurrentPermissions, ref basePerms); |
656 | 674 | ||
657 | // If the root was mod, let the mask reflect that | 675 | // If the root was mod, let the mask reflect that |
658 | // We also need to adjust the base here, because | 676 | // We also need to adjust the base here, because |
@@ -1005,6 +1023,10 @@ namespace OpenSim.Region.Framework.Scenes | |||
1005 | item.BasePermissions = baseMask; | 1023 | item.BasePermissions = baseMask; |
1006 | item.CreationDate = creationDate; | 1024 | item.CreationDate = creationDate; |
1007 | 1025 | ||
1026 | // special AnimationSet case | ||
1027 | if (item.InvType == (int)CustomInventoryType.AnimationSet) | ||
1028 | AnimationSet.enforceItemPermitions(item,true); | ||
1029 | |||
1008 | if (AddInventoryItem(item)) | 1030 | if (AddInventoryItem(item)) |
1009 | { | 1031 | { |
1010 | remoteClient.SendInventoryItemCreateUpdate(item, transationID, callbackID); | 1032 | remoteClient.SendInventoryItemCreateUpdate(item, transationID, callbackID); |
@@ -1213,9 +1235,15 @@ namespace OpenSim.Region.Framework.Scenes | |||
1213 | { | 1235 | { |
1214 | agentItem.BasePermissions = taskItem.BasePermissions & (taskItem.NextPermissions | (uint)PermissionMask.Move); | 1236 | agentItem.BasePermissions = taskItem.BasePermissions & (taskItem.NextPermissions | (uint)PermissionMask.Move); |
1215 | if (taskItem.InvType == (int)InventoryType.Object) | 1237 | if (taskItem.InvType == (int)InventoryType.Object) |
1216 | agentItem.CurrentPermissions = agentItem.BasePermissions & (((taskItem.CurrentPermissions & 7) << 13) | (taskItem.CurrentPermissions & (uint)PermissionMask.Move)); | 1238 | { |
1217 | else | 1239 | uint perms = taskItem.BasePermissions & taskItem.NextPermissions; |
1218 | agentItem.CurrentPermissions = agentItem.BasePermissions & taskItem.CurrentPermissions; | 1240 | PermissionsUtil.ApplyFoldedPermissions(taskItem.CurrentPermissions, ref perms); |
1241 | // agentItem.BasePermissions = perms | (uint)PermissionMask.Move; | ||
1242 | // agentItem.CurrentPermissions = agentItem.BasePermissions; | ||
1243 | agentItem.BasePermissions = perms | (uint)PermissionMask.Move; | ||
1244 | } | ||
1245 | |||
1246 | agentItem.CurrentPermissions = agentItem.BasePermissions; | ||
1219 | 1247 | ||
1220 | agentItem.Flags |= (uint)InventoryItemFlags.ObjectSlamPerm; | 1248 | agentItem.Flags |= (uint)InventoryItemFlags.ObjectSlamPerm; |
1221 | agentItem.NextPermissions = taskItem.NextPermissions; | 1249 | agentItem.NextPermissions = taskItem.NextPermissions; |
@@ -2068,9 +2096,6 @@ namespace OpenSim.Region.Framework.Scenes | |||
2068 | // If child prims have invalid perms, fix them | 2096 | // If child prims have invalid perms, fix them |
2069 | grp.AdjustChildPrimPermissions(); | 2097 | grp.AdjustChildPrimPermissions(); |
2070 | 2098 | ||
2071 | // If child prims have invalid perms, fix them | ||
2072 | grp.AdjustChildPrimPermissions(); | ||
2073 | |||
2074 | if (remoteClient == null) | 2099 | if (remoteClient == null) |
2075 | { | 2100 | { |
2076 | // Autoreturn has a null client. Nothing else does. So | 2101 | // Autoreturn has a null client. Nothing else does. So |
@@ -2124,7 +2149,10 @@ namespace OpenSim.Region.Framework.Scenes | |||
2124 | { | 2149 | { |
2125 | // If we don't have permission, stop right here | 2150 | // If we don't have permission, stop right here |
2126 | if (!permissionToTakeCopy) | 2151 | if (!permissionToTakeCopy) |
2152 | { | ||
2153 | remoteClient.SendAlertMessage("You don't have permission to take the object"); | ||
2127 | return; | 2154 | return; |
2155 | } | ||
2128 | 2156 | ||
2129 | permissionToTake = true; | 2157 | permissionToTake = true; |
2130 | // Don't delete | 2158 | // Don't delete |
@@ -2277,6 +2305,88 @@ namespace OpenSim.Region.Framework.Scenes | |||
2277 | } | 2305 | } |
2278 | 2306 | ||
2279 | /// <summary> | 2307 | /// <summary> |
2308 | /// Returns the list of Scene Objects in an asset. | ||
2309 | /// </summary> | ||
2310 | /// <remarks> | ||
2311 | /// Returns one object if the asset is a regular object, and multiple objects for a coalesced object. | ||
2312 | /// </remarks> | ||
2313 | /// <param name="assetData">Asset data</param> | ||
2314 | /// <param name="attachment">Whether the item is an attachment</param> | ||
2315 | /// <param name="objlist">The objects included in the asset</param> | ||
2316 | /// <param name="veclist">Relative positions of the objects</param> | ||
2317 | /// <param name="bbox">Bounding box of all the objects</param> | ||
2318 | /// <param name="offsetHeight">Offset in the Z axis from the centre of the bounding box | ||
2319 | /// to the centre of the root prim (relevant only when returning a single object)</param> | ||
2320 | /// <returns>true = returning a single object; false = multiple objects</returns> | ||
2321 | public bool GetObjectsToRez(byte[] assetData, bool attachment, out List<SceneObjectGroup> objlist, out List<Vector3> veclist, | ||
2322 | out Vector3 bbox, out float offsetHeight) | ||
2323 | { | ||
2324 | objlist = new List<SceneObjectGroup>(); | ||
2325 | veclist = new List<Vector3>(); | ||
2326 | |||
2327 | XmlDocument doc = new XmlDocument(); | ||
2328 | string xmlData = Utils.BytesToString(assetData); | ||
2329 | doc.LoadXml(xmlData); | ||
2330 | XmlElement e = (XmlElement)doc.SelectSingleNode("/CoalescedObject"); | ||
2331 | |||
2332 | if (e == null || attachment) // Single | ||
2333 | { | ||
2334 | SceneObjectGroup g = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); | ||
2335 | /* | ||
2336 | if (!attachment) | ||
2337 | { | ||
2338 | g.RootPart.AttachPoint = g.RootPart.Shape.State; | ||
2339 | g.RootPart.AttachedPos = g.AbsolutePosition; | ||
2340 | g.RootPart.AttachRotation = g.GroupRotation; | ||
2341 | if (g.RootPart.Shape.PCode != (byte)PCode.NewTree && | ||
2342 | g.RootPart.Shape.PCode != (byte)PCode.Tree) | ||
2343 | g.RootPart.Shape.State = 0; | ||
2344 | } | ||
2345 | */ | ||
2346 | objlist.Add(g); | ||
2347 | veclist.Add(new Vector3(0, 0, 0)); | ||
2348 | bbox = g.GetAxisAlignedBoundingBox(out offsetHeight); | ||
2349 | return true; | ||
2350 | } | ||
2351 | else | ||
2352 | { | ||
2353 | XmlElement coll = (XmlElement)e; | ||
2354 | float bx = Convert.ToSingle(coll.GetAttribute("x")); | ||
2355 | float by = Convert.ToSingle(coll.GetAttribute("y")); | ||
2356 | float bz = Convert.ToSingle(coll.GetAttribute("z")); | ||
2357 | bbox = new Vector3(bx, by, bz); | ||
2358 | offsetHeight = 0; | ||
2359 | |||
2360 | XmlNodeList groups = e.SelectNodes("SceneObjectGroup"); | ||
2361 | foreach (XmlNode n in groups) | ||
2362 | { | ||
2363 | SceneObjectGroup g = SceneObjectSerializer.FromOriginalXmlFormat(n.OuterXml); | ||
2364 | /* | ||
2365 | g.RootPart.AttachPoint = g.RootPart.Shape.State; | ||
2366 | g.RootPart.AttachedPos = g.AbsolutePosition; | ||
2367 | g.RootPart.AttachRotation = g.GroupRotation; | ||
2368 | if (g.RootPart.Shape.PCode != (byte)PCode.NewTree && | ||
2369 | g.RootPart.Shape.PCode != (byte)PCode.Tree) | ||
2370 | g.RootPart.Shape.State = 0; | ||
2371 | */ | ||
2372 | objlist.Add(g); | ||
2373 | |||
2374 | XmlElement el = (XmlElement)n; | ||
2375 | string rawX = el.GetAttribute("offsetx"); | ||
2376 | string rawY = el.GetAttribute("offsety"); | ||
2377 | string rawZ = el.GetAttribute("offsetz"); | ||
2378 | |||
2379 | float x = Convert.ToSingle(rawX); | ||
2380 | float y = Convert.ToSingle(rawY); | ||
2381 | float z = Convert.ToSingle(rawZ); | ||
2382 | veclist.Add(new Vector3(x, y, z)); | ||
2383 | } | ||
2384 | } | ||
2385 | |||
2386 | return false; | ||
2387 | } | ||
2388 | |||
2389 | /// <summary> | ||
2280 | /// Event Handler Rez an object into a scene | 2390 | /// Event Handler Rez an object into a scene |
2281 | /// Calls the non-void event handler | 2391 | /// Calls the non-void event handler |
2282 | /// </summary> | 2392 | /// </summary> |
@@ -2351,19 +2461,25 @@ namespace OpenSim.Region.Framework.Scenes | |||
2351 | /// will be used if it exists.</param> | 2461 | /// will be used if it exists.</param> |
2352 | /// <param name="vel">The velocity of the rezzed object.</param> | 2462 | /// <param name="vel">The velocity of the rezzed object.</param> |
2353 | /// <param name="param"></param> | 2463 | /// <param name="param"></param> |
2354 | /// <returns>The SceneObjectGroup rezzed or null if rez was unsuccessful</returns> | 2464 | /// <returns>The SceneObjectGroup(s) rezzed, or null if rez was unsuccessful</returns> |
2355 | public virtual SceneObjectGroup RezObject( | 2465 | public virtual List<SceneObjectGroup> RezObject( |
2356 | SceneObjectPart sourcePart, TaskInventoryItem item, Vector3 pos, Quaternion? rot, Vector3 vel, int param) | 2466 | SceneObjectPart sourcePart, TaskInventoryItem item, Vector3 pos, Quaternion? rot, Vector3 vel, int param) |
2357 | { | 2467 | { |
2358 | if (null == item) | 2468 | if (null == item) |
2359 | return null; | 2469 | return null; |
2470 | |||
2471 | List<SceneObjectGroup> objlist; | ||
2472 | List<Vector3> veclist; | ||
2360 | 2473 | ||
2361 | SceneObjectGroup group = sourcePart.Inventory.GetRezReadySceneObject(item); | 2474 | bool success = sourcePart.Inventory.GetRezReadySceneObjects(item, out objlist, out veclist); |
2362 | 2475 | if (!success) | |
2363 | if (null == group) | ||
2364 | return null; | 2476 | return null; |
2365 | 2477 | ||
2366 | if (!Permissions.CanRezObject(group.PrimCount, item.OwnerID, pos)) | 2478 | int totalPrims = 0; |
2479 | foreach (SceneObjectGroup group in objlist) | ||
2480 | totalPrims += group.PrimCount; | ||
2481 | |||
2482 | if (!Permissions.CanRezObject(totalPrims, item.OwnerID, pos)) | ||
2367 | return null; | 2483 | return null; |
2368 | 2484 | ||
2369 | if (!Permissions.BypassPermissions()) | 2485 | if (!Permissions.BypassPermissions()) |
@@ -2372,16 +2488,28 @@ namespace OpenSim.Region.Framework.Scenes | |||
2372 | sourcePart.Inventory.RemoveInventoryItem(item.ItemID); | 2488 | sourcePart.Inventory.RemoveInventoryItem(item.ItemID); |
2373 | } | 2489 | } |
2374 | 2490 | ||
2375 | group.FromPartID = sourcePart.UUID; | 2491 | for (int i = 0; i < objlist.Count; i++) |
2376 | AddNewSceneObject(group, true, pos, rot, vel); | 2492 | { |
2377 | 2493 | SceneObjectGroup group = objlist[i]; | |
2378 | // We can only call this after adding the scene object, since the scene object references the scene | 2494 | Vector3 curpos = pos + veclist[i]; |
2379 | // to find out if scripts should be activated at all. | 2495 | |
2380 | group.CreateScriptInstances(param, true, DefaultScriptEngine, 3); | 2496 | if (group.IsAttachment == false && group.RootPart.Shape.State != 0) |
2381 | 2497 | { | |
2382 | group.ScheduleGroupForFullUpdate(); | 2498 | group.RootPart.AttachedPos = group.AbsolutePosition; |
2383 | 2499 | group.RootPart.Shape.LastAttachPoint = (byte)group.AttachmentPoint; | |
2384 | return group; | 2500 | } |
2501 | |||
2502 | group.FromPartID = sourcePart.UUID; | ||
2503 | AddNewSceneObject(group, true, curpos, rot, vel); | ||
2504 | |||
2505 | // We can only call this after adding the scene object, since the scene object references the scene | ||
2506 | // to find out if scripts should be activated at all. | ||
2507 | group.CreateScriptInstances(param, true, DefaultScriptEngine, 3); | ||
2508 | |||
2509 | group.ScheduleGroupForFullUpdate(); | ||
2510 | } | ||
2511 | |||
2512 | return objlist; | ||
2385 | } | 2513 | } |
2386 | 2514 | ||
2387 | public virtual bool returnObjects(SceneObjectGroup[] returnobjects, | 2515 | public virtual bool returnObjects(SceneObjectGroup[] returnobjects, |
@@ -2576,12 +2704,17 @@ namespace OpenSim.Region.Framework.Scenes | |||
2576 | return; | 2704 | return; |
2577 | } | 2705 | } |
2578 | 2706 | ||
2707 | bool oldUsePhysics = (root.Flags & PrimFlags.Physics) != 0; | ||
2579 | m_sceneGraph.LinkObjects(root, children); | 2708 | m_sceneGraph.LinkObjects(root, children); |
2580 | 2709 | ||
2581 | ScenePresence sp; | 2710 | ScenePresence sp; |
2582 | if (TryGetScenePresence(agentId, out sp)) | 2711 | if (TryGetScenePresence(agentId, out sp)) |
2583 | { | 2712 | { |
2584 | root.SendPropertiesToClient(sp.ControllingClient); | 2713 | root.SendPropertiesToClient(sp.ControllingClient); |
2714 | if (oldUsePhysics && (root.Flags & PrimFlags.Physics) == 0) | ||
2715 | { | ||
2716 | sp.ControllingClient.SendAlertMessage("Object physics canceled"); | ||
2717 | } | ||
2585 | } | 2718 | } |
2586 | } | 2719 | } |
2587 | 2720 | ||