Merge branch 'master' of /home/opensim/var/repo/opensim
						commit
						070d77d2f4
					
				|  | @ -1222,6 +1222,15 @@ namespace OpenSim.Region.Framework.Scenes | |||
| 
 | ||||
|             m_sceneGraph.Close(); | ||||
| 
 | ||||
|             if (PhysicsScene != null) | ||||
|             { | ||||
|                 PhysicsScene phys = PhysicsScene; | ||||
|                 // remove the physics engine from both Scene and SceneGraph | ||||
|                 PhysicsScene = null; | ||||
|                 phys.Dispose(); | ||||
|                 phys = null; | ||||
|             } | ||||
| 
 | ||||
|             if (!GridService.DeregisterRegion(RegionInfo.RegionID)) | ||||
|                 m_log.WarnFormat("[SCENE]: Deregister from grid failed for region {0}", Name); | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,308 @@ | |||
| /* | ||||
|  * 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 OpenSimulator Project nor the | ||||
|  *       names of its contributors may be used to endorse or promote products | ||||
|  *       derived from this software without specific prior written permission. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||||
|  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
|  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
|  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||||
|  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||
|  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||
|  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||||
|  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
|  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  */ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
| 
 | ||||
| using OMV = OpenMetaverse; | ||||
| 
 | ||||
| namespace OpenSim.Region.Physics.BulletSPlugin | ||||
| { | ||||
| public class BSLinkset | ||||
| { | ||||
|     private static string LogHeader = "[BULLETSIM LINKSET]"; | ||||
| 
 | ||||
|     private BSPrim m_linksetRoot; | ||||
|     public BSPrim Root { get { return m_linksetRoot; } } | ||||
| 
 | ||||
|     private BSScene m_scene; | ||||
| 
 | ||||
|     private List<BSPrim> m_children; | ||||
| 
 | ||||
|     // We lock the diddling of linkset classes to prevent any badness. | ||||
|     // This locks the modification of the instances of this class. Changes | ||||
|     //    to the physical representation is done via the tainting mechenism. | ||||
|     private object m_linksetActivityLock = new Object(); | ||||
| 
 | ||||
|     // We keep the prim's mass in the linkset structure since it could be dependent on other prims | ||||
|     private float m_mass; | ||||
|     public float LinksetMass  | ||||
|     {  | ||||
|         get  | ||||
|         { | ||||
|             m_mass = ComputeLinksetMass(); | ||||
|             return m_mass; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public OMV.Vector3 CenterOfMass | ||||
|     { | ||||
|         get { return ComputeLinksetCenterOfMass(); } | ||||
|     } | ||||
| 
 | ||||
|     public OMV.Vector3 GeometricCenter | ||||
|     { | ||||
|         get { return ComputeLinksetGeometricCenter(); } | ||||
|     } | ||||
| 
 | ||||
|     public BSLinkset(BSScene scene, BSPrim parent) | ||||
|     { | ||||
|         // A simple linkset of one (no children) | ||||
|         m_scene = scene; | ||||
|         m_linksetRoot = parent; | ||||
|         m_children = new List<BSPrim>(); | ||||
|         m_mass = parent.MassRaw; | ||||
|     } | ||||
| 
 | ||||
|     // Link to a linkset where the child knows the parent. | ||||
|     // Parent changing should not happen so do some sanity checking. | ||||
|     // We return the parent's linkset so the child can track it's membership. | ||||
|     public BSLinkset AddMeToLinkset(BSPrim child, BSPrim parent) | ||||
|     { | ||||
|         lock (m_linksetActivityLock) | ||||
|         { | ||||
|             parent.Linkset.AddChildToLinkset(child); | ||||
|         } | ||||
|         return parent.Linkset; | ||||
|     } | ||||
| 
 | ||||
|     public BSLinkset RemoveMeFromLinkset(BSPrim child) | ||||
|     { | ||||
|         lock (m_linksetActivityLock) | ||||
|         { | ||||
|             if (IsRoot(child)) | ||||
|             { | ||||
|                 // if root of linkset, take the linkset apart | ||||
|                 while (m_children.Count > 0) | ||||
|                 { | ||||
|                     // Note that we don't do a foreach because the remove routine | ||||
|                     //    takes it out of the list. | ||||
|                     RemoveChildFromLinkset(m_children[0]); | ||||
|                 } | ||||
|                 m_children.Clear(); // just to make sure | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 // Just removing a child from an existing linkset | ||||
|                 RemoveChildFromLinkset(child); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // The child is down to a linkset of just itself | ||||
|         return new BSLinkset(m_scene, child); | ||||
|     } | ||||
| 
 | ||||
|     // An existing linkset had one of its members rebuilt or something. | ||||
|     // Undo all the physical linking and rebuild the physical linkset. | ||||
|     public bool RefreshLinkset(BSPrim requestor) | ||||
|     { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Return 'true' if the passed object is the root object of this linkset | ||||
|     public bool IsRoot(BSPrim requestor) | ||||
|     { | ||||
|         return (requestor.LocalID == m_linksetRoot.LocalID); | ||||
|     } | ||||
| 
 | ||||
|     // Return 'true' if this linkset has any children (more than the root member) | ||||
|     public bool HasAnyChildren { get { return (m_children.Count > 0); } } | ||||
| 
 | ||||
|     // Return 'true' if this child is in this linkset | ||||
|     public bool HasChild(BSPrim child) | ||||
|     { | ||||
|         bool ret = false; | ||||
|         foreach (BSPrim bp in m_children) | ||||
|         { | ||||
|             if (child.LocalID == bp.LocalID) | ||||
|             { | ||||
|                 ret = true; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     private float ComputeLinksetMass() | ||||
|     { | ||||
|         float mass = m_linksetRoot.MassRaw; | ||||
|         foreach (BSPrim bp in m_children) | ||||
|         { | ||||
|             mass += bp.MassRaw; | ||||
|         } | ||||
|         return mass; | ||||
|     } | ||||
| 
 | ||||
|     private OMV.Vector3 ComputeLinksetCenterOfMass() | ||||
|     { | ||||
|         OMV.Vector3 com = m_linksetRoot.Position * m_linksetRoot.MassRaw; | ||||
|         float totalMass = m_linksetRoot.MassRaw; | ||||
| 
 | ||||
|         foreach (BSPrim bp in m_children) | ||||
|         { | ||||
|             com += bp.Position * bp.MassRaw; | ||||
|             totalMass += bp.MassRaw; | ||||
|         } | ||||
|         com /= totalMass; | ||||
| 
 | ||||
|         return com; | ||||
|     } | ||||
| 
 | ||||
|     private OMV.Vector3 ComputeLinksetGeometricCenter() | ||||
|     { | ||||
|         OMV.Vector3 com = m_linksetRoot.Position; | ||||
| 
 | ||||
|         foreach (BSPrim bp in m_children) | ||||
|         { | ||||
|             com += bp.Position * bp.MassRaw; | ||||
|         } | ||||
|         com /= m_children.Count + 1; | ||||
| 
 | ||||
|         return com; | ||||
|     } | ||||
| 
 | ||||
|     // I am the root of a linkset and a new child is being added | ||||
|     public void AddChildToLinkset(BSPrim pchild) | ||||
|     { | ||||
|         BSPrim child = pchild; | ||||
|         if (!HasChild(child)) | ||||
|         { | ||||
|             m_children.Add(child); | ||||
| 
 | ||||
|             m_scene.TaintedObject(delegate() | ||||
|             { | ||||
|                 DebugLog("{0}: AddChildToLinkset: adding child {1} to {2}", LogHeader, child.LocalID, m_linksetRoot.LocalID); | ||||
|                 DetailLog("{0},AddChildToLinkset,child={1}", m_linksetRoot.LocalID, pchild.LocalID); | ||||
|                 PhysicallyLinkAChildToRoot(pchild);     // build the physical binding between me and the child | ||||
|             }); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // I am the root of a linkset and one of my children is being removed. | ||||
|     // Safe to call even if the child is not really in my linkset. | ||||
|     public void RemoveChildFromLinkset(BSPrim pchild) | ||||
|     { | ||||
|         BSPrim child = pchild; | ||||
| 
 | ||||
|         if (m_children.Remove(child)) | ||||
|         { | ||||
|             m_scene.TaintedObject(delegate() | ||||
|             { | ||||
|                 DebugLog("{0}: RemoveChildFromLinkset: Removing constraint to {1}", LogHeader, child.LocalID); | ||||
|                 DetailLog("{0},RemoveChildFromLinkset,child={1}", m_linksetRoot.LocalID, pchild.LocalID); | ||||
| 
 | ||||
|                 if (m_children.Count == 0) | ||||
|                 { | ||||
|                     // if the linkset is empty, make sure all linkages have been removed | ||||
|                     PhysicallyUnlinkAllChildrenFromRoot(); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     PhysicallyUnlinkAChildFromRoot(pchild); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // This will happen if we remove the root of the linkset first. Non-fatal occurance. | ||||
|             // m_scene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // Create a constraint between me (root of linkset) and the passed prim (the child). | ||||
|     // Called at taint time! | ||||
|     private void PhysicallyLinkAChildToRoot(BSPrim childPrim) | ||||
|     { | ||||
|         // Zero motion for children so they don't interpolate | ||||
|         childPrim.ZeroMotion(); | ||||
| 
 | ||||
|         // relative position normalized to the root prim | ||||
|         OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(m_linksetRoot.Orientation); | ||||
|         OMV.Vector3 childRelativePosition = (childPrim.Position - m_linksetRoot.Position) * invThisOrientation; | ||||
| 
 | ||||
|         // relative rotation of the child to the parent | ||||
|         OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation; | ||||
| 
 | ||||
|         // create a constraint that allows no freedom of movement between the two objects | ||||
|         // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 | ||||
|         // DebugLog("{0}: CreateLinkset: Adding a constraint between root prim {1} and child prim {2}", LogHeader, LocalID, childPrim.LocalID); | ||||
|         DetailLog("{0},LinkAChildToMe,taint,root={1},child={2}", m_linksetRoot.LocalID, m_linksetRoot.LocalID, childPrim.LocalID); | ||||
|         BSConstraint constrain = m_scene.Constraints.CreateConstraint( | ||||
|                         m_scene.World, m_linksetRoot.Body, childPrim.Body, | ||||
|                         childRelativePosition, | ||||
|                         childRelativeRotation, | ||||
|                         OMV.Vector3.Zero, | ||||
|                         OMV.Quaternion.Identity); | ||||
|         constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); | ||||
|         constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); | ||||
| 
 | ||||
|         // tweek the constraint to increase stability | ||||
|         constrain.UseFrameOffset(m_scene.BoolNumeric(m_scene.Params.linkConstraintUseFrameOffset)); | ||||
|         constrain.TranslationalLimitMotor(m_scene.BoolNumeric(m_scene.Params.linkConstraintEnableTransMotor), | ||||
|                         m_scene.Params.linkConstraintTransMotorMaxVel, | ||||
|                         m_scene.Params.linkConstraintTransMotorMaxForce); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     // Remove linkage between myself and a particular child | ||||
|     // Called at taint time! | ||||
|     private void PhysicallyUnlinkAChildFromRoot(BSPrim childPrim) | ||||
|     { | ||||
|         DebugLog("{0}: PhysicallyUnlinkAChildFromRoot: RemoveConstraint between root prim {1} and child prim {2}",  | ||||
|                     LogHeader, m_linksetRoot.LocalID, childPrim.LocalID); | ||||
|         DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},child={2}", m_linksetRoot.LocalID, m_linksetRoot.LocalID, childPrim.LocalID); | ||||
|         // BulletSimAPI.RemoveConstraint(_scene.WorldID, LocalID, childPrim.LocalID); | ||||
|         m_scene.Constraints.RemoveAndDestroyConstraint(m_linksetRoot.Body, childPrim.Body); | ||||
|     } | ||||
| 
 | ||||
|     // Remove linkage between myself and any possible children I might have | ||||
|     // Called at taint time! | ||||
|     private void PhysicallyUnlinkAllChildrenFromRoot() | ||||
|     { | ||||
|         // DebugLog("{0}: PhysicallyUnlinkAllChildren:", LogHeader); | ||||
|         DetailLog("{0},PhysicallyUnlinkAllChildren,taint", m_linksetRoot.LocalID); | ||||
|         m_scene.Constraints.RemoveAndDestroyConstraint(m_linksetRoot.Body); | ||||
|         // BulletSimAPI.RemoveConstraintByID(_scene.WorldID, LocalID); | ||||
|     } | ||||
| 
 | ||||
|     // Invoke the detailed logger and output something if it's enabled. | ||||
|     private void DebugLog(string msg, params Object[] args) | ||||
|     { | ||||
|         m_scene.Logger.DebugFormat(msg, args); | ||||
|     } | ||||
| 
 | ||||
|     // Invoke the detailed logger and output something if it's enabled. | ||||
|     private void DetailLog(string msg, params Object[] args) | ||||
|     { | ||||
|         m_scene.PhysicsLogging.Write(msg, args); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| } | ||||
|  | @ -66,7 +66,7 @@ public sealed class BSPrim : PhysicsActor | |||
|     private bool _isSelected; | ||||
|     private bool _isVolumeDetect; | ||||
|     private OMV.Vector3 _position; | ||||
|     private float _mass; | ||||
|     private float _mass;    // the mass of this object | ||||
|     private float _density; | ||||
|     private OMV.Vector3 _force; | ||||
|     private OMV.Vector3 _velocity; | ||||
|  | @ -89,8 +89,13 @@ public sealed class BSPrim : PhysicsActor | |||
|     private bool _kinematic; | ||||
|     private float _buoyancy; | ||||
| 
 | ||||
|     private BSPrim _parentPrim; | ||||
|     private List<BSPrim> _childrenPrims; | ||||
|     // Membership in a linkset is controlled by this class. | ||||
|     private BSLinkset _linkset; | ||||
|     public BSLinkset Linkset | ||||
|     { | ||||
|         get { return _linkset; } | ||||
|         set { _linkset = value; } | ||||
|     } | ||||
| 
 | ||||
|     private int _subscribedEventsMs = 0; | ||||
|     private int _nextCollisionOkTime = 0; | ||||
|  | @ -133,9 +138,8 @@ public sealed class BSPrim : PhysicsActor | |||
|         _friction = _scene.Params.defaultFriction; // TODO: compute based on object material | ||||
|         _density = _scene.Params.defaultDensity; // TODO: compute based on object material | ||||
|         _restitution = _scene.Params.defaultRestitution; | ||||
|         _parentPrim = null;     // not a child or a parent | ||||
|         _linkset = new BSLinkset(_scene, this);     // a linkset of one | ||||
|         _vehicle = new BSDynamics(this);    // add vehicleness | ||||
|         _childrenPrims = new List<BSPrim>(); | ||||
|         _mass = CalculateMass(); | ||||
|         // do the actual object creation at taint time | ||||
|         _scene.TaintedObject(delegate() | ||||
|  | @ -161,16 +165,8 @@ public sealed class BSPrim : PhysicsActor | |||
| 
 | ||||
|         _scene.TaintedObject(delegate() | ||||
|         { | ||||
|             // undo any dependance with/on other objects | ||||
|             if (_parentPrim != null) | ||||
|             { | ||||
|                 // If I'm someone's child, tell them to forget about me. | ||||
|                 _parentPrim.RemoveChildFromLinkset(this); | ||||
|                 _parentPrim = null; | ||||
|             } | ||||
| 
 | ||||
|             // make sure there are no other prims linked to me | ||||
|             UnlinkAllChildren(); | ||||
|             // Undo any links between me and any other object | ||||
|             _linkset = _linkset.RemoveMeFromLinkset(this); | ||||
| 
 | ||||
|             // everything in the C# world will get garbage collected. Tell the C++ world to free stuff. | ||||
|             BulletSimAPI.DestroyObject(_scene.WorldID, LocalID); | ||||
|  | @ -187,7 +183,7 @@ public sealed class BSPrim : PhysicsActor | |||
|             _scene.TaintedObject(delegate() | ||||
|             { | ||||
|                 _mass = CalculateMass();   // changing size changes the mass | ||||
|                 BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, Mass, IsPhysical); | ||||
|                 BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, (IsPhysical ? _mass : 0f), IsPhysical); | ||||
|                 RecreateGeomAndObject(); | ||||
|             }); | ||||
|         }  | ||||
|  | @ -226,32 +222,8 @@ public sealed class BSPrim : PhysicsActor | |||
|         BSPrim parent = obj as BSPrim; | ||||
|         DebugLog("{0}: link {1}/{2} to {3}", LogHeader, _avName, _localID, obj.LocalID); | ||||
|         DetailLog("{0},link,parent={1}", LocalID, obj.LocalID); | ||||
|         // TODO: decide if this parent checking needs to happen at taint time | ||||
|         if (_parentPrim == null) | ||||
|         { | ||||
|             if (parent != null) | ||||
|             { | ||||
|                 // I don't have a parent so I am joining a linkset | ||||
|                 parent.AddChildToLinkset(this); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // I already have a parent, is parenting changing? | ||||
|             if (parent != _parentPrim) | ||||
|             { | ||||
|                 if (parent == null) | ||||
|                 { | ||||
|                     // we are being removed from a linkset | ||||
|                     _parentPrim.RemoveChildFromLinkset(this); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // asking to reparent a prim should not happen | ||||
|                     m_log.ErrorFormat("{0}: link(): Reparenting a prim. ", LogHeader); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         _linkset = _linkset.AddMeToLinkset(this, parent); | ||||
|         return;  | ||||
|     } | ||||
| 
 | ||||
|  | @ -260,81 +232,18 @@ public sealed class BSPrim : PhysicsActor | |||
|         // TODO: decide if this parent checking needs to happen at taint time | ||||
|         // Race condition here: if link() and delink() in same simulation tick, the delink will not happen | ||||
|         DebugLog("{0}: delink {1}/{2}. Parent={3}", LogHeader, _avName, _localID,  | ||||
|                 (_parentPrim==null ? "NULL" : _parentPrim._avName+"/"+_parentPrim.LocalID.ToString())); | ||||
|         DetailLog("{0},delink,parent={1}", LocalID, (_parentPrim==null ? "NULL" : _parentPrim.LocalID.ToString())); | ||||
|         if (_parentPrim != null) | ||||
|         { | ||||
|             _parentPrim.RemoveChildFromLinkset(this); | ||||
|         } | ||||
|                             _linkset.Root._avName+"/"+_linkset.Root.LocalID.ToString()); | ||||
|         DetailLog("{0},delink,parent={1}", LocalID, _linkset.Root.LocalID.ToString()); | ||||
| 
 | ||||
|         _linkset.RemoveMeFromLinkset(this); | ||||
|         return;  | ||||
|     } | ||||
| 
 | ||||
|     // I am the root of a linkset and a new child is being added | ||||
|     public void AddChildToLinkset(BSPrim pchild) | ||||
|     { | ||||
|         BSPrim child = pchild; | ||||
|         _scene.TaintedObject(delegate() | ||||
|         { | ||||
|             if (!_childrenPrims.Contains(child)) | ||||
|             { | ||||
|                 DebugLog("{0}: AddChildToLinkset: adding child {1} to {2}", LogHeader, child.LocalID, this.LocalID); | ||||
|                 DetailLog("{0},AddChildToLinkset,child={1}", LocalID, pchild.LocalID); | ||||
|                 _childrenPrims.Add(child); | ||||
|                 child._parentPrim = this;    // the child has gained a parent | ||||
|                 // RecreateGeomAndObject();    // rebuild my shape with the new child added | ||||
|                 LinkAChildToMe(pchild);     // build the physical binding between me and the child | ||||
| 
 | ||||
|                 _mass = CalculateMass(); | ||||
|             } | ||||
|         }); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // I am the root of a linkset and one of my children is being removed. | ||||
|     // Safe to call even if the child is not really in my linkset. | ||||
|     public void RemoveChildFromLinkset(BSPrim pchild) | ||||
|     { | ||||
|         BSPrim child = pchild; | ||||
|         _scene.TaintedObject(delegate() | ||||
|         { | ||||
|             if (_childrenPrims.Contains(child)) | ||||
|             { | ||||
|                 DebugLog("{0}: RemoveChildFromLinkset: Removing constraint to {1}", LogHeader, child.LocalID); | ||||
|                 DetailLog("{0},RemoveChildFromLinkset,child={1}", LocalID, pchild.LocalID); | ||||
|                 _childrenPrims.Remove(child); | ||||
|                 child._parentPrim = null;    // the child has lost its parent | ||||
|                 if (_childrenPrims.Count == 0) | ||||
|                 { | ||||
|                     // if the linkset is empty, make sure all linkages have been removed | ||||
|                     UnlinkAllChildren(); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // RecreateGeomAndObject();    // rebuild my shape with the child removed | ||||
|                     UnlinkAChildFromMe(pchild); | ||||
|                 } | ||||
| 
 | ||||
|                 _mass = CalculateMass(); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 m_log.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset"); | ||||
|             } | ||||
|         }); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // return true if we are the root of a linkset (there are children to manage) | ||||
|     public bool IsRootOfLinkset | ||||
|     { | ||||
|         get { return (_parentPrim == null && _childrenPrims.Count != 0); } | ||||
|     } | ||||
| 
 | ||||
|     // Set motion values to zero. | ||||
|     // Do it to the properties so the values get set in the physics engine. | ||||
|     // Push the setting of the values to the viewer. | ||||
|     // Called at taint time! | ||||
|     private void ZeroMotion() | ||||
|     public void ZeroMotion() | ||||
|     { | ||||
|         _velocity = OMV.Vector3.Zero; | ||||
|         _acceleration = OMV.Vector3.Zero; | ||||
|  | @ -355,9 +264,10 @@ public sealed class BSPrim : PhysicsActor | |||
| 
 | ||||
|     public override OMV.Vector3 Position {  | ||||
|         get {  | ||||
|             // child prims move around based on their parent. Need to get the latest location | ||||
|             if (_parentPrim != null) | ||||
|             if (!_linkset.IsRoot(this)) | ||||
|                 // child prims move around based on their parent. Need to get the latest location | ||||
|                 _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); | ||||
| 
 | ||||
|             // don't do the GetObjectPosition for root elements because this function is called a zillion times | ||||
|             // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); | ||||
|             return _position;  | ||||
|  | @ -373,16 +283,31 @@ public sealed class BSPrim : PhysicsActor | |||
|         }  | ||||
|     } | ||||
| 
 | ||||
|     // Return the effective mass of the object. Non-physical objects do not have mass. | ||||
|     public override float Mass {  | ||||
|         get { | ||||
|             if (IsPhysical) | ||||
|                 return _mass; | ||||
|             else | ||||
|                 return 0f; | ||||
|     // Return the effective mass of the object. | ||||
|     // If there are multiple items in the linkset, add them together for the root | ||||
|     public override float Mass | ||||
|     {  | ||||
|         get | ||||
|         { | ||||
|             return _linkset.LinksetMass; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // used when we only want this prim's mass and not the linkset thing | ||||
|     public float MassRaw { get { return _mass; } } | ||||
| 
 | ||||
|     // Is this used? | ||||
|     public override OMV.Vector3 CenterOfMass | ||||
|     { | ||||
|         get { return _linkset.CenterOfMass; } | ||||
|     } | ||||
| 
 | ||||
|     // Is this used? | ||||
|     public override OMV.Vector3 GeometricCenter | ||||
|     { | ||||
|         get { return _linkset.GeometricCenter; } | ||||
|     } | ||||
| 
 | ||||
|     public override OMV.Vector3 Force {  | ||||
|         get { return _force; }  | ||||
|         set { | ||||
|  | @ -473,8 +398,6 @@ public sealed class BSPrim : PhysicsActor | |||
|         return;  | ||||
|     } | ||||
| 
 | ||||
|     public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } } | ||||
|     public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } } | ||||
|     public override OMV.Vector3 Velocity {  | ||||
|         get { return _velocity; }  | ||||
|         set { | ||||
|  | @ -503,9 +426,9 @@ public sealed class BSPrim : PhysicsActor | |||
|     } | ||||
|     public override OMV.Quaternion Orientation {  | ||||
|         get { | ||||
|             if (_parentPrim != null) | ||||
|             if (!_linkset.IsRoot(this)) | ||||
|             { | ||||
|                 // children move around because tied to parent. Get a fresh value. | ||||
|                 // Children move around because tied to parent. Get a fresh value. | ||||
|                 _orientation = BulletSimAPI.GetObjectOrientation(_scene.WorldID, LocalID); | ||||
|             } | ||||
|             return _orientation; | ||||
|  | @ -555,14 +478,16 @@ public sealed class BSPrim : PhysicsActor | |||
|     private void SetObjectDynamic() | ||||
|     { | ||||
|         // m_log.DebugFormat("{0}: ID={1}, SetObjectDynamic: IsStatic={2}, IsSolid={3}", LogHeader, _localID, IsStatic, IsSolid); | ||||
|         // non-physical things work best with a mass of zero | ||||
|         if (!IsStatic) | ||||
|         { | ||||
|             _mass = CalculateMass(); | ||||
|             RecreateGeomAndObject(); | ||||
|         } | ||||
|         DetailLog("{0},SetObjectDynamic,taint,static={1},solid={2},mass={3}", LocalID, IsStatic, IsSolid, Mass); | ||||
|         BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), Mass); | ||||
| 
 | ||||
|         RecreateGeomAndObject(); | ||||
| 
 | ||||
|         float mass = _mass; | ||||
|         // Bullet wants static objects have a mass of zero | ||||
|         if (IsStatic)  | ||||
|             mass = 0f; | ||||
| 
 | ||||
|         DetailLog("{0},SetObjectDynamic,taint,static={1},solid={2},mass={3}", LocalID, IsStatic, IsSolid, mass); | ||||
|         BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), mass); | ||||
|     } | ||||
| 
 | ||||
|     // prims don't fly | ||||
|  | @ -1004,6 +929,9 @@ public sealed class BSPrim : PhysicsActor | |||
| 
 | ||||
|         returnMass = _density * volume; | ||||
| 
 | ||||
|         /* | ||||
|          * This change means each object keeps its own mass and the Mass property | ||||
|          * will return the sum if we're part of a linkset. | ||||
|         if (IsRootOfLinkset) | ||||
|         { | ||||
|             foreach (BSPrim prim in _childrenPrims) | ||||
|  | @ -1011,6 +939,7 @@ public sealed class BSPrim : PhysicsActor | |||
|                 returnMass += prim.CalculateMass(); | ||||
|             } | ||||
|         } | ||||
|          */ | ||||
| 
 | ||||
|         if (returnMass <= 0) | ||||
|             returnMass = 0.0001f; | ||||
|  | @ -1026,9 +955,11 @@ public sealed class BSPrim : PhysicsActor | |||
|     // The objects needs a hull if it's physical otherwise a mesh is enough | ||||
|     // No locking here because this is done when we know physics is not simulating | ||||
|     // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used | ||||
|     private void CreateGeom(bool forceRebuild) | ||||
|     // Returns 'true' if the geometry was rebuilt | ||||
|     private bool CreateGeom(bool forceRebuild) | ||||
|     { | ||||
|         // the mesher thought this was too simple to mesh. Use a native Bullet collision shape. | ||||
|         bool ret = false; | ||||
|         if (!_scene.NeedsMeshing(_pbs)) | ||||
|         { | ||||
|             if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1) | ||||
|  | @ -1036,18 +967,26 @@ public sealed class BSPrim : PhysicsActor | |||
|                 if (_size.X == _size.Y && _size.Y == _size.Z && _size.X == _size.Z) | ||||
|                 { | ||||
|                     // m_log.DebugFormat("{0}: CreateGeom: Defaulting to sphere of size {1}", LogHeader, _size); | ||||
|                     _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE; | ||||
|                     DetailLog("{0},CreateGeom,sphere", LocalID); | ||||
|                     // Bullet native objects are scaled by the Bullet engine so pass the size in | ||||
|                     _scale = _size; | ||||
|                     if (_shapeType != ShapeData.PhysicsShapeType.SHAPE_SPHERE) | ||||
|                     { | ||||
|                         DetailLog("{0},CreateGeom,sphere", LocalID); | ||||
|                         _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE; | ||||
|                         ret = true; | ||||
|                         // Bullet native objects are scaled by the Bullet engine so pass the size in | ||||
|                         _scale = _size; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 // m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, size={2}", LogHeader, LocalID, _size); | ||||
|                 DetailLog("{0},CreateGeom,box", LocalID); | ||||
|                 _shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX; | ||||
|                 _scale = _size; | ||||
|                 if (_shapeType != ShapeData.PhysicsShapeType.SHAPE_BOX) | ||||
|                 { | ||||
|                     DetailLog("{0},CreateGeom,box", LocalID); | ||||
|                     _shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX; | ||||
|                     ret = true; | ||||
|                     _scale = _size; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|  | @ -1059,6 +998,7 @@ public sealed class BSPrim : PhysicsActor | |||
|                     // physical objects require a hull for interaction. | ||||
|                     // This will create the mesh if it doesn't already exist | ||||
|                     CreateGeomHull(); | ||||
|                     ret = true; | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|  | @ -1067,9 +1007,11 @@ public sealed class BSPrim : PhysicsActor | |||
|                 { | ||||
|                     // Static (non-physical) objects only need a mesh for bumping into | ||||
|                     CreateGeomMesh(); | ||||
|                     ret = true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     // No locking here because this is done when we know physics is not simulating | ||||
|  | @ -1254,20 +1196,18 @@ public sealed class BSPrim : PhysicsActor | |||
|     // No locking here because this is done when the physics engine is not simulating | ||||
|     private void CreateObject() | ||||
|     { | ||||
|         if (IsRootOfLinkset) | ||||
|         { | ||||
|             // Create a linkset around this object | ||||
|             CreateLinkset(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // simple object | ||||
|             // the mesh or hull must have already been created in Bullet | ||||
|             ShapeData shape; | ||||
|             FillShapeInfo(out shape); | ||||
|             // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type); | ||||
|             BulletSimAPI.CreateObject(_scene.WorldID, shape); | ||||
|         } | ||||
|         // this routine is called when objects are rebuilt.  | ||||
| 
 | ||||
|         // the mesh or hull must have already been created in Bullet | ||||
|         ShapeData shape; | ||||
|         FillShapeInfo(out shape); | ||||
|         // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type); | ||||
|         BulletSimAPI.CreateObject(_scene.WorldID, shape); | ||||
|         // the CreateObject() may have recreated the rigid body. Make sure we have the latest. | ||||
|         m_body.Ptr = BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID); | ||||
| 
 | ||||
|         // The root object could have been recreated. Make sure everything linksety is up to date. | ||||
|         _linkset.RefreshLinkset(this); | ||||
|     } | ||||
| 
 | ||||
|     // Copy prim's info into the BulletSim shape description structure | ||||
|  | @ -1279,7 +1219,7 @@ public sealed class BSPrim : PhysicsActor | |||
|         shape.Rotation = _orientation; | ||||
|         shape.Velocity = _velocity; | ||||
|         shape.Scale = _scale; | ||||
|         shape.Mass = Mass; | ||||
|         shape.Mass = _isPhysical ? _mass : 0f; | ||||
|         shape.Buoyancy = _buoyancy; | ||||
|         shape.HullKey = _hullKey; | ||||
|         shape.MeshKey = _meshKey; | ||||
|  | @ -1289,83 +1229,6 @@ public sealed class BSPrim : PhysicsActor | |||
|         shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue; | ||||
|     } | ||||
| 
 | ||||
|     #region Linkset creation and destruction | ||||
| 
 | ||||
|     // Create the linkset by putting constraints between the objects of the set so they cannot move | ||||
|     // relative to each other. | ||||
|     void CreateLinkset() | ||||
|     { | ||||
|         // DebugLog("{0}: CreateLinkset. Root prim={1}, prims={2}", LogHeader, LocalID, _childrenPrims.Count+1); | ||||
| 
 | ||||
|         // remove any constraints that might be in place | ||||
|         UnlinkAllChildren(); | ||||
| 
 | ||||
|         // create constraints between the root prim and each of the children | ||||
|         foreach (BSPrim prim in _childrenPrims) | ||||
|         { | ||||
|             LinkAChildToMe(prim); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Create a constraint between me (root of linkset) and the passed prim (the child). | ||||
|     // Called at taint time! | ||||
|     private void LinkAChildToMe(BSPrim childPrim) | ||||
|     { | ||||
|         // Zero motion for children so they don't interpolate | ||||
|         childPrim.ZeroMotion(); | ||||
| 
 | ||||
|         // relative position normalized to the root prim | ||||
|         OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(this._orientation); | ||||
|         OMV.Vector3 childRelativePosition = (childPrim._position - this._position) * invThisOrientation; | ||||
| 
 | ||||
|         // relative rotation of the child to the parent | ||||
|         OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim._orientation; | ||||
| 
 | ||||
|         // create a constraint that allows no freedom of movement between the two objects | ||||
|         // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 | ||||
|         // DebugLog("{0}: CreateLinkset: Adding a constraint between root prim {1} and child prim {2}", LogHeader, LocalID, childPrim.LocalID); | ||||
|         DetailLog("{0},LinkAChildToMe,taint,root={1},child={2}", LocalID, LocalID, childPrim.LocalID); | ||||
|         BSConstraint constrain = _scene.Constraints.CreateConstraint( | ||||
|                         _scene.World, this.Body, childPrim.Body, | ||||
|                         childRelativePosition, | ||||
|                         childRelativeRotation, | ||||
|                         OMV.Vector3.Zero, | ||||
|                         OMV.Quaternion.Identity); | ||||
|         constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); | ||||
|         constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); | ||||
| 
 | ||||
|         // tweek the constraint to increase stability | ||||
|         constrain.UseFrameOffset(_scene.BoolNumeric(_scene.Params.linkConstraintUseFrameOffset)); | ||||
|         if (_scene.BoolNumeric(_scene.Params.linkConstraintEnableTransMotor)) | ||||
|         { | ||||
|             constrain.TranslationalLimitMotor(true,  | ||||
|                             _scene.Params.linkConstraintTransMotorMaxVel, | ||||
|                             _scene.Params.linkConstraintTransMotorMaxForce); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Remove linkage between myself and a particular child | ||||
|     // Called at taint time! | ||||
|     private void UnlinkAChildFromMe(BSPrim childPrim) | ||||
|     { | ||||
|         DebugLog("{0}: UnlinkAChildFromMe: RemoveConstraint between root prim {1} and child prim {2}",  | ||||
|                     LogHeader, LocalID, childPrim.LocalID); | ||||
|         DetailLog("{0},UnlinkAChildFromMe,taint,root={1},child={2}", LocalID, LocalID, childPrim.LocalID); | ||||
|         // BulletSimAPI.RemoveConstraint(_scene.WorldID, LocalID, childPrim.LocalID); | ||||
|         _scene.Constraints.RemoveAndDestroyConstraint(this.Body, childPrim.Body); | ||||
|     } | ||||
| 
 | ||||
|     // Remove linkage between myself and any possible children I might have | ||||
|     // Called at taint time! | ||||
|     private void UnlinkAllChildren() | ||||
|     { | ||||
|         DebugLog("{0}: UnlinkAllChildren:", LogHeader); | ||||
|         DetailLog("{0},UnlinkAllChildren,taint", LocalID); | ||||
|         _scene.Constraints.RemoveAndDestroyConstraint(this.Body); | ||||
|         // BulletSimAPI.RemoveConstraintByID(_scene.WorldID, LocalID); | ||||
|     } | ||||
| 
 | ||||
|     #endregion // Linkset creation and destruction | ||||
| 
 | ||||
|     // Rebuild the geometry and object. | ||||
|     // This is called when the shape changes so we need to recreate the mesh/hull. | ||||
|  | @ -1443,7 +1306,7 @@ public sealed class BSPrim : PhysicsActor | |||
|         // Don't check for damping here -- it's done in BulletSim and SceneObjectPart. | ||||
| 
 | ||||
|         // Updates only for individual prims and for the root object of a linkset. | ||||
|         if (_parentPrim == null) | ||||
|         if (_linkset.IsRoot(this)) | ||||
|         { | ||||
|             // Assign to the local variables so the normal set action does not happen | ||||
|             _position = entprop.Position; | ||||
|  |  | |||
|  | @ -73,7 +73,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
|     private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||||
|     private static readonly string LogHeader = "[BULLETS SCENE]"; | ||||
| 
 | ||||
|     private void DebugLog(string mm, params Object[] xx) { if (shouldDebugLog) m_log.DebugFormat(mm, xx); } | ||||
|     public void DebugLog(string mm, params Object[] xx) { if (shouldDebugLog) m_log.DebugFormat(mm, xx); } | ||||
| 
 | ||||
|     public string BulletSimVersion = "?"; | ||||
| 
 | ||||
|  | @ -87,6 +87,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
|     private uint m_worldID; | ||||
|     public uint WorldID { get { return m_worldID; } } | ||||
| 
 | ||||
|     // let my minuions use my logger | ||||
|     public ILog Logger { get { return m_log; } } | ||||
| 
 | ||||
|     private bool m_initialized = false; | ||||
| 
 | ||||
|     private int m_detailedStatsStep = 0; | ||||
|  | @ -1026,7 +1029,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
|             (s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ), | ||||
| 
 | ||||
| 	    new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.", | ||||
|             ConfigurationParameters.numericTrue, | ||||
|             ConfigurationParameters.numericFalse, | ||||
|             (s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, | ||||
|             (s) => { return s.m_params[0].linkConstraintUseFrameOffset; }, | ||||
|             (s,p,l,v) => { s.m_params[0].linkConstraintUseFrameOffset = v; } ), | ||||
|  |  | |||
|  | @ -239,10 +239,10 @@ public static extern bool DestroyMesh(uint worldID, System.UInt64 meshKey); | |||
| [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||||
| public static extern bool CreateObject(uint worldID, ShapeData shapeData); | ||||
| 
 | ||||
| /*  Remove old functionality | ||||
| [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||||
| public static extern void CreateLinkset(uint worldID, int objectCount, ShapeData[] shapeDatas); | ||||
| 
 | ||||
| /*  Remove old functionality | ||||
| [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||||
| public static extern void AddConstraint(uint worldID, uint id1, uint id2,  | ||||
|                         Vector3 frame1, Quaternion frame1rot, | ||||
|  |  | |||
|  | @ -489,6 +489,8 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
|         /// </summary> | ||||
|         internal Object OdeLock = new Object(); | ||||
| 
 | ||||
|         private bool _worldInitialized = false; | ||||
| 
 | ||||
|         public IMesher mesher; | ||||
| 
 | ||||
|         private IConfigSource m_config; | ||||
|  | @ -875,6 +877,8 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
|                     staticPrimspace[i, j] = IntPtr.Zero; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             _worldInitialized = true; | ||||
|         } | ||||
| 
 | ||||
| //        internal void waitForSpaceUnlock(IntPtr space) | ||||
|  | @ -2896,6 +2900,8 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
|         /// <returns>The number of frames simulated over that period.</returns> | ||||
|         public override float Simulate(float timeStep) | ||||
|         { | ||||
|             if (!_worldInitialized) return 11f; | ||||
| 
 | ||||
|             int startFrameTick = CollectStats ? Util.EnvironmentTickCount() : 0; | ||||
|             int tempTick = 0, tempTick2 = 0; | ||||
| 
 | ||||
|  | @ -4017,6 +4023,8 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
| 
 | ||||
|         public override void Dispose() | ||||
|         { | ||||
|             _worldInitialized = false; | ||||
| 
 | ||||
|             m_rayCastManager.Dispose(); | ||||
|             m_rayCastManager = null; | ||||
| 
 | ||||
|  | @ -4037,6 +4045,7 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
|                 d.WorldDestroy(world); | ||||
|                 //d.CloseODE(); | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         public override Dictionary<uint, float> GetTopColliders() | ||||
|  |  | |||
|  | @ -99,6 +99,8 @@ namespace OpenSim.Region.RegionCombinerModule | |||
| 
 | ||||
|         public void RemoveRegion(Scene scene) | ||||
|         { | ||||
|             lock (m_startingScenes) | ||||
|                 m_startingScenes.Remove(scene.RegionInfo.originRegionID); | ||||
|         } | ||||
| 
 | ||||
|         public void RegionLoaded(Scene scene) | ||||
|  |  | |||
|  | @ -915,7 +915,7 @@ | |||
|     NumberOfSolverIterations = 0;    | ||||
| 
 | ||||
|     ; Linkset constraint parameters | ||||
|     LinkConstraintUseFrameOffset = True; | ||||
|     LinkConstraintUseFrameOffset = False; | ||||
|     LinkConstraintEnableTransMotor = True; | ||||
|     LinkConstraintTransMotorMaxVel = 5.0; | ||||
|     LinkConstraintTransMotorMaxForce = 0.1; | ||||
|  | @ -937,7 +937,7 @@ | |||
|     FixedTimeStep = .01667 | ||||
| 
 | ||||
|     MaxCollisionsPerFrame = 2048 | ||||
|     MaxUpdatesPerFrame = 2048 | ||||
|     MaxUpdatesPerFrame = 8192 | ||||
| 
 | ||||
| [RemoteAdmin] | ||||
|     enabled = false | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 BlueWall
						BlueWall