diff --git a/OpenSim/Framework/UserProfiles.cs b/OpenSim/Framework/UserProfiles.cs index 6d46fe9c2d..4ed890bc43 100644 --- a/OpenSim/Framework/UserProfiles.cs +++ b/OpenSim/Framework/UserProfiles.cs @@ -135,6 +135,7 @@ namespace OpenSim.Framework public string born; public byte[] membershipType; public uint flags; + public HashSet ClientsWaitingProps; } } diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index b6225232db..89cf045716 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -1725,12 +1725,16 @@ namespace OpenSim.Framework return new UUID(bytes, 0); } - public static void ParseFakeParcelID(UUID parcelID, out ulong regionHandle, out uint x, out uint y) + public static bool ParseFakeParcelID(UUID parcelID, out ulong regionHandle, out uint x, out uint y) { byte[] bytes = parcelID.GetBytes(); regionHandle = Utils.BytesToUInt64(bytes); x = Utils.BytesToUInt(bytes, 8) & 0xffff; y = Utils.BytesToUInt(bytes, 12) & 0xffff; + // validation may fail, just reducing the odds of using a real UUID as encoded parcel + return ( bytes[0] == 0 && bytes[4] == 0 && // handler x,y multiples of 256 + bytes[9] < 64 && bytes[13] < 64 && // positions < 16km + bytes[14] == 0 && bytes[15] == 0); } public static void ParseFakeParcelID(UUID parcelID, out ulong regionHandle, out uint x, out uint y, out uint z) diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs index 9413598de6..b6dd5652c7 100644 --- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs @@ -62,7 +62,8 @@ namespace OpenSim.Region.CoreModules.Asset MethodBase.GetCurrentMethod().DeclaringType); private bool m_Enabled; - private bool m_Running; + private bool m_timerRunning; + private bool m_cleanupRunning; private const string m_ModuleName = "FlotsamAssetCache"; private const string m_DefaultCacheDirectory = "./assetcache"; @@ -91,9 +92,8 @@ namespace OpenSim.Region.CoreModules.Asset private bool m_MemoryCacheEnabled = false; // Expiration is expressed in hours. - private const double m_DefaultMemoryExpiration = 2; + private double m_MemoryExpiration = 0.001; private const double m_DefaultFileExpiration = 48; - private TimeSpan m_MemoryExpiration = TimeSpan.FromHours(m_DefaultMemoryExpiration); private TimeSpan m_FileExpiration = TimeSpan.FromHours(m_DefaultFileExpiration); private TimeSpan m_FileExpirationCleanupTimer = TimeSpan.FromHours(1.0); @@ -150,7 +150,8 @@ namespace OpenSim.Region.CoreModules.Asset m_CacheDirectory = assetConfig.GetString("CacheDirectory", m_DefaultCacheDirectory); m_MemoryCacheEnabled = assetConfig.GetBoolean("MemoryCacheEnabled", m_MemoryCacheEnabled); - m_MemoryExpiration = TimeSpan.FromHours(assetConfig.GetDouble("MemoryCacheTimeout", m_DefaultMemoryExpiration)); + m_MemoryExpiration = assetConfig.GetDouble("MemoryCacheTimeout", m_MemoryExpiration); + m_MemoryExpiration *= 3600.0; // config in hours to seconds #if WAIT_ON_INPROGRESS_REQUESTS m_WaitOnInprogressTimeout = assetConfig.GetInt("WaitOnInprogressTimeout", 3000); @@ -224,9 +225,9 @@ namespace OpenSim.Region.CoreModules.Asset m_Scenes.Remove(scene); lock(timerLock) { - if(m_Running && m_Scenes.Count <= 0) + if(m_timerRunning && m_Scenes.Count <= 0) { - m_Running = false; + m_timerRunning = false; m_CacheCleanTimer.Stop(); m_CacheCleanTimer.Close(); } @@ -242,7 +243,7 @@ namespace OpenSim.Region.CoreModules.Asset m_AssetService = scene.RequestModuleInterface(); lock(timerLock) { - if(!m_Running) + if(!m_timerRunning) { if (m_FileCacheEnabled && (m_FileExpiration > TimeSpan.Zero) && (m_FileExpirationCleanupTimer > TimeSpan.Zero)) { @@ -250,7 +251,7 @@ namespace OpenSim.Region.CoreModules.Asset m_CacheCleanTimer.AutoReset = false; m_CacheCleanTimer.Elapsed += CleanupExpiredFiles; m_CacheCleanTimer.Start(); - m_Running = true; + m_timerRunning = true; } } } @@ -263,6 +264,7 @@ namespace OpenSim.Region.CoreModules.Asset private void UpdateMemoryCache(string key, AssetBase asset) { + // NOTE DO NOT USE SLIDEEXPIRE option on current libomv m_MemoryCache.AddOrUpdate(key, asset, m_MemoryExpiration); } @@ -480,12 +482,10 @@ namespace OpenSim.Region.CoreModules.Asset asset = GetFromMemoryCache(id); if (asset == null && m_FileCacheEnabled) - { asset = GetFromFileCache(id); - if (m_MemoryCacheEnabled && asset != null) - UpdateMemoryCache(id, asset); - } + if (m_MemoryCacheEnabled && asset != null) + UpdateMemoryCache(id, asset); if (((m_LogLevel >= 1)) && (m_HitRateDisplay != 0) && (m_Requests % m_HitRateDisplay == 0)) { @@ -561,8 +561,12 @@ namespace OpenSim.Region.CoreModules.Asset if (m_LogLevel >= 2) m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Checking for expired files older then {0}.", m_FileExpiration); - if(!m_Running) - return; + lock(timerLock) + { + if(!m_timerRunning || m_cleanupRunning) + return; + m_cleanupRunning = true; + } // Purge all files last accessed prior to this point DateTime purgeLine = DateTime.Now - m_FileExpiration; @@ -578,8 +582,9 @@ namespace OpenSim.Region.CoreModules.Asset lock(timerLock) { - if(m_Running) + if(m_timerRunning) m_CacheCleanTimer.Start(); + m_cleanupRunning = false; } } @@ -816,13 +821,13 @@ namespace OpenSim.Region.CoreModules.Asset s.ForEachSOG(delegate(SceneObjectGroup e) { - if(!m_Running && !storeUncached) + if(!m_timerRunning && !storeUncached) return; gatherer.AddForInspection(e); gatherer.GatherAll(); - if(!m_Running && !storeUncached) + if(!m_timerRunning && !storeUncached) return; foreach (UUID assetID in gatherer.GatheredUuids.Keys) @@ -854,13 +859,13 @@ namespace OpenSim.Region.CoreModules.Asset } gatherer.GatheredUuids.Clear(); - if(!m_Running && !storeUncached) + if(!m_timerRunning && !storeUncached) return; if(!storeUncached) Thread.Sleep(50); }); - if(!m_Running && !storeUncached) + if(!m_timerRunning && !storeUncached) break; } @@ -905,16 +910,23 @@ namespace OpenSim.Region.CoreModules.Asset { List outputLines = new List(); - double fileHitRate = (double)m_DiskHits / m_Requests * 100.0; + double invReq = 100.0 / m_Requests; + + double fileHitRate = m_DiskHits * invReq; outputLines.Add( string.Format("File Hit Rate: {0}% for {1} requests", fileHitRate.ToString("0.00"), m_Requests)); if (m_MemoryCacheEnabled) { - double memHitRate = (double)m_MemoryHits / m_Requests * 100.0; + double HitRate = m_MemoryHits * invReq; outputLines.Add( - string.Format("Memory Hit Rate: {0}% for {1} requests", memHitRate.ToString("0.00"), m_Requests)); + string.Format("Memory Hit Rate: {0}% for {1} requests", HitRate.ToString("0.00"), m_Requests)); + + HitRate += fileHitRate; + + outputLines.Add( + string.Format("Total Hit Rate: {0}% for {1} requests", HitRate.ToString("0.00"), m_Requests)); } outputLines.Add( @@ -1019,17 +1031,27 @@ namespace OpenSim.Region.CoreModules.Asset break; case "assets": - con.Output("Ensuring assets are cached for all scenes."); + lock(timerLock) + { + if(m_cleanupRunning) + { + con.OutputFormat("FloatSam assets check already running"); + return; + } + m_cleanupRunning = true; + } + + con.Output("FloatSam Ensuring assets are cached for all scenes."); WorkManager.RunInThread(delegate { bool wasRunning= false; lock(timerLock) { - if(m_Running) + if(m_timerRunning) { m_CacheCleanTimer.Stop(); - m_Running = false; + m_timerRunning = false; wasRunning = true; Thread.Sleep(100); } @@ -1041,8 +1063,9 @@ namespace OpenSim.Region.CoreModules.Asset if(wasRunning) { m_CacheCleanTimer.Start(); - m_Running = true; + m_timerRunning = true; } + m_cleanupRunning = false; } con.OutputFormat("Completed check with {0} assets.", assetReferenceTotal); }, null, "TouchAllSceneAssets"); diff --git a/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs b/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs index d232d82a84..3f7a8ee7a9 100644 --- a/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs @@ -897,20 +897,36 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles else { // we have a proxy on map - // this is a fail on large regions - uint gtmp = (uint)globalPos.X >> 8; - globalPos.X -= (gtmp << 8); - globalPos.X += target.RegionLocX; + ulong oriHandle; + uint oriX; + uint oriY; + if(Util.ParseFakeParcelID(pick.ParcelId, out oriHandle, out oriX, out oriY)) + { + pick.ParcelId = Util.BuildFakeParcelID(target.RegionHandle, oriX, oriY); + globalPos.X = target.RegionLocX + oriX; + globalPos.Y = target.RegionLocY + oriY; + pick.GlobalPos = globalPos.ToString(); + } + else + { + // this is a fail on large regions + uint gtmp = (uint)globalPos.X >> 8; + globalPos.X -= (gtmp << 8); - gtmp = (uint)globalPos.Y >> 8; - globalPos.Y -= (gtmp << 8); - globalPos.Y += target.RegionLocY; + gtmp = (uint)globalPos.Y >> 8; + globalPos.Y -= (gtmp << 8); + + pick.ParcelId = Util.BuildFakeParcelID(target.RegionHandle, (uint)globalPos.X, (uint)globalPos.Y); + + globalPos.X += target.RegionLocX; + globalPos.Y += target.RegionLocY; + pick.GlobalPos = globalPos.ToString(); + } } } m_log.DebugFormat("[PROFILES]: PickInfoRequest: {0} : {1}", pick.Name.ToString(), pick.SnapshotId.ToString()); - pick.GlobalPos = globalPos.ToString(); lock(m_profilesCache) { if(!m_profilesCache.TryGetValue(targetID, out uce) || uce == null) @@ -1331,16 +1347,35 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles if(uce.props != null) { props = uce.props; - remoteClient.SendAvatarProperties(props.UserId, props.AboutText, - uce.born, uce.membershipType , props.FirstLifeText, uce.flags, - props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId); + uint cflags = uce.flags; + // if on same region force online + if(p != null && !p.IsDeleted) + cflags |= 0x10; + remoteClient.SendAvatarProperties(props.UserId, props.AboutText, + uce.born, uce.membershipType , props.FirstLifeText, cflags, + props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId); remoteClient.SendAvatarInterestsReply(props.UserId, (uint)props.WantToMask, props.WantToText, (uint)props.SkillsMask, props.SkillsText, props.Language); - return; + return; } + else + { + if(uce.ClientsWaitingProps == null) + uce.ClientsWaitingProps = new HashSet(); + else if(uce.ClientsWaitingProps.Contains(remoteClient)) + return; + uce.ClientsWaitingProps.Add(remoteClient); + } + } + else + { + uce = new UserProfileCacheEntry(); + uce.ClientsWaitingProps = new HashSet(); + uce.ClientsWaitingProps.Add(remoteClient); + m_profilesCache.AddOrUpdate(avatarID, uce, PROFILECACHEEXPIRE); } } @@ -1402,14 +1437,11 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles { props.AboutText ="Profile not available at this time. User may still be unknown to this grid"; } - - // if on same region force online - if(p != null && !p.IsDeleted) - flags |= 0x10; if(!m_allowUserProfileWebURLs) props.WebUrl =""; + HashSet clients; lock(m_profilesCache) { if(!m_profilesCache.TryGetValue(props.UserId, out uce) || uce == null) @@ -1418,15 +1450,39 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles uce.born = born; uce.membershipType = membershipType; uce.flags = flags; - + clients = uce.ClientsWaitingProps; + uce.ClientsWaitingProps = null; m_profilesCache.AddOrUpdate(props.UserId, uce, PROFILECACHEEXPIRE); } - remoteClient.SendAvatarProperties(props.UserId, props.AboutText, born, membershipType , props.FirstLifeText, flags, + // if on same region force online + if(p != null && !p.IsDeleted) + flags |= 0x10; + + if(clients == null) + { + remoteClient.SendAvatarProperties(props.UserId, props.AboutText, born, membershipType , props.FirstLifeText, flags, props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId); - remoteClient.SendAvatarInterestsReply(props.UserId, (uint)props.WantToMask, props.WantToText, (uint)props.SkillsMask, - props.SkillsText, props.Language); + remoteClient.SendAvatarInterestsReply(props.UserId, (uint)props.WantToMask, props.WantToText, + (uint)props.SkillsMask, props.SkillsText, props.Language); + } + else + { + if(!clients.Contains(remoteClient)) + clients.Add(remoteClient); + foreach(IClientAPI cli in clients) + { + if(!cli.IsActive) + continue; + cli.SendAvatarProperties(props.UserId, props.AboutText, born, membershipType , props.FirstLifeText, flags, + props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId); + + cli.SendAvatarInterestsReply(props.UserId, (uint)props.WantToMask, props.WantToText, + (uint)props.SkillsMask, props.SkillsText, props.Language); + + } + } } /// diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs index bec5322341..22bc49e3c1 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs @@ -1880,8 +1880,9 @@ namespace OpenSim.Region.CoreModules.World.Land UUID.TryParse(id, out parcel); // assume we've got the parcelID we just computed in RemoteParcelRequest ExtendedLandData extLandData = new ExtendedLandData(); - Util.ParseFakeParcelID(parcel, out extLandData.RegionHandle, - out extLandData.X, out extLandData.Y); + if(!Util.ParseFakeParcelID(parcel, out extLandData.RegionHandle, + out extLandData.X, out extLandData.Y)) + return null; m_log.DebugFormat("[LAND MANAGEMENT MODULE]: Got parcelinfo request for regionHandle {0}, x/y {1}/{2}", extLandData.RegionHandle, extLandData.X, extLandData.Y); diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 5638f01cba..ba5a46d1be 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -310,7 +310,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api foreach (string id in ids) { string current = id.Trim(); - if (current.ToUpper() == "PARCEL_GROUP_MEMBER" || current.ToUpper() == "PARCEL_OWNER" || current.ToUpper() == "ESTATE_MANAGER" || current.ToUpper() == "ESTATE_OWNER" || current.ToUpper() == "GRID_GOD") + if (current.ToUpper() == "PARCEL_GROUP_MEMBER" || current.ToUpper() == "PARCEL_OWNER" || current.ToUpper() == "ESTATE_MANAGER" || current.ToUpper() == "ESTATE_OWNER" || current.ToUpper() == "GOD" || current.ToUpper() == "GRID_GOD") { if (!perms.AllowedOwnerClasses.Contains(current)) perms.AllowedOwnerClasses.Add(current.ToUpper()); @@ -416,9 +416,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } //Only gods may use the function + if (m_FunctionPerms[function].AllowedOwnerClasses.Contains("GOD")) + { + if (World.Permissions.IsGod(ownerID)) + { + return String.Empty; + } + } + + //Only grid gods may use the function if (m_FunctionPerms[function].AllowedOwnerClasses.Contains("GRID_GOD")) { - if (World.Permissions.IsGridGod(ownerID)) + if (World.Permissions.IsGridGod(ownerID)) { return String.Empty; } diff --git a/bin/config-include/FlotsamCache.ini.example b/bin/config-include/FlotsamCache.ini.example index db8d4db01a..666681211d 100644 --- a/bin/config-include/FlotsamCache.ini.example +++ b/bin/config-include/FlotsamCache.ini.example @@ -20,15 +20,20 @@ HitRateDisplay = 100 ; Set to false for no memory cache - MemoryCacheEnabled = false + ; assets can be requested several times in short periods + ; so even a small memory cache is usefull + MemoryCacheEnabled = true ; Set to false for no file cache FileCacheEnabled = true ; How long {in hours} to keep assets cached in memory, .5 == 30 minutes - ; Optimization: for VPS or limited memory system installs set Timeout to .016 (1 minute) - ; increases performance without large memory impact - MemoryCacheTimeout = 2 + ; even a few minutes may mean many assets loaded to memory, if not all. + ; this is good if memory is not a problem. + ; if memory is a problem then a few seconds may actually save same. + ; reducing duplications. + ; see hit rates with console comand: fcache status + MemoryCacheTimeout = .001 // 3.6s ie around 4s (1s resolution) ; How long {in hours} to keep assets cached on disk, .5 == 30 minutes ; Specify 0 if you do not want your disk cache to expire diff --git a/bin/config-include/osslEnable.ini b/bin/config-include/osslEnable.ini index a064f0957f..b96688bafd 100644 --- a/bin/config-include/osslEnable.ini +++ b/bin/config-include/osslEnable.ini @@ -37,6 +37,8 @@ ; To enable for individuals or groups, set it to a comma separated list. This checks ; against the owner of the object containing the script. ; The comma separated entries in the list may be one of: + ; "GRID_GOD" -- enable for users with UserLevel >= 200 + ; "GOD" -- enable for users with any type of god rights ; "ESTATE_MANAGER" -- enable for estate manager ; "ESTATE_OWNER" -- enable for estate owner ; "PARCEL_OWNER" -- enable for parcel owner