From 6cd0d7a62b696d28d488f3cb82838ccf973ccfd7 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Sat, 30 Nov 2013 15:28:39 -0800 Subject: varregion: Add MaxRegionSize constant and enforce in RegionInfo. Intermediate checkin of changing border cross computation from checking boundry limits to requests to GridService. Not totally functional. --- OpenSim/Framework/Constants.cs | 4 +- OpenSim/Framework/RegionInfo.cs | 11 +- .../EntityTransfer/EntityTransferModule.cs | 218 +++++++++++++++------ .../Grid/LocalGridServiceConnector.cs | 3 + OpenSim/Region/Framework/Scenes/Scene.cs | 17 ++ OpenSim/Region/Framework/Scenes/ScenePresence.cs | 48 ++++- 6 files changed, 235 insertions(+), 66 deletions(-) diff --git a/OpenSim/Framework/Constants.cs b/OpenSim/Framework/Constants.cs index d18b32e..d80808c 100644 --- a/OpenSim/Framework/Constants.cs +++ b/OpenSim/Framework/Constants.cs @@ -30,11 +30,13 @@ namespace OpenSim.Framework { public class Constants { - // 'RegionSize' captures the legacy region size. + // 'RegionSize' is the legacy region size. // DO NOT USE THIS FOR ANY NEW CODE. Use Scene.RegionSize[XYZ] as a region might not // be the legacy region size. public const uint RegionSize = 256; public const uint RegionHeight = 4096; + // This could be a parameters but, really, a region of greater than this is pretty unmanageable + public const uint MaximumRegionSize = 8192; // Since terrain is stored in 16x16 heights, regions must be a multiple of this number and that is the minimum public const int MinRegionSize = 16; diff --git a/OpenSim/Framework/RegionInfo.cs b/OpenSim/Framework/RegionInfo.cs index cf909cd..63b3d89 100644 --- a/OpenSim/Framework/RegionInfo.cs +++ b/OpenSim/Framework/RegionInfo.cs @@ -788,7 +788,16 @@ namespace OpenSim.Framework RegionSizeX = minSize; RegionSizeY = minSize; m_log.ErrorFormat("{0} Regions must be square until viewers are updated. Forcing region {1} size to <{2},{3}>", - LogHeader, m_regionName, RegionSizeX, RegionSizeY); + LogHeader, m_regionName, RegionSizeX, RegionSizeY); + } + + // There is a practical limit to region size. + if (RegionSizeX > Constants.MaximumRegionSize || RegionSizeY > Constants.MaximumRegionSize) + { + RegionSizeX = Util.Clamp(RegionSizeX, Constants.RegionSize, Constants.MaximumRegionSize); + RegionSizeY = Util.Clamp(RegionSizeY, Constants.RegionSize, Constants.MaximumRegionSize); + m_log.ErrorFormat("{0} Region dimensions must be less than {1}. Clamping {2}'s size to <{3},{4}>", + LogHeader, Constants.MaximumRegionSize, m_regionName, RegionSizeX, RegionSizeY); } m_log.InfoFormat("{0} Region {1} size set to <{2},{3}>", LogHeader, m_regionName, RegionSizeX, RegionSizeY); diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index eb1b271..c3d0765 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -52,6 +52,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer public class EntityTransferModule : INonSharedRegionModule, IEntityTransferModule { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[ENTITY TRANSFER MODULE]"; public const int DefaultMaxTransferDistance = 4095; public const bool WaitForAgentArrivedAtDestinationDefault = true; @@ -433,10 +434,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer float posZLimit = 22; // TODO: Check other Scene HeightField - if (position.X > 0 && position.X <= (int)Constants.RegionSize && position.Y > 0 && position.Y <= (int)Constants.RegionSize) - { - posZLimit = (float)sp.Scene.Heightmap[(int)position.X, (int)position.Y]; - } + posZLimit = (float)sp.Scene.Heightmap[(int)position.X, (int)position.Y]; float newPosZ = posZLimit + localAVHeight; if (posZLimit >= (position.Z - (localAVHeight / 2)) && !(Single.IsInfinity(newPosZ) || Single.IsNaN(newPosZ))) @@ -485,9 +483,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer if (finalDestination == null) { - m_log.WarnFormat( - "[ENTITY TRANSFER MODULE]: Final destination is having problems. Unable to teleport {0} {1}", - sp.Name, sp.UUID); + m_log.WarnFormat( "{0} Final destination is having problems. Unable to teleport {1} {2}", + LogHeader, sp.Name, sp.UUID); sp.ControllingClient.SendTeleportFailed("Problem at destination"); return; @@ -528,11 +525,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // and set the map-tile to '(Offline)' uint regX, regY; - Utils.LongToUInts(regionHandle, out regX, out regY); + Util.RegionHandleToRegionLoc(regionHandle, out regX, out regY); MapBlockData block = new MapBlockData(); - block.X = (ushort)(regX / Constants.RegionSize); - block.Y = (ushort)(regY / Constants.RegionSize); + block.X = (ushort)Util.WorldToRegionLoc(regX); + block.Y = (ushort)Util.WorldToRegionLoc(regY); block.Access = 254; // == not there List blocks = new List(); @@ -1372,6 +1369,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // m_log.DebugFormat( // "[ENTITY TRANSFER MODULE]: Crossing agent {0} at pos {1} in {2}", agent.Name, pos, scene.Name); + /* Vector3 newpos = new Vector3(pos.X, pos.Y, pos.Z); uint neighbourx = scene.RegionInfo.LegacyRegionLocX; @@ -1506,6 +1504,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer neighboury += (uint)(int)(b.BorderLine.Z / (int)Constants.RegionSize); newpos.Y = enterDistance; } + */ /* @@ -1532,52 +1531,71 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer } */ - ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize)); - - int x = (int)(neighbourx * Constants.RegionSize), y = (int)(neighboury * Constants.RegionSize); + double presenceWorldX = (double)scene.RegionInfo.RegionLocX + pos.X; + double presenceWorldY = (double)scene.RegionInfo.RegionLocY + pos.Y; - ExpiringCache r; - DateTime banUntil; + // Call the grid service to lookup the region containing the new position. + GridRegion neighbourRegion = GetRegionContainingWorldLocation(scene.GridService, scene.RegionInfo.ScopeID, + presenceWorldX, presenceWorldY); - if (m_bannedRegions.TryGetValue(agent.ControllingClient.AgentId, out r)) - { - if (r.TryGetValue(neighbourHandle, out banUntil)) - { - if (DateTime.Now < banUntil) - return false; - r.Remove(neighbourHandle); - } - } - else + if (neighbourRegion != null) { - r = null; - } + Vector3 newRegionRelativeObjectPosition = new Vector3( + (float)(presenceWorldX - (double)neighbourRegion.RegionLocX), + (float)(presenceWorldY - (double)neighbourRegion.RegionLocY), + pos.Z); + agent.ControllingClient.SendAgentAlertMessage( + String.Format("Moving you to region {0},{1}", neighbourRegion.RegionCoordX, neighbourRegion.RegionCoordY), false); + InformClientToInitiateTeleportToLocation(agent, (uint)neighbourRegion.RegionCoordX, (uint)neighbourRegion.RegionCoordY, + newRegionRelativeObjectPosition, scene); - GridRegion neighbourRegion = scene.GridService.GetRegionByPosition(scene.RegionInfo.ScopeID, (int)x, (int)y); + ExpiringCache r; + DateTime banUntil; - string reason; - string version; - if (!scene.SimulationService.QueryAccess(neighbourRegion, agent.ControllingClient.AgentId, newpos, out version, out reason)) - { - agent.ControllingClient.SendAlertMessage("Cannot region cross into banned parcel"); - if (r == null) + if (m_bannedRegions.TryGetValue(agent.ControllingClient.AgentId, out r)) { - r = new ExpiringCache(); - r.Add(neighbourHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); - - m_bannedRegions.Add(agent.ControllingClient.AgentId, r, TimeSpan.FromSeconds(45)); + if (r.TryGetValue(neighbourRegion.RegionHandle, out banUntil)) + { + if (DateTime.Now < banUntil) + return false; + r.Remove(neighbourRegion.RegionHandle); + } } else { - r.Add(neighbourHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); + r = null; } - return false; - } - agent.IsInTransit = true; + string reason; + string version; + if (!scene.SimulationService.QueryAccess(neighbourRegion, agent.ControllingClient.AgentId, newRegionRelativeObjectPosition, out version, out reason)) + { + agent.ControllingClient.SendAlertMessage("Cannot region cross into banned parcel"); + if (r == null) + { + r = new ExpiringCache(); + r.Add(neighbourRegion.RegionHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); + + m_bannedRegions.Add(agent.ControllingClient.AgentId, r, TimeSpan.FromSeconds(45)); + } + else + { + r.Add(neighbourRegion.RegionHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); + } + return false; + } - CrossAgentToNewRegionDelegate d = CrossAgentToNewRegionAsync; - d.BeginInvoke(agent, newpos, neighbourx, neighboury, neighbourRegion, isFlying, version, CrossAgentToNewRegionCompleted, d); + agent.IsInTransit = true; + + CrossAgentToNewRegionDelegate d = CrossAgentToNewRegionAsync; + d.BeginInvoke(agent, newRegionRelativeObjectPosition, + (uint)neighbourRegion.RegionLocX, (uint)neighbourRegion.RegionLocY, + neighbourRegion, isFlying, version, CrossAgentToNewRegionCompleted, d); + } + else + { + m_log.ErrorFormat("{0} Cross(sp). Did not find target region. SP.AbsolutePosition={1}", LogHeader, pos); + } return true; } @@ -2055,8 +2073,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer } } + // Computes the difference between two region bases. + // Returns a vector of world coordinates (meters) from base of first region to the second. + // The first region is the home region of the passed scene presence. Vector3 CalculateOffset(ScenePresence sp, GridRegion neighbour) { + /* int rRegionX = (int)sp.Scene.RegionInfo.LegacyRegionLocX; int rRegionY = (int)sp.Scene.RegionInfo.LegacyRegionLocY; int tRegionX = neighbour.RegionLocX / (int)Constants.RegionSize; @@ -2064,6 +2086,67 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer int shiftx = (rRegionX - tRegionX) * (int)Constants.RegionSize; int shifty = (rRegionY - tRegionY) * (int)Constants.RegionSize; return new Vector3(shiftx, shifty, 0f); + */ + return new Vector3(sp.Scene.RegionInfo.RegionLocX - neighbour.RegionLocX, + sp.Scene.RegionInfo.RegionLocY - neighbour.RegionLocY, + 0f); + } + + // Given a world position (fractional meter coordinate), get the GridRegion info for + // the region containing that point. + // Return 'null' if no such region exists. + private GridRegion GetRegionContainingWorldLocation(IGridService pGridService, UUID pScopeID, double px, double py) + { + m_log.DebugFormat("{0} GetRegionContainingWorldLocation: call, XY=<{1},{2}>", LogHeader, px, py); + GridRegion ret = null; + + // As an optimization, since most regions will be legacy sized regions (256x256), first try to get + // the region at the appropriate legacy region location. + uint possibleX = (uint)Math.Floor(px); + possibleX -= possibleX % Constants.RegionSize; + uint possibleY = (uint)Math.Floor(py); + possibleY -= possibleY % Constants.RegionSize; + ret = pGridService.GetRegionByPosition(pScopeID, (int)possibleX, (int)possibleY); + if (ret != null) + { + m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Found region using legacy size. rloc=<{1},{2}>. Rname={3}", + LogHeader, possibleX, possibleY, ret.RegionName); + } + + if (ret == null) + { + // If the simple lookup failed, search the larger area for a region that contains this point + double range = (double)Constants.RegionSize + 2; + while (ret == null && range <= (Constants.MaximumRegionSize + Constants.RegionSize)) + { + // Get from the grid service a list of regions that might contain this point + List possibleRegions = pGridService.GetRegionRange(pScopeID, + (int)(px - range), (int)(px + range), + (int)(py - range), (int)(py + range)); + m_log.DebugFormat("{0} GetRegionContainingWorldLocation: possibleRegions cnt={1}, range={2}", + LogHeader, possibleRegions.Count, range); + if (possibleRegions != null && possibleRegions.Count > 0) + { + // If we found some regions, check to see if the point is within + foreach (GridRegion gr in possibleRegions) + { + m_log.DebugFormat("{0} GetRegionContainingWorldLocation: possibleRegion nm={1}, regionLoc=<{2},{3}>, regionSize=<{4},{5}>", + LogHeader, gr.RegionName, gr.RegionLocX, gr.RegionLocY, gr.RegionSizeX, gr.RegionSizeY); + if (px >= (double)gr.RegionLocX && px < (double)(gr.RegionLocX + gr.RegionSizeX) + && py >= (double)gr.RegionLocY && py < (double)(gr.RegionLocY + gr.RegionSizeY)) + { + // Found a region that contains the point + ret = gr; + m_log.DebugFormat("{0} GetRegionContainingWorldLocation: found. RegionName={1}", LogHeader, ret.RegionName); + break; + } + } + } + // Larger search area for next time around if not found + range *= 2; + } + } + return ret; } private void InformClientOfNeighbourCompleted(IAsyncResult iar) @@ -2272,10 +2355,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer /// Move the given scene object into a new region depending on which region its absolute position has moved /// into. /// - /// This method locates the new region handle and offsets the prim position for the new region + /// Using the objects new world location, ask the grid service for a the new region and adjust the prim + /// position to be relative to the new region. /// - /// the attempted out of region position of the scene object /// the scene object that we're crossing + /// the attempted out of region position of the scene object. This position is + /// relative to the region the object currently is in. + /// if 'true', the deletion of the client from the region is not broadcast to the clients public void Cross(SceneObjectGroup grp, Vector3 attemptedPosition, bool silent) { if (grp == null) @@ -2301,6 +2387,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer return; } + /* int thisx = (int)scene.RegionInfo.LegacyRegionLocX; int thisy = (int)scene.RegionInfo.LegacyRegionLocY; Vector3 EastCross = new Vector3(0.1f, 0, 0); @@ -2309,10 +2396,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer Vector3 SouthCross = new Vector3(0, -0.1f, 0); - // use this if no borders were crossed! - ulong newRegionHandle - = Util.UIntsToLong((uint)((thisx) * Constants.RegionSize), - (uint)((thisy) * Constants.RegionSize)); + // use this default if no borders were crossed (handle of the current region) + ulong newRegionHandle = Util.RegionWorldLocToHandle(scene.RegionInfo.RegionWorldLocX, scene.RegionInfo.RegionWorldLocY); Vector3 pos = attemptedPosition; @@ -2469,30 +2554,43 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer = Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy + changeY) * Constants.RegionSize)); // y + 1 } + */ - // Offset the positions for the new region across the border + // Remember the old group position in case the region lookup fails so position can be restored. Vector3 oldGroupPosition = grp.RootPart.GroupPosition; - // If we fail to cross the border, then reset the position of the scene object on that border. - uint x = 0, y = 0; - Utils.LongToUInts(newRegionHandle, out x, out y); - GridRegion destination = scene.GridService.GetRegionByPosition(scene.RegionInfo.ScopeID, (int)x, (int)y); + // Compute the absolute position of the object. + double objectWorldLocX = (double)scene.RegionInfo.RegionWorldLocX + attemptedPosition.X; + double objectWorldLocY = (double)scene.RegionInfo.RegionWorldLocY + attemptedPosition.Y; + + // Ask the grid service for the region that contains the passed address + GridRegion destination = GetRegionContainingWorldLocation(scene.GridService, scene.RegionInfo.ScopeID, objectWorldLocX, objectWorldLocY); + + Vector3 pos = Vector3.Zero; + if (destination != null) + { + // Adjust the object's relative position from the old region (attemptedPosition) + // to be relative to the new region (pos). + pos = new Vector3( (float)(objectWorldLocX - (double)destination.RegionLocX), + (float)(objectWorldLocY - (double)destination.RegionLocY), + attemptedPosition.Z); + } if (destination == null || !CrossPrimGroupIntoNewRegion(destination, pos, grp, silent)) { - m_log.InfoFormat("[ENTITY TRANSFER MODULE] cross region transfer failed for object {0}",grp.UUID); + m_log.InfoFormat("[ENTITY TRANSFER MODULE] cross region transfer failed for object {0}", grp.UUID); // We are going to move the object back to the old position so long as the old position // is in the region - oldGroupPosition.X = Util.Clamp(oldGroupPosition.X,1.0f,(float)Constants.RegionSize-1); - oldGroupPosition.Y = Util.Clamp(oldGroupPosition.Y,1.0f,(float)Constants.RegionSize-1); - oldGroupPosition.Z = Util.Clamp(oldGroupPosition.Z,1.0f,4096.0f); + oldGroupPosition.X = Util.Clamp(oldGroupPosition.X, 1.0f, (float)(scene.RegionInfo.RegionSizeX - 1)); + oldGroupPosition.Y = Util.Clamp(oldGroupPosition.Y, 1.0f, (float)(scene.RegionInfo.RegionSizeY - 1)); + oldGroupPosition.Z = Util.Clamp(oldGroupPosition.Z, 1.0f, Constants.RegionHeight); grp.RootPart.GroupPosition = oldGroupPosition; // Need to turn off the physics flags, otherwise the object will continue to attempt to // move out of the region creating an infinite loop of failed attempts to cross - grp.UpdatePrimFlags(grp.RootPart.LocalId,false,grp.IsTemporary,grp.IsPhantom,false); + grp.UpdatePrimFlags(grp.RootPart.LocalId, false, grp.IsTemporary, grp.IsPhantom, false); if (grp.RootPart.KeyframeMotion != null) grp.RootPart.KeyframeMotion.CrossingFailure(); diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs index 598351e..277293a 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs @@ -197,6 +197,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid // be the base coordinate of the region. public GridRegion GetRegionByPosition(UUID scopeID, int x, int y) { + m_log.DebugFormat("{0} GetRegionByPosition. pos=<{1},{2}>", "[LOCAL GRID SERVICE CONNECTOR", x, y); GridRegion region = null; // First see if it's a neighbour, even if it isn't on this sim. @@ -209,6 +210,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid region = rcache.GetRegionByPosition(x, y); if (region != null) { + m_log.DebugFormat("{0} GetRegionByPosition. Found region {1}. Pos=<{2},{3}>", + "[LOCAL GRID SERVICE CONNECTOR", region.RegionName, x, y); return region; } } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 99c7079..be8aed1 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -2483,6 +2483,23 @@ namespace OpenSim.Region.Framework.Scenes EntityTransferModule.Cross(grp, attemptedPosition, silent); } + // Simple test to see if a position is in the current region. + // Resuming the position is relative to the region so anything outside its bounds. + // Return 'true' if position inside region. + public bool PositionIsInCurrentRegion(Vector3 pos) + { + bool ret = true; + int xx = (int)Math.Floor(pos.X); + int yy = (int)Math.Floor(pos.Y); + if (xx < 0 + || xx > RegionInfo.RegionSizeX + || yy < 0 + || yy > RegionInfo.RegionSizeY) + ret = false; + return ret; + + } + public Border GetCrossedBorder(Vector3 position, Cardinals gridline) { if (BordersLocked) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 2c64c85..e827229 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -3123,6 +3123,9 @@ namespace OpenSim.Region.Framework.Scenes int neighbor = 0; int[] fix = new int[2]; + // Compute the avatar position in the next physics tick. + // If the avatar will be crossing, we force the crossing to happen now + // in the hope that this will make the avatar movement smoother when crossing. float timeStep = 0.1f; pos2.X = pos2.X + (vel.X * timeStep); pos2.Y = pos2.Y + (vel.Y * timeStep); @@ -3130,12 +3133,48 @@ namespace OpenSim.Region.Framework.Scenes if (!IsInTransit) { -// m_log.DebugFormat( -// "[SCENE PRESENCE]: Testing border check for projected position {0} of {1} in {2}", -// pos2, Name, Scene.Name); + m_log.DebugFormat( + "[SCENE PRESENCE]: Testing border check for projected position {0} of {1} in {2}", + pos2, Name, Scene.Name); + + if (!m_scene.PositionIsInCurrentRegion(pos2)) + { + // Disconnect from the current region + bool isFlying = Flying; + RemoveFromPhysicalScene(); + // pos2 is the forcasted position so make that the 'current' position so the crossing + // code will move us into the newly addressed region. + m_pos = pos2; + if (CrossToNewRegion()) + { + AddToPhysicalScene(isFlying); + } + else + { + // Tried to make crossing happen but it failed. + if (m_requestedSitTargetUUID == UUID.Zero) + { + + Vector3 pos = AbsolutePosition; + if (AbsolutePosition.X < 0) + pos.X += Velocity.X * 2; + else if (AbsolutePosition.X > m_scene.RegionInfo.RegionSizeX) + pos.X -= Velocity.X * 2; + if (AbsolutePosition.Y < 0) + pos.Y += Velocity.Y * 2; + else if (AbsolutePosition.Y > m_scene.RegionInfo.RegionSizeY) + pos.Y -= Velocity.Y * 2; + Velocity = Vector3.Zero; + AbsolutePosition = pos; + + AddToPhysicalScene(isFlying); + } + } + + } + /* // Checks if where it's headed exists a region - bool needsTransit = false; if (m_scene.TestBorderCross(pos2, Cardinals.W)) { if (m_scene.TestBorderCross(pos2, Cardinals.S)) @@ -3236,6 +3275,7 @@ namespace OpenSim.Region.Framework.Scenes } } } + */ } else { -- cgit v1.1